first commit

This commit is contained in:
yumoqing 2025-07-04 18:18:41 +08:00
commit c6d775cd7c
7 changed files with 245 additions and 0 deletions

28
README.md Normal file
View File

@ -0,0 +1,28 @@
# 第三方应用登录服务器端
本模块为应用软件提供其他第三方应用的客户使用apikey自动登录
## 依赖的软件包
* [ahserver](https://git.kaiyuancloud.cn/yumoqing/ahserver)
* [sqlor](https://git.kaiyuancloud.cn/yumoqing/sqlor)
* [apppublic](https://git.kaiyuancloud.cn/yumoqing/apppublic)
## 安装
pip install kyapikeyserver
## 业务流程
* 申请系统下位的apikey
客户注册后添加一个下位系统并将系统自动生成的apikey和secretkey复制到实际的下午系统中登记
* 用户同步
下位系统用上位系统生成的系统aksk将需要同步的用户支持多个按照用户同步的格式上传到本地系统
上位系统系统收到请求后添加系统本身的机构和用户并为此用户生成apikey与下位系统的userid一起返回给下位系统

3
json/build.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
xls2ui -m ../models -o ../wwwroot kyapikey *.json

176
kyapikeyserver/server.py Normal file
View File

@ -0,0 +1,176 @@
from traceback import format_exc
from appPublic.log import debug, exception, info
from appPublic.timeUtils import curDateString
from uniqueID import getID
from time import time
from ahserver.serverenv import ServerEnv
from ahserver.auth_api import get_session_userinfo
from sqlor.dbpools import DBPools
form rbac.check_perm improt create_org, create_user
from appPublic.aes import aes_encrypt_ecb, aes_decrypt_ecb
return_messages = {
-9: '用户同步:未知未知错误',
-4: '用户同步添加用户apikey失败',
-3: '用户同步:添加用户失败',
-2: '用户同步:添加机构失败',
-1: '用户同步:用户已同步'
}
def get_dbname():
dbname = get_serverenv('get_module_dbname')('dapi')
return dbname
async def get_secretkey(sor, appid):
recs = await sor.R('kydownapp', {'id':appid})
if len(recs) < 1:
return None
secret_key = recs[0].secret_key
f = get_serverenv('password_decode')
return f(secret_key).encode('utf-8')
async def get_apikey_user(sor, apikey):
f = get_serverenv('password_encode')
apikey = f(apikey)
users = await sor.R('kydownapikey', {'apikey': apikey})
if len(users) < 1:
return None
apiuser = users[0]
if not apiuser.enabled:
e = Exception(f'user(id={apiuser.userid}) is not enabled')
exception(f'{e},{format_exc()}')
raise e
if apiuser.expires_at < curDateString():
e = Exception(f"user(id={apiuser.userid})'s apikey is expired({apiuser.expires_at})')
exception(f'{e}, {format_exc()}')
raise e
users = await sor.R('users', {'id': apiuser.userid, 'orgid': apiuser.orgid})
if len(users) < 1:
e = Exception(f'user(id={apiuser.userid}) not found in users')
exception(f'{e}, {format_exc()}')
raise e
return users[0]
async def get_user_from_bear_data(self, bear_data):
if not bear_data[:5] == 'Bear ':
return None
bear_data = bear_data[5:]
appid, cyber = bear_data.split('-:-')
db = DBPools()
dbname = get_dbname()
async with db.sqlorContext(dbname) as sor:
secretkey = await get_secretkey(sor, appid)
txt = aes_decrypt_ecb(secretkey, cyber)
t, apikey = txt.split(':')
userinfo = await get_apikey_user(apikey)
return userinfo
return None
def return_error(code):
return {
'status':'error',
'errcode': code,
'errmsg': return_messages.get(code, '未定义信息')
}
def return_success(data):
return {
'status':'success',
'data':data
}
async def get_orgid_by_dorgid(sor, dappid, dorgid):
d = {
'dappid':dappid,
'dorgid':dorgid
}
recs = await sor.R('kydownapikey', d)
if len(recs) < 1:
return None
return recs[0].orgid
async def check_duserid_exists(sor, dappid, dorgid, duserid):
d = {
'dappid': dappid,
'duserid': duserid,
'dorgid': dorgid
}
recs = await sor.R('kwdownapikey', d)
if len(recs):
return True
return False
async def add_organzation(sor, dappid, org):
id = getID()
org['id'] = id
await create_org(sor, org)
return id
async def add_user(sor, user):
id = getID()
user['id'] = id
await create_user(sor, user, roles=user['roles']
return id
async def add_apikey(sor, dappid, dorgid, duserid, orgid, userid):
apikey = getID()
d = {
'id': getID,
'dappid': dappid,
'dorgid': dorgid,
'duserid': duserid,
'orgid': orgid,
'userid': userid,
'apikey': id,
'enabled': '1',
'created_at': curDateString(),
'expires_at': '9999-12-31'
}
await sor.C('kydownapikey', d)
return apikey
async def sync_user(request, params_kw, *args, **kw):
dappid = params_kw.dappid
db = DBPools()
dbname = get_dbname()
userinfo = await get_session_userinfo(request)
async with db.sqlorContext(dbname) as sor:
ret_users = []
roles = [{
'orgtypeid': 'customer',
'roles': [ 'customer', 'syncuser' ]
}]
for o in params_kw.organizations:
for u in o['users']:
dorgid = o['id']
duserid = u['id']
orgid = await get_orgid_by_dorgid(sor, dappid, dorgid)
if orgid is None:
if o.get('parentid') is None:
o['parentid'] = userinfo.userorgid
else:
nparentid = await get_orgid_by_dorgid(sor, dappid, o.get('parentid'))
o['parentid'] = nparentid
orgid = await add_organzation(sor, dappid, o)
if orgid is None:
return return_error(-2)
u['orgid'] = o['id']
u['roles'] = roles
exists = check_duserid_exists(sor, dappid, dorgid, duserid)
if exists:
return return_error(-1)
userid = await add_user(sor, u)
if userid is None:
return return_error(-3)
apikey = await add_apikey(sor, dappid, orgid, userid, u)
if apikey is None:
return return_error(-4)
ret_users.append({
'id': u['id'],
'apikey': apikey
})
return return_success(ret_users)
return return_error(-9)

BIN
models/kydownapikey.xlsx Normal file

Binary file not shown.

BIN
models/kydownapp.xlsx Normal file

Binary file not shown.

4
pyproject.toml Normal file
View File

@ -0,0 +1,4 @@
[build-system]
requires = ["setuptools>=61", "wheel"]
build-backend = "setuptools.build_meta"

34
setup.cfg Normal file
View File

@ -0,0 +1,34 @@
[metadata]
name=appPublic
version = 5.2.3
description = Your project description
authors = [{
author = "yu moqing"
author_email = "yumoqing@gmail.com" }]
readme = "README.md"
license = "MIT"
[options]
packages = find:
requires-python = ">=3.8"
install_requires =
xlrd
bs4
xlwt
nanoid
psutil
ujson
numpy
rsa
brotli
aiohttp
asyncio
aiohttp
aiohttp_socks
requests
jinja2
pyzmq
cryptography
bcrypt
asyncssh
eventpy