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

fix: 修复登出失败。 #951

Closed
2 changes: 1 addition & 1 deletion .github/workflows/eslint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
with:
node-version: 16
cache: 'npm'
cache-dependency-path: 'src/pages/yarn.lock'
cache-dependency-path: 'src/pages/package-lock.json'
- name: Install modules
run: |
cd src/pages
Expand Down
4 changes: 4 additions & 0 deletions src/api/bkuser_core/api/login/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class LoginUpsertSerializer(serializers.Serializer):
staff_status = serializers.CharField(required=False)
wx_userid = serializers.CharField(required=False, allow_blank=True)

iso_code = serializers.CharField(required=False)


class LoginBatchQuerySerializer(serializers.Serializer):
username_list = serializers.ListField(child=serializers.CharField(), required=False)
Expand All @@ -56,6 +58,7 @@ class LoginBatchResponseSerializer(serializers.Serializer):
time_zone = serializers.CharField()
email = serializers.CharField()
role = serializers.IntegerField()
iso_code = serializers.CharField()

def get_username(self, data):
return get_username(
Expand Down Expand Up @@ -87,6 +90,7 @@ class Meta:
"language",
"domain",
"category_id",
"iso_code",
# NOTE: 这里缩减登陆成功之后的展示字段
# "position",
# "logo_url", => to logo?
Expand Down
37 changes: 35 additions & 2 deletions src/api/bkuser_core/api/login/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
from bkuser_core.categories.models import ProfileCategory
from bkuser_core.common.cache import clear_cache_if_succeed
from bkuser_core.common.error_codes import error_codes
from bkuser_core.profiles.constants import ProfileStatus
from bkuser_core.profiles.constants import ProfileStatus, StaffStatus
from bkuser_core.profiles.models import Profile, ProfileTokenHolder
from bkuser_core.profiles.utils import make_passwd_reset_url_by_token, parse_username_domain
from bkuser_core.profiles.utils import align_country_iso_code, make_passwd_reset_url_by_token, parse_username_domain
from bkuser_core.profiles.validators import validate_username
from bkuser_core.user_settings.loader import ConfigProvider

Expand Down Expand Up @@ -134,6 +134,17 @@ def login(self, request):
raise error_codes.PASSWORD_ERROR
# NOTE: 安全原因, 不能返回账户状态
# raise error_codes.USER_IS_LOCKED
elif profile.staff_status == StaffStatus.OUT.value:
create_profile_log(
profile=profile,
operation="Login",
request=request,
params={"is_success": False, "reason": LogInFailReason.RESIGNED_USER.value},
)
logger.info("login check, profile<%s> of %s is resigned", profile.username, message_detail)
raise error_codes.PASSWORD_ERROR
# NOTE: 安全原因, 不能返回账户状态
# raise error_codes.USER_IS_RESIGNED

# 获取密码配置
auto_unlock_seconds = int(config_loader["auto_unlock_seconds"])
Expand Down Expand Up @@ -272,6 +283,10 @@ def upsert(self, request):
username = serializer.validated_data.pop("username")
domain = serializer.validated_data.pop("domain", None)

iso_code = None
if "iso_code" in validated_data:
iso_code = validated_data.pop("iso_code")

try:
category = ProfileCategory.objects.get(domain=domain)
# 当 domain 存在时,校验 username
Expand All @@ -296,6 +311,24 @@ def upsert(self, request):
if created:
logger.info("user<%s/%s> created by login", category.id, username)

if iso_code:
try:
# NOTE: 这里直接用iso_code设置country_code, 无视原来的country_code
# 原因: 目前产品只暴露iso_code, country_code是内部的
profile.country_code, profile.iso_code = align_country_iso_code(
country_code="",
iso_code=iso_code,
)
except ValueError:
profile.country_code = settings.DEFAULT_COUNTRY_CODE
profile.iso_code = settings.DEFAULT_IOS_CODE

try:
profile.save()
except Exception: # pylint: disable=broad-except
logger.exception("failed to update iso_code for profile %s", username)
# do nothing

return Response(data=ProfileSerializer(profile, context={"request": request}).data)

@method_decorator(cache_page(settings.GLOBAL_CACHES_TIMEOUT))
Expand Down
24 changes: 24 additions & 0 deletions src/api/bkuser_core/api/web/password/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ def post(self, request, *args, **kwargs):
logger.exception("failed to get profile by email<%s>", email)
return Response(data={})

# 用户状态校验
if not profile.is_normal:
error_msg = (
"failed to send password via sms."
"profile is abnormal [profile.id=%s, profile.username=%s, profile.enabled=%s, profile.status=%s]"
)
logger.error(
error_msg, profile.id, f"{profile.username}@{profile.domain}", profile.enabled, profile.status
)
return Response(data={})

# FIXME:需要check是否有频率限制,否则会对用户有骚扰 send_password_by_email
token_holder = ProfileTokenHolder.objects.create(profile=profile)
try:
Expand Down Expand Up @@ -199,9 +210,22 @@ def post(self, request, *args, **kwargs):
profile = get_profile_by_username(username, domain)

# 不存在则才是telephone
# FIXME: get_profile_by_telephone 和 get_profile_by_username 理论上行为应该一致, 目前不一致, 需要重构
if not profile:
profile = get_profile_by_telephone(input_telephone)

# 用户状态校验
if not profile.is_normal:
error_msg = (
"failed to send password via sms. "
"profile is abnormal [profile.id=%s, profile.username=%s, profile.enabled=%s, profile.status=%s]"
)

logger.error(
error_msg, profile.id, f"{profile.username}@{profile.domain}", profile.enabled, profile.status
)
return Response(data={})

except Profile.DoesNotExist:
logger.exception(
"failed to get profile by telephone<%s> or username<%s>", input_telephone, input_telephone
Expand Down
14 changes: 8 additions & 6 deletions src/api/bkuser_core/apis/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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.conf import settings
from django.conf.urls import include, url
from drf_yasg import openapi
from drf_yasg.generators import OpenAPISchemaGenerator
Expand Down Expand Up @@ -41,9 +42,10 @@ def get_schema(self, request=None, public=False):
],
)


urlpatterns = [
url(r"^swagger(?P<format>\.json|\.yaml)$", schema_view.without_ui(cache_timeout=0), name="schema-json"),
url(r"^swagger/$", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
url(r"^redoc/$", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc"),
]
urlpatterns = []
if settings.DEBUG:
urlpatterns += [
url(r"^swagger(?P<format>\.json|\.yaml)$", schema_view.without_ui(cache_timeout=0), name="schema-json"),
url(r"^swagger/$", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
url(r"^redoc/$", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc"),
]
2 changes: 2 additions & 0 deletions src/api/bkuser_core/audit/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class LogInFailReason(AutoLowerEnum):
LOCKED_USER = auto()
DISABLED_USER = auto()
EXPIRED_USER = auto()
RESIGNED_USER = auto()
SHOULD_CHANGE_INITIAL_PASSWORD = auto()

_choices_labels = (
Expand All @@ -31,6 +32,7 @@ class LogInFailReason(AutoLowerEnum):
(LOCKED_USER, "用户已锁定"),
(DISABLED_USER, "用户已删除"),
(EXPIRED_USER, "用户账号已过期"),
(RESIGNED_USER, "用户已离职"),
(SHOULD_CHANGE_INITIAL_PASSWORD, "需要修改初始密码"),
)

Expand Down
1 change: 1 addition & 0 deletions src/api/bkuser_core/common/error_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ def __getattr__(self, code_name):
ErrorCode("USER_IS_DELETED", _("账号已被删除,请联系管理员"), 3210022),
ErrorCode("CATEGORY_PLUGIN_LOAD_FAIL", _("目录登录插件加载失败"), 3210023),
ErrorCode("USER_IS_EXPIRED", _("该用户账号已过期"), 3210024),
ErrorCode("USER_IS_RESIGNED", _("该用户账号已离职"), 3210025),
# 用户相关
ErrorCode("PASSWORD_DUPLICATED", _("新密码不能与最近{max_password_history}次密码相同")),
ErrorCode("EMAIL_NOT_PROVIDED", _("该用户没有提供邮箱,发送邮件失败")),
Expand Down
4 changes: 4 additions & 0 deletions src/api/bkuser_core/profiles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ def last_login_time(self) -> Optional[datetime.datetime]:

return None

@property
def is_normal(self) -> bool:
return self.enabled and self.status == ProfileStatus.NORMAL.value

def enable(self):
self.enabled = True
self.status = ProfileStatus.NORMAL.value
Expand Down
39 changes: 39 additions & 0 deletions src/api/bkuser_core/tests/profiles/test_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- 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.
"""

import pytest

from bkuser_core.profiles.constants import ProfileStatus
from bkuser_core.tests.utils import make_simple_profile

pytestmark = pytest.mark.django_db


class TestProfile:
def test_is_normal(
self,
):
profile = make_simple_profile("faker")
assert profile.is_normal

profile.enabled = False
assert not profile.is_normal

# 删除
profile.enable()
profile.delete()
assert not profile.is_normal

abnormal_status = [ProfileStatus.LOCKED.value, ProfileStatus.DISABLED.value, ProfileStatus.EXPIRED.value]
for status in abnormal_status:
profile.enable()
profile.status = status
assert not profile.is_normal
1 change: 1 addition & 0 deletions src/login/bklogin/bkauth/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ def set_bk_token_invalid(request, response=None):
"""
bk_token = request.COOKIES.get(settings.BK_COOKIE_NAME, None)
if bk_token:
bk_token = urllib.parse.unquote(bk_token)
BkToken.objects.filter(token=bk_token).update(is_logout=True)
if response is not None:
# delete cookie
Expand Down
Loading