Skip to content

Commit

Permalink
feat(bk-login): support compatibility api and URL use login prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
nannan00 committed Feb 20, 2024
1 parent 4e8c402 commit 086aacd
Show file tree
Hide file tree
Showing 19 changed files with 261 additions and 83 deletions.
52 changes: 0 additions & 52 deletions src/bk-login/bklogin/authentication/api_views.py

This file was deleted.

11 changes: 2 additions & 9 deletions src/bk-login/bklogin/authentication/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import TemplateView

from . import api_views, views
from . import views

urlpatterns = [
# 登录入口
path("", views.LoginView.as_view()),
# 登录小窗入口
path("plain/", xframe_options_exempt(views.LoginView.as_view())),
# 前端页面(选择登录的用户)
path("page/users/", TemplateView.as_view(template_name="index.html")),
path("page/users/", TemplateView.as_view(template_name="index.html"), name="page.users"),
# ------------------------------------------ 租户 & 登录方式选择 ------------------------------------------
# FIXME: 待联调tenant-global-infos完成后删除tenant-global-settings
path("tenant-global-settings/", views.TenantGlobalInfoRetrieveApi.as_view()),
Expand All @@ -42,10 +42,3 @@
# 确认登录的用户
path("sign-in-users/", views.SignInTenantUserCreateApi.as_view()),
]

# OpenAPI
urlpatterns += [
# FIXME: 临时兼容,OpenAPI后面接入APIGateway, 还要考虑兼容原有通过ESB和直接调用的
path("api/v1/is_login/", api_views.CheckTokenApi.as_view()),
path("api/v1/get_user/", api_views.GetUserApi.as_view()),
]
6 changes: 3 additions & 3 deletions src/bk-login/bklogin/authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def get(self, request, *args, **kwargs):
# session记录登录的租户
request.session[SIGN_IN_TENANT_ID_SESSION_KEY] = global_info.only_enabled_auth_tenant.id
# 联邦登录,则直接重定向到第三方登录
return HttpResponseRedirect(f"/auth/idps/{idp.id}/actions/{BuiltinActionEnum.LOGIN}/")
return HttpResponseRedirect(f"/login/auth/idps/{idp.id}/actions/{BuiltinActionEnum.LOGIN}/")

# 返回登录页面
return render(request, self.template_name)
Expand Down Expand Up @@ -362,15 +362,15 @@ def _dispatch_federation_idp_plugin(
# 记录支持登录的租户用户
request.session[ALLOWED_SIGN_IN_TENANT_USERS_SESSION_KEY] = tenant_users
# 联邦认证则重定向到前端选择账号页面
return HttpResponseRedirect(redirect_to="/page/users/")
return HttpResponseRedirect(redirect_to="/login/page/users/")

return self.wrap_plugin_error(
plugin_error_context, plugin.dispatch_extension, action=action, http_method=http_method, request=request
)

def _get_complete_action_url(self, idp_id: str, action: str) -> str:
"""获取完整"""
return urljoin(settings.BK_LOGIN_URL, f"auth/idps/{idp_id}/actions/{action}/")
return urljoin(settings.BK_LOGIN_URL, f"/login/auth/idps/{idp_id}/actions/{action}/")

def _auth_backend(
self, request, sign_in_tenant_id: str, idp_id: str, user_infos: Dict[str, Any] | List[Dict[str, Any]]
Expand Down
10 changes: 10 additions & 0 deletions src/bk-login/bklogin/open_apis/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
10 changes: 10 additions & 0 deletions src/bk-login/bklogin/open_apis/compatibility/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
50 changes: 50 additions & 0 deletions src/bk-login/bklogin/open_apis/compatibility/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from blue_krill.data_types.enum import EnumField, StructuredEnum


class CompatibilityApiErrorCodeEnum(str, StructuredEnum):
"""兼容 API 错误码"""

SUCCESS = EnumField("SUCCESS")
PARAM_NOT_VALID = EnumField("PARAM_NOT_VALID")
USER_NOT_EXISTS = EnumField("USER_NOT_EXISTS")
USER_NOT_EXISTS2 = EnumField("USER_NOT_EXISTS2")
USER_INFO_UPDATE_FAIL = EnumField("USER_INFO_UPDATE_FAIL")
ACCESS_PERMISSION_DENIED = EnumField("ACCESS_PERMISSION_DENIED")


CompatibilityApiErrorCodeMap = {
"v1": {
CompatibilityApiErrorCodeEnum.SUCCESS: "00",
CompatibilityApiErrorCodeEnum.PARAM_NOT_VALID: "1200",
CompatibilityApiErrorCodeEnum.USER_NOT_EXISTS: "1201",
CompatibilityApiErrorCodeEnum.USER_NOT_EXISTS2: "1300",
CompatibilityApiErrorCodeEnum.USER_INFO_UPDATE_FAIL: "1202",
CompatibilityApiErrorCodeEnum.ACCESS_PERMISSION_DENIED: "1203",
},
"v2": {
CompatibilityApiErrorCodeEnum.SUCCESS: 0,
CompatibilityApiErrorCodeEnum.PARAM_NOT_VALID: 1302100,
CompatibilityApiErrorCodeEnum.USER_NOT_EXISTS: 1302101,
CompatibilityApiErrorCodeEnum.USER_NOT_EXISTS2: 1302103,
CompatibilityApiErrorCodeEnum.USER_INFO_UPDATE_FAIL: 1302102,
CompatibilityApiErrorCodeEnum.ACCESS_PERMISSION_DENIED: 1302403,
},
"v3": {
CompatibilityApiErrorCodeEnum.SUCCESS: 0,
CompatibilityApiErrorCodeEnum.PARAM_NOT_VALID: 1302100,
CompatibilityApiErrorCodeEnum.USER_NOT_EXISTS: 1302101,
CompatibilityApiErrorCodeEnum.USER_NOT_EXISTS2: 1302103,
CompatibilityApiErrorCodeEnum.USER_INFO_UPDATE_FAIL: 1302102,
CompatibilityApiErrorCodeEnum.ACCESS_PERMISSION_DENIED: 1302403,
},
}
52 changes: 52 additions & 0 deletions src/bk-login/bklogin/open_apis/compatibility/mixins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from typing import Dict

from django.conf import settings
from django.http import JsonResponse

from .constants import CompatibilityApiErrorCodeEnum, CompatibilityApiErrorCodeMap


class CompatibilityApiMixin:
"""兼容API Mixin"""

api_version = "v1"

@staticmethod
def is_request_from_esb(request):
"""
请求是否来自ESB
"""
x_app_token = request.META.get("HTTP_X_APP_TOKEN")
x_app_code = request.META.get("HTTP_X_APP_CODE")
if x_app_code == "esb" and x_app_token == settings.BK_PAAS_APP_SECRET:
return True

return False

def fail_response(self, error_code: CompatibilityApiErrorCodeEnum, message: str) -> JsonResponse:
code = CompatibilityApiErrorCodeMap[self.api_version][error_code] # type: ignore
if self.api_version == "v2":
return JsonResponse({"result": False, "bk_error_code": code, "bk_error_msg": message, "data": {}})

return JsonResponse({"result": False, "code": code, "message": message, "data": {}})

def ok_response(self, data: Dict) -> JsonResponse:
code = CompatibilityApiErrorCodeMap[self.api_version][CompatibilityApiErrorCodeEnum.SUCCESS] # type: ignore
if self.api_version == "v2":
return JsonResponse({"result": True, "bk_error_code": code, "bk_error_msg": "", "data": data})

return JsonResponse({"result": True, "code": code, "message": "", "data": data})

@property
def username_key(self) -> str:
return "bk_username" if self.api_version in ["v2", "v3"] else "username"
77 changes: 77 additions & 0 deletions src/bk-login/bklogin/open_apis/compatibility/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from typing import Dict

from django.conf import settings
from django.views.generic import View

from bklogin.authentication.manager import BkTokenManager
from bklogin.component.bk_user import api as bk_user_api

from .constants import CompatibilityApiErrorCodeEnum
from .mixins import CompatibilityApiMixin


class TokenIntrospectCompatibilityApi(View, CompatibilityApiMixin):
"""Token 解析"""

def get(self, request, *args, **kwargs):
bk_token = request.GET.get(settings.BK_TOKEN_COOKIE_NAME)

ok, username, msg = BkTokenManager().is_bk_token_valid(bk_token)
if not ok:
return self.fail_response(error_code=CompatibilityApiErrorCodeEnum.PARAM_NOT_VALID, message=msg)

return self.ok_response(data={self.username_key: username})


class UserRetrieveCompatibilityApi(View, CompatibilityApiMixin):
"""通过 Token 获取用户"""

def get(self, request, *args, **kwargs):
bk_token = request.GET.get(settings.BK_TOKEN_COOKIE_NAME)

ok, username, msg = BkTokenManager().is_bk_token_valid(bk_token)
if not ok:
# 对于来着 ESB 请求,如果 bk_token 验证不通过,还可以通过 username 参数指定查询的用户
username = request.GET.get(self.username_key)
if not (self.is_request_from_esb(request) and username):
return self.fail_response(error_code=CompatibilityApiErrorCodeEnum.PARAM_NOT_VALID, message=msg)

# 通过用户管理查询用户信息
user = bk_user_api.get_tenant_user(username)

# Note: 与 self.username_key 不一样,区别在于 v3 API, 其 is_login 放回 bk_username, get_user 返回 username
username_key = "bk_username" if self.api_version == "v2" else "username"
user_info: Dict[str, int | str] = {
# bk_username / username
username_key: user.id,
# 基本信息
"language": user.language,
"time_zone": user.time_zone,
# 多租户版本新增
"tenant_id": user.tenant_id,
"full_name": user.full_name, # 姓名
"display_name": user.display_name, # 统一展示名
# ----- 兼容 ------
# 兼容数据
"chname": user.full_name,
# 【兼容】固定或空值返回
"qq": "",
"phone": "",
"email": "",
"wx_userid": "",
}
# 角色已废弃,这里只是兼容处理
role_key = "bk_role" if self.api_version == "v2" else "role"
user_info[role_key] = "0" if self.api_version == "v1" else 0

return self.ok_response(data=user_info)
25 changes: 25 additions & 0 deletions src/bk-login/bklogin/open_apis/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-用户管理(Bk-User) available.
Copyright (C) 2017-2021 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at http://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""
from django.urls import path

from .compatibility import views as compatibility_views

urlpatterns = [
# 兼容API, 兼容原有通过 ESB 和直接调用的两种方式
path("accounts/is_login/", compatibility_views.TokenIntrospectCompatibilityApi.as_view(api_version="v1")),
path("accounts/get_user/", compatibility_views.UserRetrieveCompatibilityApi.as_view(api_version="v1")),
path("api/v2/is_login/", compatibility_views.TokenIntrospectCompatibilityApi.as_view(api_version="v2")),
path("api/v2/get_user/", compatibility_views.UserRetrieveCompatibilityApi.as_view(api_version="v2")),
path("api/v3/is_login/", compatibility_views.TokenIntrospectCompatibilityApi.as_view(api_version="v3")),
path("api/v3/get_user/", compatibility_views.UserRetrieveCompatibilityApi.as_view(api_version="v3")),
# TODO: 新的 OpenAPI 后面统一接入 APIGateway,不支持直接调用,
# 同时只提供给 APIGateway 做用户认证的接口与通用 OpenAPI 区分开
]
9 changes: 6 additions & 3 deletions src/bk-login/bklogin/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,10 @@
TIME_ZONE = "Asia/Shanghai"

# SITE
SITE_URL = env.str("SITE_URL", default="/")
SITE_URL = env.str("SITE_URL", default="/login/")
# Static files (CSS, JavaScript, Images)
STATIC_ROOT = BASE_DIR / "staticfiles"
WHITENOISE_STATIC_PREFIX = "/staticfiles/"
WHITENOISE_STATIC_PREFIX = os.path.join(SITE_URL, "staticfiles/")
# STATIC_URL 也可以是CDN地址
STATIC_URL = env.str("STATIC_URL", default=SITE_URL + "staticfiles/")

Expand All @@ -121,6 +121,9 @@
BK_APP_SECRET = env.str("BK_APP_SECRET")
# Django SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = BK_APP_SECRET
# [兼容] 用于判断是否 ESB 请求(2.x 版本里,paas_v2/ESB/console/login 共用 bk_paas 的 AppSecret)
BK_PAAS_APP_SECRET = env.str("BK_PAAS_APP_SECRET", "")


# 蓝鲸数据库内容加密私钥
# 使用 `from cryptography.fernet import Fernet; Fernet.generate_key()` 生成随机秘钥
Expand Down Expand Up @@ -195,7 +198,7 @@
_LOG_DIR = env.str("LOG_FILE_DIR", default=_DEFAULT_LOG_DIR)
_LOG_FILE_NAME_PREFIX = env.str("LOG_FILE_NAME_PREFIX", default=BK_APP_CODE)
if not os.path.exists(_LOG_DIR):
os.makedirs(_LOG_DIR)
os.makedirs(_LOG_DIR, exist_ok=True)
_LOGGING_FORMAT = {
"()": "pythonjsonlogger.jsonlogger.JsonFormatter",
"fmt": ("%(levelname)s %(asctime)s %(pathname)s %(lineno)d " "%(funcName)s %(process)d %(thread)d %(message)s"),
Expand Down
11 changes: 10 additions & 1 deletion src/bk-login/bklogin/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
from django.urls import include, path

urlpatterns = [
path("", include("bklogin.authentication.urls")),
path(
# 兼容旧版本,与 PaaS V2 / Console 共享域名,/login 路径前缀指向登录服务
"login/",
include(
[
path("", include("bklogin.authentication.urls")),
path("", include("bklogin.open_apis.urls")),
]
),
),
path("", include("bklogin.monitoring.urls")),
]
2 changes: 1 addition & 1 deletion src/bk-login/pages/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<link rel="icon" href="<%= process.env.BK_STATIC_URL %>/images/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="<%= process.env.BK_STATIC_URL %>/images/favicon.png" type="image/x-icon" />
<link rel="shortcut icon" href="<%= process.env.BK_STATIC_URL %>/images/favicon.ico" type="image/x-icon" />
<meta charset="utf-8">
<title> {{ TITLE }} </title>
</head>
Expand Down
Loading

0 comments on commit 086aacd

Please sign in to comment.