316 lines
8.2 KiB
Python
316 lines
8.2 KiB
Python
from traceback import format_exc
|
||
from appPublic.log import debug, exception, info
|
||
from appPublic.timeUtils import curDateString
|
||
from appPublic.aes import aes_decode_b64
|
||
from appPublic.uniqueID import getID
|
||
from time import time
|
||
from ahserver.serverenv import get_serverenv
|
||
from ahserver.auth_api import get_session_userinfo, user_login
|
||
from sqlor.dbpools import DBPools
|
||
from rbac.check_perm import create_org, create_user
|
||
|
||
|
||
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('downapp', {'id':appid})
|
||
if len(recs) < 1:
|
||
return None
|
||
secretkey = recs[0].secretkey
|
||
f = get_serverenv('password_decode')
|
||
return f(secretkey)
|
||
|
||
async def get_apikey_user(sor, apikey, client_ip):
|
||
f = get_serverenv('password_encode')
|
||
apikey = f(apikey)
|
||
sql = """select u.*, a.dappid, b.allowedips from downapikey a, users u, downapp b
|
||
where a.userid = u.id
|
||
and b.id = a.dappid
|
||
and a.apikey=${apikey}$
|
||
and a.expires_at > ${today}$"""
|
||
|
||
recs = await sor.sqlExe(sql, {"apikey":apikey, 'today': curDateString()})
|
||
if len(recs) < 1:
|
||
debug(f'{apikey=} not registered')
|
||
return None
|
||
rec = recs[0]
|
||
if rec.allowedips is None:
|
||
return rec
|
||
|
||
ips = rec.allowedips.split(',')
|
||
ips = [ ip.strip() for ip in ips ]
|
||
if client_ip not in ips:
|
||
debug(f' {client_ip} not in {rec.allowedips=}')
|
||
return None
|
||
return rec
|
||
|
||
async def bearer_auth(sor, request):
|
||
auth = request.headers.get('Authorization')
|
||
if auth is None:
|
||
debug(f'headers has not "Authorization"')
|
||
return None
|
||
if not auth.startswith('Bearer '):
|
||
debug(f'"Authorization" not starts with "Bearer "')
|
||
return None
|
||
apikey = auth[7:]
|
||
client_ip = request['client_ip']
|
||
return await apikey_user(sor, apikey, client_ip, request)
|
||
|
||
async def apikey_user(sor, apikey, client_ip, request):
|
||
if apikey is None:
|
||
debug(f'keykey is None')
|
||
return None
|
||
user = await get_apikey_user(sor, apikey, client_ip)
|
||
if user is None:
|
||
debug(f'get_apikey_user() {apikey=}, {client_ip} return None')
|
||
return None
|
||
await user_login(request, user.id, username=user.username, userorgid=user.orgid)
|
||
|
||
# 认证成功后,给用户添加downappuser角色(角色id=downapp.id)
|
||
dappid = getattr(user, 'dappid', None)
|
||
if dappid:
|
||
await ensure_downappuser_role_and_assign(sor, user.id, dappid)
|
||
|
||
return user.id
|
||
|
||
async def ensure_downappuser_role_and_assign(sor, userid, dappid):
|
||
"""确保downapp/logined角色存在,并分配给用户"""
|
||
try:
|
||
# 检查角色是否存在,不存在则创建
|
||
roles = await sor.R('role', {'orgtypeid': 'downapp', 'name': 'logined'})
|
||
if not roles:
|
||
roleid = getID()
|
||
await sor.C('role', {
|
||
'id': roleid,
|
||
'name': 'logined',
|
||
'orgtypeid': 'downapp'
|
||
})
|
||
debug(f'created logined role (downapp): {roleid}')
|
||
else:
|
||
roleid = roles[0].id
|
||
|
||
# 检查用户是否已有该角色
|
||
existing = await sor.R('userrole', {'userid': userid, 'roleid': roleid})
|
||
if not existing:
|
||
await sor.C('userrole', {
|
||
'id': getID(),
|
||
'userid': userid,
|
||
'roleid': roleid
|
||
})
|
||
debug(f'assigned logined role {roleid} (downapp) to user {userid}')
|
||
except Exception as e:
|
||
exception(f'ensure_downappuser_role_and_assign error: {e}')
|
||
|
||
async def x_api_key_auth(sor, request):
|
||
"""Anthropic标准认证模式:从 x-api-key header 读取apikey"""
|
||
apikey = request.headers.get('x-api-key')
|
||
if apikey is None:
|
||
debug(f'x-api-key header not found')
|
||
return None
|
||
client_ip = request['client_ip']
|
||
return await apikey_user(sor, apikey, client_ip, request)
|
||
|
||
async def deerer_auth(sor, request):
|
||
auth = request.headers.get('Authorization')
|
||
if auth is None:
|
||
return None
|
||
if not auth.startswith('Deerer '):
|
||
return None
|
||
deer_data = auth[7:]
|
||
client_ip = request['client_ip']
|
||
return await deerer_user(sor, deer_data, client_ip, request)
|
||
|
||
async def deerer_user(sor, deer_data, client_ip, request):
|
||
appid, cyber = deer_data.split('-:-')
|
||
secretkey = await get_secretkey(sor, appid)
|
||
try:
|
||
txt = aes_decode_b64(secretkey, cyber)
|
||
t, apikey = txt.split(':')
|
||
return await apikey_user(sor, apikey, client_ip, request)
|
||
except Exception as e:
|
||
exception(f'{e}, {deer_data=},{secretkey=}')
|
||
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('downapikey', 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.get('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('downapikey', d)
|
||
return apikey
|
||
|
||
async def create_user_apikey(sor, dappid, user_id, user_orgid, **kwargs):
|
||
"""为已有用户创建dapi apikey
|
||
|
||
Args:
|
||
sor: sqlor连接对象
|
||
dappid: dapi应用ID
|
||
user_id: rbac用户ID
|
||
user_orgid: 用户机构ID
|
||
**kwargs: 可选字段(username, name, email等users表字段)
|
||
|
||
Returns:
|
||
dict: {'status': 'success'|'error', 'apikey': str, 'message': str}
|
||
"""
|
||
try:
|
||
# 检查apikey是否已存在
|
||
existing = await sor.R('downapikey', {
|
||
'dappid': dappid,
|
||
'duserid': user_id,
|
||
'dorgid': user_orgid
|
||
})
|
||
|
||
if existing:
|
||
# 返回现有apikey
|
||
f = get_serverenv('password_decode')
|
||
apikey = f(existing[0].apikey)
|
||
return {
|
||
'status': 'success',
|
||
'apikey': apikey,
|
||
'user_id': user_id,
|
||
'message': '用户apikey已存在'
|
||
}
|
||
|
||
# 创建新apikey
|
||
apikey_id = getID()
|
||
apikey_value = getID()
|
||
|
||
ns = {
|
||
'id': apikey_id,
|
||
'dappid': dappid,
|
||
'dorgid': user_orgid,
|
||
'duserid': user_id,
|
||
'orgid': user_orgid,
|
||
'userid': user_id,
|
||
'apikey': password_encode(apikey_value),
|
||
'enabled': '1',
|
||
'created_at': curDateString(),
|
||
'expires_at': '9999-12-31'
|
||
}
|
||
|
||
await sor.C('downapikey', ns)
|
||
|
||
return {
|
||
'status': 'success',
|
||
'apikey': apikey_value,
|
||
'user_id': user_id,
|
||
'message': 'apikey创建成功'
|
||
}
|
||
except Exception as e:
|
||
exception(f'create_user_apikey error: {e}')
|
||
return {
|
||
'status': 'error',
|
||
'message': f'创建apikey失败: {str(e)}'
|
||
}
|
||
|
||
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)
|
||
|
||
if __name__ == '__main__':
|
||
print('ggg')
|