Skip to content

Commit a1fa355

Browse files
authored
feat: support tenant mode (TencentBlueKing#205)
1 parent e16377d commit a1fa355

17 files changed

Lines changed: 331 additions & 276 deletions

sdks/bkpaas-auth/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# 版本历史
22

3+
## 3.1.0
4+
- feat: UniversalAuthBackend 支持获取用户租户身份信息
5+
36
## 3.0.0
47
- BreakChange: 不再支持 Python 3.6,3.7
58
- BreakChange: Django 版本要求 >=4.2,<5.0

sdks/bkpaas-auth/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ BKAUTH_USER_COOKIE_VERIFY_URL = "http://bk-login-web/api/v3/is_login/"
6060
BKAUTH_DEFAULT_PROVIDER_TYPE = 'RTX' # 可选值:RTX/UIN/BK,详见 ProviderType
6161
```
6262

63+
启用多租户模式时, 需要更新上面的 settings
64+
```python
65+
# 启用多租户模式
66+
BKAUTH_ENABLE_MULTI_TENANT_MODE = True
67+
68+
# 用户登录态认证类型
69+
BKAUTH_BACKEND_TYPE = "bk_token" # 只能选择:bk_token
70+
71+
# 验证用户信息的网关 API(租户版本)
72+
# 如 BK_API_URL_TMPL.format(api_name="bk-login") + "/prod/login/api/v3/open/bk-tokens/userinfo/"
73+
BKAUTH_USER_INFO_APIGW_URL = ""
74+
75+
# [可选]`BKAUTH_DEFAULT_PROVIDER_TYPE` 的值用于 JWT 校验时获取默认的用户认证类型。
76+
BKAUTH_DEFAULT_PROVIDER_TYPE = 'BK' # 只能选择:BK
77+
```
78+
6379
2. 在 app config 中进行 patch:
6480

6581
配置登录模块的 apps.py
@@ -131,6 +147,8 @@ class YourDjangoAuthUserCompatibleBackend(DjangoAuthUserCompatibleBackend):
131147
return db_user
132148
```
133149

150+
> 说明: 启用多租户模式后, user 会增加 tenant_id 和 display_name 两个字段,可以通过 `request.user.tenant_id` 获取租户 ID, 通过 `request.user.display_name` 获取用户展示名。
151+
134152
#### [apigw-manager](../apigw-manager) 集成
135153
该 SDK 可以和 apigw-manager 集成,完成网关 JWT 的校验,在 settings 中配置:
136154
```python

sdks/bkpaas-auth/README.rst

Lines changed: 0 additions & 180 deletions
This file was deleted.

sdks/bkpaas-auth/bkpaas_auth/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
# -*- coding: utf-8 -*-
2-
__version__ = "3.0.0"
2+
__version__ = "3.1.0"
33

44

55
def get_user_by_user_id(user_id: str, username_only: bool = True):
66
"""Get a user object from given user_id"""
7+
from bkpaas_auth.conf import bkauth_settings as conf
78
from bkpaas_auth.core.constants import ProviderType
89
from bkpaas_auth.core.encoder import user_id_encoder
910
from bkpaas_auth.core.services import get_bk_user_info, get_rtx_user_info
1011
from bkpaas_auth.models import User
1112

1213
provider_type, username = user_id_encoder.decode(user_id)
1314
user = User(token=None, provider_type=ProviderType(provider_type), username=username)
15+
1416
if username_only:
1517
return user
1618

19+
# 多租户模式下, 暂时没有根据用户名获取用户详细信息的接口
20+
if conf.ENABLE_MULTI_TENANT_MODE:
21+
raise ValueError('Multi-tenant mode only return username, please set username_only=True')
22+
1723
# Request third party service to get info other than username
1824
if provider_type == ProviderType.RTX:
1925
user_info = get_rtx_user_info(username)

sdks/bkpaas-auth/bkpaas_auth/backends.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313

1414
from bkpaas_auth.conf import bkauth_settings
1515
from bkpaas_auth.core.constants import ProviderType
16-
from bkpaas_auth.core.exceptions import InvalidTokenCredentialsError, ServiceError
16+
from bkpaas_auth.core.exceptions import InvalidTokenCredentialsError, ResponseError, ServiceError
1717
from bkpaas_auth.core.plugins import BkTicketPlugin, BkTokenPlugin
1818
from bkpaas_auth.core.token import (
1919
LoginToken,
2020
RequestBackend,
2121
TokenRequestBackend,
22+
UserAccount,
2223
create_user_from_token,
2324
mocked_create_user_from_token,
2425
)
@@ -53,19 +54,33 @@ def __init__(self):
5354

5455
def authenticate(self, request: HttpRequest, auth_credentials: Dict) -> Optional[Union[User, AnonymousUser]]:
5556
try:
56-
username = self.request_backend.request_username(**auth_credentials)
57+
user_account: UserAccount = self.request_backend.request_user_account(**auth_credentials)
58+
59+
if bkauth_settings.ENABLE_MULTI_TENANT_MODE and not user_account.tenant_id:
60+
raise ImproperlyConfigured(
61+
"No tenant information found. You may check whether BKAUTH_USER_INFO_APIGW_URL is set to "
62+
"correct gateway url that can retrieve the user's tenant information"
63+
)
64+
5765
login_token = generate_random_token()
5866
token = LoginToken(
5967
login_token=login_token,
6068
expires_in=bkauth_settings.LOGIN_TOKEN_EXPIRE_IN,
6169
)
62-
token.user_info = UserInfo(username=username)
70+
token.user_info = UserInfo(
71+
username=user_account.bk_username,
72+
display_name=user_account.display_name,
73+
tenant_id=user_account.tenant_id,
74+
)
6375
logger.debug("New login token exchanged by credentials")
76+
except ResponseError as e:
77+
logger.warning(f"authenticate error: {e}")
78+
return None
6479
except InvalidTokenCredentialsError:
65-
logger.warning("authenticate error, invalid credentials given")
80+
logger.warning("authenticate error: invalid credentials given")
6681
return None
6782
except ServiceError:
68-
logger.warning("authenticate error, Error requesting third-party API service")
83+
logger.warning("authenticate error: unable to request backend services")
6984
return None
7085

7186
return self.get_user_by_token(token)
@@ -164,6 +179,9 @@ def connect_to_django_user(self, user: User):
164179
db_user.provider_type = user.provider_type
165180
db_user.bkpaas_user_id = user.bkpaas_user_id
166181
db_user.token = user.token
182+
db_user.display_name = getattr(user, "display_name", user.username)
183+
db_user.tenant_id = getattr(user, "tenant_id", None)
184+
167185
return db_user
168186

169187
def configure_user(self, db_user, bk_user: User):

sdks/bkpaas-auth/bkpaas_auth/conf.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ class Settings:
3131
# 验证用户登录态的 API,如 蓝鲸统一登录校验登录态的 API
3232
USER_COOKIE_VERIFY_URL: str = field(default_factory=get_settings('USER_COOKIE_VERIFY_URL'))
3333

34+
# 是否使用多租户模式
35+
ENABLE_MULTI_TENANT_MODE: bool = field(default_factory=get_settings("ENABLE_MULTI_TENANT_MODE", default=False))
36+
# 验证用户信息的网关 API
37+
USER_INFO_APIGW_URL: str = field(default_factory=get_settings("USER_INFO_APIGW_URL"))
38+
3439
# 获取用户详情的 API,如中文名、邮箱等,且必须提供应用鉴权信息
3540
TOKEN_USER_INFO_ENDPOINT: str = field(default_factory=get_settings('TOKEN_USER_INFO_ENDPOINT'))
3641
TOKEN_APP_CODE: str = field(default_factory=get_settings('TOKEN_APP_CODE'))
@@ -47,6 +52,20 @@ class Settings:
4752
USE_MOCKED_USER_INFO: bool = field(default_factory=get_settings('USE_MOCKED_USER_INFO', default=False))
4853
MOCKED_USER_NAME: str = field(default_factory=get_settings('MOCKED_USER_NAME', default=''))
4954

55+
def __post_init__(self):
56+
self.validate()
57+
58+
def validate(self):
59+
if self.ENABLE_MULTI_TENANT_MODE:
60+
if self.BACKEND_TYPE == "bk_ticket":
61+
raise ImproperlyConfigured(
62+
"BKAUTH_ENABLE_MULTI_TENANT_MODE cannot be True when BKAUTH_BACKEND_TYPE is 'bk_ticket'"
63+
)
64+
if not self.USER_INFO_APIGW_URL:
65+
raise ImproperlyConfigured(
66+
"BKAUTH_USER_INFO_APIGW_URL must be set correctly when BKAUTH_ENABLE_MULTI_TENANT_MODE is True"
67+
)
68+
5069
def reload(self):
5170
for f in fields(self):
5271
setattr(self, f.name, f.default_factory()) # type: ignore
@@ -59,6 +78,7 @@ def reload_settings(*args, **kwargs):
5978
setting: str = kwargs['setting']
6079
if setting.startswith("BKAUTH_"):
6180
bkauth_settings.reload()
81+
bkauth_settings.validate()
6282

6383

6484
setting_changed.connect(reload_settings)

sdks/bkpaas-auth/bkpaas_auth/core/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ class ServiceError(Exception):
77
"""Login or Token service is not available"""
88

99

10+
class HttpRequestError(Exception):
11+
"""http request error"""
12+
13+
14+
class ResponseError(Exception):
15+
"""service response error"""
16+
17+
1018
class InvalidSkeyError(Exception):
1119
"""Invalid uin/skey given"""
1220

0 commit comments

Comments
 (0)