Skip to content

Commit 61625d6

Browse files
authored
feat: User 模型添加 time_zone 字段 (#244)
1 parent 31716ad commit 61625d6

7 files changed

Lines changed: 85 additions & 4 deletions

File tree

sdks/bkpaas-auth/bkpaas_auth/backends.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def authenticate(self, request: HttpRequest, auth_credentials: Dict) -> Optional
7070
token.user_info = UserInfo(
7171
username=user_account.bk_username,
7272
display_name=user_account.display_name,
73+
time_zone=user_account.time_zone,
7374
tenant_id=user_account.tenant_id,
7475
)
7576
logger.debug("New login token exchanged by credentials")
@@ -235,8 +236,8 @@ def authenticate_with_signature_v3(
235236
request: HttpRequest,
236237
gateway_name: str,
237238
bk_username: str,
238-
tenant_id: str,
239-
verified: bool,
239+
tenant_id: str | None = None,
240+
verified: bool = False,
240241
**credentials: Dict,
241242
) -> Union[User, AnonymousUser]:
242243
"""authenticate function with signature required by ApiGatewayJWTUserMiddleware in apigw_manager == '^3.0.0'"""

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,13 @@ class UserAccount(NamedTuple):
3434
3535
:param bk_username: 用户唯一标识,全局唯一
3636
:param display_name: 用户展示名.
37+
:param time_zone: 用户时区信息. 为 None 时表示用户无时区信息(可能接口不支持)
3738
:param tenant_id: 用户所属租户 ID. 为 None 时表示用户无租户信息(可能接口不支持)
3839
"""
3940

4041
bk_username: str
4142
display_name: str
43+
time_zone: str | None = None
4244
tenant_id: str | None = None
4345

4446

@@ -98,6 +100,7 @@ def _request_apigw(**credentials) -> UserAccount:
98100
return UserAccount(
99101
bk_username=bk_username,
100102
display_name=resp_json["data"].get("display_name") or bk_username,
103+
time_zone=resp_json["data"].get("time_zone"),
101104
tenant_id=resp_json["data"].get("tenant_id"),
102105
)
103106

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class UserInfo:
1616
def __init__(self, username, **kwargs):
1717
self.username = username
1818
self.display_name = kwargs.get("display_name") or username
19+
self.time_zone = kwargs.get("time_zone")
1920
self.tenant_id = kwargs.get("tenant_id")
2021

2122
def provide(self, user: 'User'):

sdks/bkpaas-auth/bkpaas_auth/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class AbstractUserWithProvider(models.AbstractBaseUser, models.AnonymousUser):
1313

1414
bkpaas_user_id = db_models.CharField(primary_key=True, max_length=255)
1515
USERNAME_FIELD = 'bkpaas_user_id'
16-
USERINFO_FIELDS = ('display_name', 'tenant_id', 'nickname', 'chinese_name', 'avatar_url', 'email', 'phone')
16+
USERINFO_FIELDS = ('display_name', 'tenant_id', 'time_zone', 'nickname', 'chinese_name', 'avatar_url', 'email', 'phone')
1717

1818
def __init__(self, provider_type, username):
1919
if not provider_type:

sdks/bkpaas-auth/tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def get_rtx_user_info_response(settings):
7979
"bk_username": settings.USER_NAME,
8080
"LoginName": settings.USER_NAME,
8181
"ChineseName": settings.USER_NICKNAME,
82+
"time_zone": "Asia/Shanghai",
8283
"avatar_url": "",
8384
},
8485
"result": True,

sdks/bkpaas-auth/tests/test_backends.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ def test_authenticate_bk_token(self, mock_request, mocker):
4848
)
4949
@mock.patch("requests.Session.request")
5050
def test_authenticate_bk_token_for_tenant_mode(self, mock_request, mocker):
51+
"""Test basic fields validation for tenant mode authentication"""
5152
mock_request.return_value = mock_raw_response(
5253
{
5354
"data": {
5455
"bk_username": "5461b239-5ef2-4c81-a682-5907dbd5f394",
5556
"tenant_id": "system",
5657
"display_name": "foo",
5758
"language": "zh-cn",
58-
"time_zone": "Asia/Shanghai",
5959
}
6060
}
6161
)
@@ -71,6 +71,45 @@ def test_authenticate_bk_token_for_tenant_mode(self, mock_request, mocker):
7171
assert getattr(user, "display_name") == "foo"
7272
assert getattr(user, "tenant_id") == "system"
7373

74+
@override_settings(
75+
BKAUTH_ENABLE_MULTI_TENANT_MODE=True,
76+
BKAUTH_BACKEND_TYPE="bk_token",
77+
BKAUTH_USER_INFO_APIGW_URL="fake_url",
78+
)
79+
@pytest.mark.parametrize(
80+
("api_time_zone", "expected_time_zone"),
81+
[
82+
# Valid time zones
83+
("Asia/Shanghai", "Asia/Shanghai"),
84+
("UTC", "UTC"),
85+
("Asia/Tokyo", "Asia/Tokyo"),
86+
# Missing time_zone field
87+
(None, None),
88+
],
89+
)
90+
@mock.patch("requests.Session.request")
91+
def test_authenticate_bk_token_for_tenant_mode_time_zone(
92+
self, mock_request, mocker, api_time_zone, expected_time_zone
93+
):
94+
"""Test time_zone field handling in tenant mode authentication"""
95+
response_data = {
96+
"data": {
97+
"bk_username": "test_user",
98+
"tenant_id": "system",
99+
"display_name": "test",
100+
"language": "zh-cn",
101+
}
102+
}
103+
if api_time_zone is not None:
104+
response_data["data"]["time_zone"] = api_time_zone
105+
106+
mock_request.return_value = mock_raw_response(response_data)
107+
108+
user = UniversalAuthBackend().authenticate(
109+
request=mocker.MagicMock(), auth_credentials={"bk_token": generate_random_string()}
110+
)
111+
112+
assert getattr(user, "time_zone") == expected_time_zone
74113

75114
class TestAPIGatewayAuthBackend:
76115
@pytest.fixture()

sdks/bkpaas-auth/tests/test_models.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# -*- coding: utf-8 -*-
2+
import copy
23
from unittest import mock
34

5+
import pytest
46
from django.conf import settings
57

68
from bkpaas_auth.core.constants import ProviderType
@@ -27,6 +29,7 @@ def test_user_authenticated(self):
2729
assert user.is_anonymous is False
2830

2931
def test_user_info(self, get_rtx_user_info_response):
32+
"""Test base user info fields mapping from RTX user info"""
3033
with mock.patch('requests.Session.request') as mocked_request:
3134
mocked_request.return_value = mock_json_response(get_rtx_user_info_response)
3235

@@ -41,3 +44,36 @@ def test_user_info(self, get_rtx_user_info_response):
4144
assert user.email == f'{settings.USER_NAME}@tencent.com'
4245
assert user.username == settings.USER_NAME
4346
assert user.chinese_name == settings.USER_NICKNAME
47+
48+
@mock.patch('django.core.cache.cache.set')
49+
@mock.patch('django.core.cache.cache.get', return_value=None)
50+
@pytest.mark.parametrize(
51+
("api_time_zone", "expected_time_zone"),
52+
[
53+
# Valid IANA time zones
54+
("Asia/Shanghai", "Asia/Shanghai"),
55+
("UTC", "UTC"),
56+
# Missing time_zone field
57+
(None, None),
58+
],
59+
)
60+
def test_user_info_time_zone(
61+
self, _mock_cache_get, _mock_cache_set, get_rtx_user_info_response, api_time_zone, expected_time_zone
62+
):
63+
"""Test time_zone field handling with various values"""
64+
with mock.patch('requests.Session.request') as mocked_request:
65+
response_data = copy.deepcopy(get_rtx_user_info_response)
66+
if api_time_zone is None:
67+
response_data["data"].pop("time_zone", None)
68+
else:
69+
response_data["data"]["time_zone"] = api_time_zone
70+
71+
mocked_request.return_value = mock_json_response(response_data)
72+
73+
user_info = get_rtx_user_info(username=settings.USER_NAME)
74+
assert user_info.time_zone == expected_time_zone
75+
76+
token = LoginToken('token', expires_in=86400)
77+
user = User(token)
78+
user_info.provide(user)
79+
assert user.time_zone == expected_time_zone

0 commit comments

Comments
 (0)