前面文章搞定了飞书作为oauth2 source,集成到了authentik,但是默认能拿到的信息非常有限,就这么几个:
{
"sub": "ou_caecc734c2e3328a62489fe0648c4b98779515d3",
"name": "李雷",
"picture": "https://www.feishu.cn/avatar",
"open_id": "ou_caecc734c2e3328a62489fe0648c4b98779515d3",
"union_id": "on_d89jhsdhjsajkda7828enjdj328ydhhw3u43yjhdj",
"en_name": "Lilei",
"tenant_key": "736588c92lxf175d",
"avatar_url": "www.feishu.cn/avatar/icon",
...
"email": "zhangsan@feishu.cn",
"user_id": "5d9bdxxx",
"employee_no": "111222333",
"mobile": "+86130xxxx0000"
}
最重要的,员工所在的组织架构,leader身份都没有,内部应用恰恰常常这些信息进行权限配置和审批流程内的审核等等。下游应用接入authentik之后,如果还需要单独去飞书拿这些信息,太麻烦了。
飞书或者企业微信返回的组织架构需要重建成树,这个过程比较耗时,如果在authentik 内部脚本做不太合适,只能用外部服务周期性同步和生成供查询。
想了个办法:
1. 服务A 定时周期性从飞书同步组织架构,重建树状的组织架构,保存到本地,供authentik 查询用户时,将组织层级信息写入用户信息;
2. Authentik 内 Property Mappings创建一个Scope Mapping,假设scope_name 为feishu_attribute。
脚本主要逻辑:
1. 检查当前request.user.attributes 是否包含feishu_attribute,若否,请求服务A更新飞书属性;
2. 检查用户当前feishu_attribute_last_updated 是否过期,若是,更新飞书属性。
3. 创建的Provider,Advanced protocol settings -> Scopes,选择加入这个scope。
4. 内部应用来认证时申请的scope增加一个: feishu_attribute。
feishu_attribute scope mapping 简化如下, 外部服务返回的user 内容按需自定义:
import json
from datetime import datetime, timedelta
TIME_FORMAT="%Y-%m-%d %H:%M:%S.%f"
def update_user_feishu_attribute():
url = 'https://xxxxx.oa.com/api/feishu/user/info'
params = {'user_id': request.user.username,
'with_department_list_info': True,
'token': 'XXXXXXX'
}
resp = requests.get(url,params=params,timeout=3).json()
if resp.get('code') == 0:
user_data = resp.get('data').get('user')
attributes = request.user.attributes
attributes['feishu_attribute'] = user_data
attributes['feishu_attribute_last_updated'] = datetime.now().strftime(TIME_FORMAT)
request.user.update_attributes({})
if 'feishu_attribute' in request.user.attributes and 'feishu_attribute_last_updated' in request.user.attributes:
now = datetime.now()
last_updated = datetime.strptime(request.user.attributes['feishu_attribute_last_updated'], TIME_FORMAT)
seconds_diff = (now - last_updated).total_seconds()
if seconds_diff > 24 * 60 * 60:
update_user_feishu_attribute()
else:
update_user_feishu_attribute()
return {
"feishu_attribute": request.user.attributes.get('feishu_attribute', None),
}
scope mapping 的脚本自由度很大,可以完成各类需求,除了用户同步,还有例如自定义的登录判断等等,可玩性很高。