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(bk-login): support local login #1302

Merged
merged 26 commits into from
Nov 2, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0d9c37e
feat(bk-login): support local login
nannan00 Oct 16, 2023
7992dec
ci: github action for idp plugin
nannan00 Oct 16, 2023
b78875a
fix: ignore idp plugin symlinks file
nannan00 Oct 16, 2023
844c6d1
ci: git ignore idp plugin links
nannan00 Oct 16, 2023
30b54f9
ci: github action for idp plugins
nannan00 Oct 16, 2023
ab88e15
fix: load idp plugin in diff project
nannan00 Oct 16, 2023
7d9ea96
Merge remote-tracking branch 'blueking/ft_tenant' into ft_bk_login
nannan00 Oct 18, 2023
eab26b6
fix(bklogin): fix csrftoken verify
nannan00 Oct 23, 2023
31a116d
Merge remote-tracking branch 'blueking/ft_tenant' into ft_bk_login
nannan00 Oct 23, 2023
b1155ce
fix(docker): fix dockerfile about copy idp_plugin dir
nannan00 Oct 23, 2023
2e9abb7
Merge remote-tracking branch 'blueking/ft_tenant' into ft_bk_login
nannan00 Oct 23, 2023
5bdc383
refactor(bklogin): modify url
nannan00 Oct 24, 2023
63f6fe4
feat(bklogin): add wecom idp plugin
nannan00 Oct 24, 2023
4f7ba44
refactor(bklogin): bk_token\wecom
nannan00 Oct 25, 2023
cfd68db
fix(bklogin): modify plugin register
nannan00 Oct 25, 2023
6cb5fd8
fix: bkuser test
nannan00 Oct 25, 2023
aec8bed
fix: mypy
nannan00 Oct 26, 2023
f70043a
fix: mypy
nannan00 Oct 26, 2023
dfd9282
refactor(bklogin): plugin exception
nannan00 Oct 26, 2023
a49eb9b
fix: cr
nannan00 Oct 26, 2023
3140600
fix: unittest
nannan00 Oct 26, 2023
8971c74
feat(bklogin): adjust error message tips
nannan00 Oct 27, 2023
8a02faf
Merge remote-tracking branch 'blueking/ft_tenant' into ft_bk_login
nannan00 Oct 27, 2023
b20f789
fix: cr
nannan00 Oct 28, 2023
41a84dd
fix: cr
nannan00 Nov 2, 2023
cd5b9d4
fix: rebase merge
nannan00 Nov 2, 2023
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
8 changes: 8 additions & 0 deletions .github/workflows/bk-user.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
uses: actions/setup-python@v2
with:
python-version: "3.10"
- name: create idp plugin symbolic link
run: |
ln -s $(pwd)/src/idp-plugins/idp_plugins $(pwd)/src/bk-login/bklogin
ln -s $(pwd)/src/idp-plugins/idp_plugins $(pwd)/src/bk-user/bkuser
- name: Format with black
run: |
pip install black==23.7.0 click==8.1.6
Expand Down Expand Up @@ -52,6 +56,10 @@ jobs:
uses: actions/setup-python@v4
with:
python-version: "3.10"
- name: create idp plugin symbolic link
run: |
ln -s $(pwd)/src/idp-plugins/idp_plugins $(pwd)/src/bk-login/bklogin
ln -s $(pwd)/src/idp-plugins/idp_plugins $(pwd)/src/bk-user/bkuser
- name: Set up Poetry
uses: abatilo/actions-poetry@v2.3.0
with:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,7 @@ pre_commit_hooks
cliff.toml
.codecc
.idea

# igonre symlinks
src/bk-user/bkuser/idp_plugins
src/bk-login/bklogin/idp_plugins
50 changes: 48 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,41 @@ repos:
- id: debug-statements
- id: detect-private-key
- id: end-of-file-fixer
exclude: ^(src/pages/|src/bk-login/pages)
- id: trailing-whitespace
exclude: ^(src/pages/|src/bk-login/pages)
- repo: local
hooks:
- id: Name check
name: Check sensitive info of name
verbose: true
language: system
entry: bash -c "if [[ -d pre_commit_hooks ]]; then pre_commit_hooks/rtx.sh $@; fi"
exclude: ^(.*/\.env|.*/poetry\.lock|.*/pyproject\.toml|src/bk-user/logs|src/pages/node_modules/)
exclude: >
(?x)^(
.*/\.env|
.*/poetry\.lock|
.*/pyproject\.toml|
src/bk-user/logs|
src/pages/node_modules/
)$
- id: IP check
name: Check sensitive info of ip
verbose: true
language: system
exclude: ^(.*/\.env|.*/poetry\.lock|.*/pyproject\.toml|\.github/workflows/bk-user.yml|src/bk-user/logs|src/pages/node_modules/)
exclude: >
(?x)^(
.*/\.env|
.*/poetry\.lock|
.*/pyproject\.toml|
\.github/workflows/bk-user.yml|
src/bk-user/logs|src/pages/node_modules/|
src/bk-login/pages/src/views/components/protocol.vue|
src/pages/static/bk_icon_font/iconcool.json|
src/pages/static/bk_icon_font/iconcool.js|
src/pages/paas-server/index.js|
src/bk-login/pages/paas-server/index.js
)$
entry: bash -c "if [[ -d pre_commit_hooks ]]; then pre_commit_hooks/ip.sh $@; fi"
- repo: local
hooks:
Expand All @@ -51,3 +72,28 @@ repos:
require_serial: true
language: system
entry: bash -c "cd src/bk-user && lint-imports"
- repo: local
hooks:
- id: black
name: black
language: python
types: [python]
entry: black --config=src/bk-login/pyproject.toml
files: src/bk-login/
- id: ruff
name: ruff
language: python
types: [python]
entry: ruff --config=src/bk-login/pyproject.toml --force-exclude --fix
files: src/bk-login/
- id: mypy
name: mypy
language: python
types: [python]
entry: mypy --config-file=src/bk-login/pyproject.toml
files: src/bk-login/
- id: import-linter
name: import-linter
require_serial: true
language: system
entry: bash -c "cd src/bk-login && lint-imports"
39 changes: 39 additions & 0 deletions src/bk-login/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
FROM node:18.17.1-bullseye-slim AS StaticBuilding
ENV NPM_VERSION 9.6.7

COPY src/bk-login/pages /
WORKDIR /
RUN npm install
RUN npm run build

FROM python:3.10.12-slim-bullseye
USER root

RUN rm /etc/apt/sources.list && \
echo "deb https://mirrors.tencent.com/debian bullseye main" >> /etc/apt/sources.list && \
echo "deb https://mirrors.tencent.com/debian-security bullseye-security main" >> /etc/apt/sources.list && \
echo "deb https://mirrors.tencent.com/debian bullseye-updates main" >> /etc/apt/sources.list

RUN mkdir ~/.pip && printf '[global]\nindex-url = https://mirrors.tencent.com/pypi/simple/' > ~/.pip/pip.conf

RUN apt-get update && apt-get install -y default-libmysqlclient-dev build-essential pkg-config

ENV LC_ALL=C.UTF-8 \
LANG=C.UTF-8

RUN pip install --upgrade pip setuptools
RUN pip install poetry==1.5.1

WORKDIR /app
COPY src/bk-login/pyproject.toml /app
COPY src/bk-login/poetry.lock /app
RUN poetry config virtualenvs.create false && poetry install --only main
nannan00 marked this conversation as resolved.
Show resolved Hide resolved

COPY src/bk-login/bklogin /app/bklogin
COPY src/bk-login/bin /app/bin
COPY src/bk-login/manage.py /app

COPY --from=StaticBuilding /dist /app/staticfiles
COPY --from=StaticBuilding /dist/index.html /app/templates/index.html

CMD ["bash", "/app/bin/start.sh"]
4 changes: 4 additions & 0 deletions src/bk-login/bin/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash

LISTEN_PORT="${PORT:=8000}"
gunicorn bklogin.wsgi -w 8 --threads 2 --max-requests 1024 --max-requests-jitter 50 --worker-class gevent -b [::]:$LISTEN_PORT --access-logfile - --error-logfile - --access-logformat '[%(h)s] %({request_id}i)s %(u)s %(t)s "%(r)s" %(s)s %(D)s %(b)s "%(f)s" "%(a)s"'
10 changes: 10 additions & 0 deletions src/bk-login/bklogin/authentication/__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.
"""
16 changes: 16 additions & 0 deletions src/bk-login/bklogin/authentication/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
## -*- 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.apps import AppConfig


class AuthenticationConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "bklogin.authentication"
15 changes: 15 additions & 0 deletions src/bk-login/bklogin/authentication/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# -*- 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.
"""
REDIRECT_FIELD_NAME = "c_url"

SIGN_IN_TENANT_ID_SESSION_KEY = "sign_in_tenant_id"

SUPPORT_SIGN_IN_TENANT_USER_IDS_SESSION_KEY = "support_sign_in_tenant_user_ids"
159 changes: 159 additions & 0 deletions src/bk-login/bklogin/authentication/helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
# -*- 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 datetime
import logging
import random
import string
import time
from typing import Tuple
from urllib.parse import unquote

from blue_krill.encrypt.handler import EncryptHandler
from django.conf import settings
from django.utils import timezone
from django.utils.encoding import force_bytes
from django.utils.translation import gettext_lazy as _

from .models import BkToken

logger = logging.getLogger(__name__)


class BkTokenProcessor:
def __init__(self, encrypt_secret_key: bytes):
# Token加密密钥
self.encrypt_secret_key = encrypt_secret_key

@staticmethod
def _salt(length: int = 8) -> str:
"""生成长度为length 的随机字符串"""
allow_chars = string.ascii_letters + string.digits
return "".join([random.choice(allow_chars) for __ in range(length)])

def generate(self, username: str, expires: int) -> str:
"""token生成"""
# 加盐
plain_token = "%s|%s|%s" % (expires, username, self._salt())

# 加密
return EncryptHandler(secret_key=self.encrypt_secret_key).encrypt(plain_token)

def parse(self, bk_token: str) -> Tuple[str, int]:
"""token解析"""
try:
plain_bk_token = EncryptHandler(secret_key=self.encrypt_secret_key).decrypt(bk_token)
except Exception:
logger.exception("参数[%s] 解析失败", bk_token)
plain_bk_token = ""

error_msg = _("参数 bk_token 非法")
if not plain_bk_token:
raise ValueError(error_msg)

try:
token_info = plain_bk_token.split("|")
except Exception:
logger.exception("分割 bk_token[%s] 失败", bk_token)
raise ValueError(error_msg)

if not token_info or len(token_info) < 3: # noqa: PLR2004
raise ValueError(error_msg)

return token_info[1], int(token_info[0])


class BkTokenManager:
def __init__(self):
# Token加密密钥
self.bk_token_processor = BkTokenProcessor(encrypt_secret_key=force_bytes(settings.ENCRYPT_SECRET_KEY))
# Token 过期间隔
self.cookie_age = settings.BK_TOKEN_COOKIE_AGE
# Token 无操作失效间隔
self.inactive_age = settings.BK_TOKEN_INACTIVE_AGE
# Token 校验时间允许误差
self.offset_error_age = settings.BK_TOKEN_OFFSET_ERROR_AGE

# Token生成失败的重试次数
self.allowed_retry_count = 5

def get_bk_token(self, username: str) -> Tuple[str, datetime.datetime]:
"""
生成用户的登录态
"""
bk_token = ""
expire_time = int(time.time())
# 重试5次
retry_count = 0
while not bk_token and retry_count < self.allowed_retry_count:
now_time = int(time.time())
# Token过期时间
expire_time = now_time + self.cookie_age
# Token 无操作失效时间
inactive_expire_time = now_time + self.inactive_age
# 生成bk_token
bk_token = self.bk_token_processor.generate(username, expire_time)
# DB记录
try:
BkToken.objects.create(token=bk_token, inactive_expire_time=inactive_expire_time)
except Exception: # noqa: PERF203
logger.exception("Login ticket failed to be saved during ticket generation")
# 循环结束前将bk_token置空后重新生成
bk_token = "" if retry_count + 1 < self.allowed_retry_count else bk_token
retry_count += 1

return bk_token, datetime.datetime.fromtimestamp(expire_time, timezone.get_current_timezone())

def is_bk_token_valid(self, bk_token: str) -> Tuple[bool, str, str]:
"""
验证用户登录态
"""
if not bk_token:
return False, "", _("参数 bk_token 缺失")

bk_token = unquote(bk_token)
# 解析bk_token获取username和过期时间
try:
username, expire_time = self.bk_token_processor.parse(bk_token)
except ValueError as error:
return False, "", str(error)

# 检查DB是存在
try:
bk_token_obj = BkToken.objects.get(token=bk_token)
is_logout = bk_token_obj.is_logout
inactive_expire_time = bk_token_obj.inactive_expire_time
except Exception:
return False, "", _("不存在 bk_token[%s] 的记录").format(bk_token)

# token已注销
if is_logout:
return False, "", _("登录态已注销")

now_time = int(time.time())
# token有效期已过
if now_time > expire_time + self.offset_error_age:
return False, "", _("登录态已过期")

# token有效期大于当前时间的有效期
if expire_time - now_time > self.cookie_age + self.offset_error_age:
return False, "", _("登录态有效期不合法")

# token 无操作有效期已过
if now_time > inactive_expire_time + self.inactive_age:
return False, "", _("长时间无操作,登录态已过期")

# 更新 无操作有效期
try:
BkToken.objects.filter(token=bk_token).update(inactive_expire_time=now_time + self.inactive_age)
except Exception:
logger.exception("update inactive_expire_time fail")

return True, username, ""
23 changes: 23 additions & 0 deletions src/bk-login/bklogin/authentication/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.21 on 2023-09-27 02:34

from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='BkToken',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('token', models.CharField(db_index=True, max_length=255, unique=True, verbose_name='登录票据')),
('is_logout', models.BooleanField(default=False, verbose_name='票据是否已经执行过退出登录操作')),
('inactive_expire_time', models.IntegerField(default=0, verbose_name='无操作失效时间戳')),
],
),
]
Empty file.
23 changes: 23 additions & 0 deletions src/bk-login/bklogin/authentication/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- 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.db import models


class BkToken(models.Model):
"""
登录票据
"""

token = models.CharField("登录票据", max_length=255, unique=True, db_index=True)
# 是否已经退出登录
is_logout = models.BooleanField("票据是否已经执行过退出登录操作", default=False)
# 无操作过期时间戳
inactive_expire_time = models.IntegerField("无操作失效时间戳", default=0)
nannan00 marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading