feat: 添加x-api-key认证模式和downappuser角色自动分配

- 新增x_api_key_auth函数支持Anthropic标准x-api-key header认证
- get_apikey_user查询增加dappid字段
- 认证成功后自动创建并分配downappuser角色(id=dappid)
- rbac/check_perm中增加x-api-key header检查,优先于Authorization
This commit is contained in:
yumoqing 2026-05-11 15:36:41 +08:00
parent 27794d3d25
commit e142f4b84e
2 changed files with 48 additions and 6 deletions

View File

@ -33,24 +33,24 @@ async def get_secretkey(sor, appid):
async def get_apikey_user(sor, apikey, client_ip): async def get_apikey_user(sor, apikey, client_ip):
f = get_serverenv('password_encode') f = get_serverenv('password_encode')
apikey = f(apikey) apikey = f(apikey)
sql = """select u.*, b.allowedips from downapikey a, users u, downapp b sql = """select u.*, a.dappid, b.allowedips from downapikey a, users u, downapp b
where a.userid = u.id where a.userid = u.id
and b.id = a.dappid and b.id = a.dappid
and apikey=${apikey}$ and a.apikey=${apikey}$
and expired_date > ${today}$""" and a.expires_at > ${today}$"""
recs = await sor.sqlExe(sql, {"apikey":apikey, 'today': curDateString()}) recs = await sor.sqlExe(sql, {"apikey":apikey, 'today': curDateString()})
if len(recs) < 1: if len(recs) < 1:
debug(f'{apikey=} not registered') debug(f'{apikey=} not registered')
return None return None
rec = recs[0] rec = recs[0]
if rec.allowips is None: if rec.allowedips is None:
return rec return rec
ips = rec.allowedips.split(',') ips = rec.allowedips.split(',')
ips = [ ip.strip() for ip in ips ] ips = [ ip.strip() for ip in ips ]
if client_ip not in ips: if client_ip not in ips:
debug(f' {client_ip} not in {rec.allowips=}') debug(f' {client_ip} not in {rec.allowedips=}')
return None return None
return rec return rec
@ -75,8 +75,49 @@ async def apikey_user(sor, apikey, client_ip, request):
debug(f'get_apikey_user() {apikey=}, {client_ip} return None') debug(f'get_apikey_user() {apikey=}, {client_ip} return None')
return None return None
await user_login(request, user.id, username=user.username, userorgid=user.orgid) 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 return user.id
async def ensure_downappuser_role_and_assign(sor, userid, dappid):
"""确保downappuser角色存在id=dappid并分配给用户"""
try:
# 检查角色是否存在,不存在则创建
roles = await sor.R('role', {'id': dappid})
if not roles:
await sor.C('role', {
'id': dappid,
'name': 'downappuser',
'orgtypeid': '*'
})
debug(f'created downappuser role: {dappid}')
# 检查用户是否已有该角色
existing = await sor.R('userrole', {'userid': userid, 'roleid': dappid})
if not existing:
from appPublic.uniqueID import getID
await sor.C('userrole', {
'id': getID(),
'userid': userid,
'roleid': dappid
})
debug(f'assigned downappuser role {dappid} 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): async def deerer_auth(sor, request):
auth = request.headers.get('Authorization') auth = request.headers.get('Authorization')
if auth is None: if auth is None:

View File

@ -1,4 +1,4 @@
from dapi.dapi import sync_user, bearer_auth, deerer_auth, deerer_user, apikey_user, create_user_apikey from dapi.dapi import sync_user, bearer_auth, deerer_auth, deerer_user, apikey_user, create_user_apikey, x_api_key_auth
from ahserver.serverenv import ServerEnv from ahserver.serverenv import ServerEnv
from rbac.check_perm import register_auth_method from rbac.check_perm import register_auth_method
@ -8,6 +8,7 @@ def load_dapi():
env.deerer_user = deerer_user env.deerer_user = deerer_user
env.apikey_user = apikey_user env.apikey_user = apikey_user
env.create_user_apikey = create_user_apikey env.create_user_apikey = create_user_apikey
env.x_api_key_auth = x_api_key_auth
register_auth_method('Bearer ', bearer_auth) register_auth_method('Bearer ', bearer_auth)
register_auth_method('Deerer ', deerer_auth) register_auth_method('Deerer ', deerer_auth)