Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: login new flow #1706

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions src/bk-login/bklogin/authentication/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,18 @@
# 前端页面(选择登录的用户)
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()),
# 租户全局信息
path("tenant-global-infos/", views.TenantGlobalInfoRetrieveApi.as_view()),
# 租户信息
path("tenants/", views.TenantListApi.as_view()),
path("tenants/<str:tenant_id>/", views.TenantRetrieveApi.as_view()),
# 确认登录的租户
path("sign-in-tenants/", views.SignInTenantCreateApi.as_view()),
# 认证源
path("idps/", views.TenantIdpListApi.as_view()),
path(
"tenants/<str:tenant_id>/idp-owner-tenants/<str:idp_owner_tenant_id>/idps/", views.TenantIdpListApi.as_view()
),
# ------------------------------------------ 认证插件 ------------------------------------------
# 插件认证
path(
"tenants/<str:tenant_id>/idps/<str:idp_id>/actions/<str:action>/",
xframe_options_exempt(views.IdpPluginDispatchView.as_view()),
),
path("auth/idps/<str:idp_id>/actions/<str:action>/", xframe_options_exempt(views.IdpPluginDispatchView.as_view())),
# ------------------------------------------ 用户选择 ------------------------------------------
# 已认证后的用户列表
Expand Down
102 changes: 13 additions & 89 deletions src/bk-login/bklogin/authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
from bklogin.common.response import APISuccessResponse
from bklogin.component.bk_user import api as bk_user_api
from bklogin.component.bk_user.constants import IdpStatus
from bklogin.idp_plugins.base import BaseCredentialIdpPlugin, BaseFederationIdpPlugin, get_plugin_cls, get_plugin_type
from bklogin.idp_plugins.constants import AllowedHttpMethodEnum, BuiltinActionEnum, PluginTypeEnum
from bklogin.idp_plugins.base import BaseCredentialIdpPlugin, BaseFederationIdpPlugin, get_plugin_cls
from bklogin.idp_plugins.constants import AllowedHttpMethodEnum, BuiltinActionEnum
from bklogin.idp_plugins.exceptions import (
InvalidParamError,
ParseRequestBodyError,
Expand Down Expand Up @@ -72,40 +72,10 @@ def get(self, request, *args, **kwargs):
# 存储到当前session里,待认证成功后取出后重定向
request.session["redirect_uri"] = redirect_url

# 当只有一个租户且该租户有且仅有一种登录方式,且该登录方式为联邦登录,则直接重定向到第三方登录
global_info = bk_user_api.get_global_info()
if (
global_info.enabled_auth_tenant_number == 1
and global_info.only_enabled_auth_tenant
and len(global_info.only_enabled_auth_tenant.enabled_idps) == 1
):
idp = global_info.only_enabled_auth_tenant.enabled_idps[0]
# 判断是否联邦登录
if get_plugin_type(idp.plugin_id) == PluginTypeEnum.FEDERATION:
# session记录登录的租户
request.session[SIGN_IN_TENANT_ID_SESSION_KEY] = global_info.only_enabled_auth_tenant.id
# 联邦登录,则直接重定向到第三方登录
return HttpResponseRedirect(
f"{settings.SITE_URL}auth/idps/{idp.id}/actions/{BuiltinActionEnum.LOGIN}/"
)

# 返回登录页面
return render(request, self.template_name)


class TenantGlobalInfoRetrieveApi(View):
def get(self, request, *args, **kwargs):
"""
租户的全局信息
"""
global_info = bk_user_api.get_global_info()
return APISuccessResponse(
data=global_info.model_dump(
include={"tenant_visible", "enabled_auth_tenant_number", "only_enabled_auth_tenant"}
)
)


class TenantListApi(View):
def get(self, request, *args, **kwargs):
"""
Expand All @@ -115,69 +85,24 @@ def get(self, request, *args, **kwargs):
tenant_ids_str = request.GET.get("tenant_ids", "")
tenant_ids = [i for i in tenant_ids_str.split(",") if i]

# 无tenant_ids表示需要获取全部租户,这时候需要检查租户是否可见
global_setting = bk_user_api.get_global_info()
if not tenant_ids and not global_setting.tenant_visible:
raise error_codes.NO_PERMISSION.f(_("租户信息不可见"))

tenants = bk_user_api.list_tenant(tenant_ids)

return APISuccessResponse(data=[t.model_dump(include={"id", "name", "logo"}) for t in tenants])


class TenantRetrieveApi(View):
def get(self, request, *args, **kwargs):
"""
通过租户ID,查询单个租户信息
"""
tenant_id = kwargs["tenant_id"]
tenant = bk_user_api.get_tenant(tenant_id)
if tenant is None:
raise error_codes.OBJECT_NOT_FOUND.f(f"租户 {tenant_id} 不存在", replace=True)

return APISuccessResponse(data=tenant.model_dump(include={"id", "name", "logo"}))


class SignInTenantCreateApi(View):
def post(self, request, *args, **kwargs):
"""
确认选择要登录的租户
"""
request_body = parse_request_body_json(request.body)
tenant_id = request_body.get("tenant_id")

# 校验参数
if not tenant_id:
raise error_codes.VALIDATION_ERROR.f(_("tenant_id参数必填"))

# 校验租户是否存在
tenants = bk_user_api.list_tenant()
tenant_id_set = {i.id for i in tenants}
if tenant_id not in tenant_id_set:
raise error_codes.OBJECT_NOT_FOUND.f(_("租户({})未找到").format(tenant_id))

# session记录登录的租户
request.session[SIGN_IN_TENANT_ID_SESSION_KEY] = tenant_id

return APISuccessResponse()
return APISuccessResponse(data=[t.model_dump() for t in tenants])


class TenantIdpListApi(View):
def get(self, request, *args, **kwargs):
"""
获取需要登录租户的认证方式列表
"""
# Session里获取当前登录的租户
sign_in_tenant_id = request.session.get(SIGN_IN_TENANT_ID_SESSION_KEY)
if not sign_in_tenant_id:
raise error_codes.NO_PERMISSION.f(_("未选择需要登录的租户"))
# 获取路径参数
tenant_id = kwargs["tenant_id"]
idp_owner_tenant_id = kwargs["idp_owner_tenant_id"]

# 查询本租户配置的认证源
idps = bk_user_api.list_idp(sign_in_tenant_id)
# 获取指定租户中 本租户可用 且 已启用 的认证源
idps = bk_user_api.list_idp(tenant_id, idp_owner_tenant_id)

return APISuccessResponse(
data=[i.model_dump(include={"id", "name", "plugin"}) for i in idps if i.status == IdpStatus.ENABLED],
)
return APISuccessResponse(data=[i.model_dump() for i in idps])


class IdpBasicInfo(pydantic.BaseModel):
Expand Down Expand Up @@ -208,6 +133,10 @@ def dispatch(self, request, *args, **kwargs):
"""
# Session里获取当前登录的租户
sign_in_tenant_id = request.session.get(SIGN_IN_TENANT_ID_SESSION_KEY)
if not sign_in_tenant_id:
sign_in_tenant_id = kwargs.get("tenant_id")
# session记录登录的租户
request.session[SIGN_IN_TENANT_ID_SESSION_KEY] = sign_in_tenant_id
if not sign_in_tenant_id:
raise error_codes.NO_PERMISSION.f(_("未选择需要登录的租户"))

Expand All @@ -218,11 +147,6 @@ def dispatch(self, request, *args, **kwargs):

# 获取认证源信息
idp = bk_user_api.get_idp(idp_id)
# 判断是否当前登录租户所属数据源
# TODO: 后续协同租户的数据源,需要调整判断关系
if idp.owner_tenant_id != sign_in_tenant_id:
raise error_codes.NO_PERMISSION.f(_("非当前登录租户所配置的认证源"))

if idp.status != IdpStatus.ENABLED:
raise error_codes.NO_PERMISSION.f(_("当前认证源未启用,无法通过该认证源登录"))

Expand Down
31 changes: 7 additions & 24 deletions src/bk-login/bklogin/component/bk_user/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from bklogin.component.http import HttpStatusCode, http_get, http_post
from bklogin.utils.url import urljoin

from .models import GlobalInfo, IdpDetailInfo, IdpInfo, TenantInfo, TenantUserDetailInfo, TenantUserInfo
from .models import IdpDetail, IdpInfo, TenantInfo, TenantUserDetailInfo, TenantUserInfo

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -54,12 +54,6 @@ def _call_bk_user_api_20x(http_func, url_path: str, **kwargs):
return _call_bk_user_api(http_func, url_path, allow_error_status_func=lambda s: False, **kwargs)["data"]


def get_global_info() -> GlobalInfo:
"""获取全局信息"""
data = _call_bk_user_api_20x(http_get, "/api/v1/login/global-infos/")
return GlobalInfo(**data)


def list_tenant(tenant_ids: List[str] | None = None) -> List[TenantInfo]:
"""查询租户列表,支持过滤"""
params = {}
Expand All @@ -70,29 +64,18 @@ def list_tenant(tenant_ids: List[str] | None = None) -> List[TenantInfo]:
return [TenantInfo(**i) for i in data]


def get_tenant(tenant_id: str) -> TenantInfo | None:
"""通过租户 ID 获取租户信息"""
resp = _call_bk_user_api(
http_get,
f"/api/v1/login/tenants/{tenant_id}/",
allow_error_status_func=lambda s: s.is_not_found,
)
if resp.get("error"):
return None

return TenantInfo(**resp["data"])


def list_idp(tenant_id: str) -> List[IdpInfo]:
def list_idp(tenant_id: str, idp_owner_tenant_id: str) -> List[IdpInfo]:
"""获取租户关联的认证源"""
data = _call_bk_user_api_20x(http_get, f"/api/v1/login/tenants/{tenant_id}/idps/")
data = _call_bk_user_api_20x(
http_get, f"/api/v1/login/tenants/{tenant_id}/idp-owner-tenants/{idp_owner_tenant_id}/idps/"
)
return [IdpInfo(**i) for i in data]


def get_idp(idp_id: str) -> IdpDetailInfo:
def get_idp(idp_id: str) -> IdpDetail:
"""获取IDP信息"""
data = _call_bk_user_api_20x(http_get, f"/api/v1/login/idps/{idp_id}/")
return IdpDetailInfo(**data)
return IdpDetail(**data)


def list_matched_tencent_user(tenant_id: str, idp_id: str, idp_users: List[Dict[str, Any]]) -> List[TenantUserInfo]:
Expand Down
36 changes: 14 additions & 22 deletions src/bk-login/bklogin/component/bk_user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,52 +15,44 @@
from .constants import IdpStatus


class EnabledIdp(BaseModel):
class CollaborationTenant(BaseModel):
"""协同租户信息"""

id: str
plugin_id: str
name: str


class OnlyEnabledAuthTenant(BaseModel):
class TenantInfo(BaseModel):
"""租户信息"""

id: str
name: str
logo: str = ""
enabled_idps: List[EnabledIdp]

collaboration_tenants: List[CollaborationTenant] = []

class GlobalInfo(BaseModel):
"""全局信息"""

tenant_visible: bool
enabled_auth_tenant_number: int
# 当且仅当只有一个租户认证可用时候才有值,即 enabled_auth_tenant_number = 1 时才有值
only_enabled_auth_tenant: OnlyEnabledAuthTenant | None


class TenantInfo(BaseModel):
"""租户信息"""
class IdpInfo(BaseModel):
"""认证源基本信息"""

id: str
name: str
logo: str = ""
plugin_id: str
data_source_type: str
nannan00 marked this conversation as resolved.
Show resolved Hide resolved


class IdpPluginInfo(BaseModel):
id: str
name: str


class IdpInfo(BaseModel):
"""认证源基本信息"""
class IdpDetail(BaseModel):
"""认证源详情"""

id: str
name: str
status: IdpStatus
plugin: IdpPluginInfo


class IdpDetailInfo(IdpInfo):
"""认证源详情"""

owner_tenant_id: str
plugin_config: Dict[str, Any]

Expand Down
Loading
Loading