Coder Social home page Coder Social logo

tencentblueking / bk-user Goto Github PK

View Code? Open in Web Editor NEW
57.0 9.0 57.0 13.7 MB

蓝鲸用户管理是蓝鲸智云提供的企业组织架构和用户管理解决方案,为企业统一登录提供认证源服务。

License: MIT License

Makefile 0.20% Dockerfile 0.13% Python 66.05% Shell 0.06% JavaScript 2.83% HTML 2.87% Vue 21.02% CSS 0.54% Procfile 0.01% TypeScript 4.47% Less 1.82%
blueking python vue django django-rest-framework ldap active-directory organization

bk-user's Introduction

蓝鲸用户管理


license Release Version PRs Welcome

简体中文 | English

蓝鲸用户管理是蓝鲸智云提供的企业组织架构和用户管理解决方案,为企业统一登录提供认证源服务。

总览

功能

  • 支持多层级的组织架构管理
  • 支持通过多种方式同步数据:OpenLDAP、Microsoft Active Directory(MAD)、Excel 表格等
  • 支持用户密码周期管理、密码强度校验、用户登录试错限制、邮件发送随机密码等安全措施

详细介绍请参考功能说明

快速开始

路线图

支持

蓝鲸社区

  • BK-CI:蓝鲸持续集成平台是一个开源的持续集成和持续交付系统,可以轻松将你的研发流程呈现到你面前。
  • BK-BCS:蓝鲸容器管理平台是以容器技术为基础,为微服务业务提供编排管理的基础服务平台。
  • BK-CMDB:蓝鲸配置平台(蓝鲸CMDB)是一个面向资产及应用的企业级配置管理平台。
  • BK-PaaS:蓝鲸PaaS平台是一个开放式的开发平台,让开发者可以方便快捷地创建、开发、部署和管理SaaS应用。
  • BK-SOPS:标准运维(SOPS)是通过可视化的图形界面进行任务流程编排和执行的系统,是蓝鲸体系中一款轻量级的调度编排类SaaS产品。

贡献

对于项目感兴趣,想一起贡献并完善项目请参阅 Contributing Guide

腾讯开源激励计划 鼓励开发者的参与和贡献,期待你的加入。

协议

基于 MIT 协议, 详细请参考LICENSE

bk-user's People

Contributors

bkpaas3 avatar brookylin avatar canway-shiisa avatar caohua avatar houming818 avatar imblues avatar iuzreo avatar nannan00 avatar narasux avatar neronkl avatar shabbywu avatar uddmorningsun avatar wklken avatar yuri0528 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bk-user's Issues

审计支持失败记录

当前所有的审计都是在成功之后再记录,失败则“来去无痕”

应当为审计日志增加一个 status 字段标记操作是否成功,并能够记录失败的(已知)异常信息

用户密码过期后处理方案

当用户的密码过期后,目前只能管理员重置用户密码

应该在提示密码过期后面,提供「修改密码」入口

修正 SettingMeta 默认路径参数为 id

正如 #44 中描述,由于 key 并不是全局唯一的,不适合作为路径参数,需要修正为 id

class SettingMetaViewSet(AdvancedModelViewSet, AdvancedListAPIView):
    """配置信息"""

    queryset = SettingMeta.objects.all()
    serializer_class = serializers.SettingMetaSerializer
    lookup_field = "id"

AD目录插件支持同步多个OU

功能需求背景,例如你遇到了什么问题

为什么会有这个点子?或者遇到了什么问题驱使你需要这个功能?

描述你觉得更好的解决方案

可以举例子来描述你觉得怎样做会更好

额外信息

任何你觉得有助于理解的信息

支持敏感信息设置

因为某些字段(例如手机号、微信号)是非常敏感的,不便于直接在 SaaS 上展示,考虑在【目录设置】中增加【敏感信息设置】

  • 用户可以设置某些字段作为敏感信息,默认产品上是脱敏的,如果需要查看,要做二次验证 #34
  • 保持兼容,暂时对接口拉取无限制

AD目录插件支持同步自定义字段

功能需求背景,例如你遇到了什么问题

为什么会有这个点子?或者遇到了什么问题驱使你需要这个功能?

描述你觉得更好的解决方案

可以举例子来描述你觉得怎样做会更好

额外信息

任何你觉得有助于理解的信息

直接以 {category-id}-{dn} 来作为 LDAP/MAD 的 code 标识

https://github.com/TencentBlueKing/bk-user/blob/master/src/api/bkuser_core/categories/plugins/ldap/syncer.py#L103

def _get_code(self, raw_obj: dict) -> str:
        """如果不存在 uuid 则用 dn(sha) 作为唯一标示"""
        entry_uuid = raw_obj.get("raw_attributes", {}).get("entryUUID", [])
        if isinstance(entry_uuid, list) and entry_uuid:
            logger.debug("uuid in raw_attributes: return %s", entry_uuid[0])
            return entry_uuid[0]
        else:
            # 由于其他目录也可能会出现这样的 code,所以添加 category_id 进行转换
            dn = f"{self.category_id}-{raw_obj.get('dn')}"

            sha = hashlib.sha256(force_bytes(dn)).hexdigest()
            logger.debug("no uuid in raw_attributes, use dn instead: %s -> %s", dn, sha)
            return sha

当前我们主要是以 entryUUID 优先作为 code 字段,但是由于很多情况目标服务返回的 raw_attributes 中没有该字段,而通过 sha256 保存的 code 基本不可读,所以考虑是否直接使用 {category-id}-{dn} 这样的拼接来作为 code

需要考虑的问题:

  • dn 的长度可能会超长,需要做一个超长控制,如果直接超长截取需要考虑 code 重复问题(添加随机?)
  • 现有数据是否会有兼容性问题

将数据源插件和目录类型解耦

当前我们可以创建不同的目录,不同的目录对应不同的数据集合,数据的命名空间(尤其是 username)是相互独立的,同时目录类型本身和同步方式是一一绑定的,这样会带来一些使用上的麻烦,所以我们希望能够针对二者做解耦

现状

image

存在几个内建的 category.type 直接对应同步方式(插件)

  • local
  • ldap
  • mad
  • custom

同时,为了兼容自定义插件的场景,临时增加了一种特殊的类型pluggable 作为过渡,当目录类型是 pluggable 时会尝试从 settings 中获取对应的插件类型。

def get_plugin_by_category(category: "ProfileCategory") -> "DataSourcePlugin":
    """通过 category 类型获取插件名"""
    if category.type == CategoryType.PLUGGABLE.value:
        plugin_name = ConfigProvider(category.id)[PLUGIN_NAME_SETTING_KEY]
        return get_plugin_by_name(plugin_name)

    for n, p in _global_plugins.items():
        if p.category_type == category.type:
            return p
    raise ValueError(f"Plugin with category type: {category.type} does not exist")

同时也会带来一些问题:

  • 同一种类型的目录只能同步一种数据类型。当系统已经在公司内铺开使用后,各个体系产品都已经存储了默认目录的用户名,如果要修改目录的默认属性,会带来巨量的数据一致性挑战。
  • 当我们做多租户改造时,目录将转换成租户组的概念,隔离属性将被复用,但是数据源绑定属性则不应该放在租户组中。

改造

所以我们需要将二者完全解耦。达到的效果就是:

  • 同一个目录(租户组)可以添加多种数据源同步插件,可以同步多种数据源
  • 数据源插件之间的数据不再具有隔离性,将共享命名空间
  • 每一个数据本身将添加一个“数据源标记”,例如,用户在登录时,将通过这个标记找寻到对应的插件登录逻辑,进而统一蓝鲸登录插件和用户管理数据源插件

image

数据迁移

为了保证整体的平滑,我们将已有数据转换概念,一次性升级

image

整合蓝鲸登录

目前蓝鲸登录是放在 蓝鲸 PaaS 平台 中维护的,而实际上在体系中,登录和用户管理的关系更加紧密一些,所以将登录的代码整合在用户管理看起来是更合理的方向。

整合之后,可以更方便的支持一些登录相关功能:

  • 首次登录强制修改密码
  • 统一插件方案:数据同步源插件 + 自定义登录插件
  • 两步验证

将散落多个路径中的 .gitignore 合并到顶层 .gitignore

  • src/api/bkuser_core/config/overlays/.gitignore
  • src/api/.gitignore
  • src/saas/bkuser_shell/config/overlays/.gitignore
  • src/saas/.gitignore
  • src/pages/.gitignore
  • deploy/helm/.gitignore

(src/sdk 中还有一个,不过由于都是 swagger-codegen 生成的,所以忽略问题不大)

更方便转换成正确的 .dockerignore

发布支持【二进制/Smart】格式包

用文字描述你遇到的问题

请用简练的文字描述你遇到的问题,问题描述的清晰程度决定了问题被解决的效率。

重现方法

请描述问题重现的方法,如果不方便描述,可以通过截图或者视频辅助。

预期行为

预期的正常行为

版本

  • 提供用户管理的具体版本号
  • 是否是企业版问题?

如果是 SaaS 页面问题,请提供使用的操作系统和浏览器信息

  • OS: [e.g. iOS]
  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

额外信息

任何你觉得有助于问题解决的内容

用户首次登录强制修改密码

背景: 企业版3.0部署后,admin的密码是后台生成的随机密码,产品上需要增加用户首次登录蓝鲸强制修改密码的开关(默认开启),这样不管是admin还是其他用户,第一次登录都会强制修改密码。

需要将登陆代码挪到用户管理维护(#5)后才能进一步开展设计。

优化数据源插件的配置方式

当前插件的数据配置需要插件开发者手动配置 Settings,也没有页面交互,非常不友好,给插件开发带来了较高的门槛

注册时声明配置

目前的注册

DataSourcePlugin(
    name="ldap",
    syncer_cls=LDAPSyncer,
    login_handler_cls=LoginHandler,
    allow_client_write=False,
    category_type="ldap",
    extra_config={"default_sync_period": 60, "min_sync_period": 60, "ldap_max_paged_size": 1000},
).register()

开发者可以在这里通过 extra_config 注册一些同步配置,但是只有简单的 key-value 模式,相当于只能在填入开发时的默认值,无法在插件使用时配置。

利用 Python Type Annotation 声明配置

@dataclass
class PluginConfig:
    """使用者需要关注的配置"""
    # 字符枚举类型,无默认值
    basic_pull_node: Literal["foo", "bar"]
    # 具备默认值
    default_sync_period: int = 60
    user_filter_class: str = "(objectClass=user)"

    @dataclass
    class Private:
        """仅在同步内部使用的配置"""
        # 开发内部使用,不会暴露到页面配置
        min_sync_period: int = 60

用户在注册时需要传入配置类

DataSourcePlugin(
    name="ldap",
    syncer_cls=LDAPSyncer,
    login_handler_cls=LoginHandler,
    allow_client_write=False,
    category_type="ldap",
    extra_config_cls=PluginConfig,
).register()

然后,Api 需要在注册时,将 PluginConfig 描述的配置创建成 SettingsMeta

同时需要提供一个配置接口,将 dataclass 转换成配置描述:

{
  "basic_pull_node": {
    "type": "enum",
    "required": "true",
    "default": null,
    "choices": ["foo", "bar"]
  },
  "default_sync_period": {
    "type": "int",
    "required": "false",
    "default": 60,
    "choices": null
  },
  "user_filter_class": {
    "type": "str",
    "required": "true",
    "default": "(objectClass=user)",
    "choices": null
  }
}

在 SaaS 侧前端需要解析这样的 JSON 描述,然后渲染成配置页面。

前端在用户设置选择上级用户报错

API 地址

api/v2/categories/%s/profiles/$

应用

saas

报错信息

KeyError: "Got KeyError when attempting to get a value for field emailon serializerProfileSerializer.\nThe serializer field might be named incorrectly and not match any attribute or key on the dictinstance.\nOriginal exception text was: 'email'."

建议解决

saas ---> bk_shell ---> organization ---> views ---> profiles.py

原始代码

    def list(self, request, category_id, validated_data):
        lookup_param = {
            "page": validated_data["page"],
            "page_size": validated_data["page_size"],
            "lookup_field": "category_id",
            "exact_lookups": [
                category_id,
            ],
        }
        # 全量用户返回体过大,只选择某些字段展示
        if validated_data["no_page"]:
            lookup_param.update({"fields": ["id", "username", "display_name"]})

修改后的代码

    def list(self, request, category_id, validated_data):
        lookup_param = {
            "page": validated_data["page"],
            "page_size": validated_data["page_size"],
            "lookup_field": "category_id",
            "exact_lookups": [
                category_id,
            ],
        }
        # 全量用户返回体过大,只选择某些字段展示
        if validated_data["no_page"]:
            lookup_param.update({"fields": ["id", "username", "display_name", "email", "iso_code", "departments"]})

改进部署 Helm Chart,降低部署门槛

  • 默认禁用权限中心配置,并添加手动注册权限中心的文档指引
  • ingress 资源修改成 networking/v1 版本
  • 修复当 mariadb 禁用时,envFrom 挂载失败问题
  • 增加 redis 内建存储,作为 Celery Broker
  • 修正若干文档 & 注释指引
  • 增加初始化管理员账号密码修改指引
  • 支持全局配置镜像 registry
  • service 名字添加前缀,和其他 Chart 区分

邮箱、手机号支持唯一性校验

需要考虑存量数据可能存在重复问题,添加脚本检测更新?

可能涉及的改造内容:

  • 用户登录时支持更直接的手机号、邮箱登录(分 tab?)
  • 可以通过邮箱或者手机号重置密码

前提是完成登陆代码整合维护 (#5 )

批量获取用户接口/api/v2/batch/profiles/,swagger显示的是没有参数,但实际代码中校验参数query_ids

访问批量获取用户接口/api/v2/batch/profiles/,返回错误:
{"result":false,"data":null,"code":-1,"message":"request failed, please check api log of bk-user"}

源码:
bkuser_core-->common-->viewset.py的mutliple_retrieve获取query_ids

建议修改文件 bkuser_core/profiles/views.py:

@swagger_auto_schema(
manual_parameters=[],
responses={"200": local_serializers.ProfileSerializer()},
tags=["profiles"],
)

bkuser_core/profiles/views.py文件增加导入 openapi

from drf_yasg import openapi

swagger部分修改:

@swagger_auto_schema(
    manual_parameters=[openapi.Parameter("query_ids", openapi.IN_QUERY,
                                         description="input departments query_ids,逗号分割",
                                         type=openapi.TYPE_STRING)],
    responses={"200": local_serializers.ProfileSerializer(many=True)},
)

API 支持通过 POST body 筛选数据

当前我们提供了列表拉取 API,形如

api/v2/profiles?exact_lookups=foo,bar,baz

可以用来筛选特定用户名列表的用户,但是有些场景中这个用户名列表会非常巨大,使用 GET 方法会导致 URL 超长。
为此我们提供了一个特定 HEADER 支持用户使用 POST 达到 GET 效果

但是如果请求 body 本身已经非常大了,会触发 gunicorn 的限制

所以最好能够在用户使用覆盖 HEADER 时,能够告知处理端从 POST Body 中读取参数,而不是 query_params

支持二次登录认证

支持用户通过短信动态码等方式二次确认登陆

需要将登陆代码挪到用户管理维护(#5)后才能进一步开展设计。

增加版本升级[不兼容]需知文档

从原来的 2.2.x -> 2.3.x 有一些不兼容的修改:

  • 移除了原来的 v1 查询接口
  • 去除了 no_page 查询参数

提供修改原因,和如何迁移到新方案的指引。

请求接口/api/v2/setting_metas/base_dn/,返回request failed, please check api log of bk-user

请求接口/api/v2/setting_metas/base_dn/,返回错误request failed, please check api log of bk-user

检查日志报错大致如下:
bkuser_core.user_settings.models.SettingMeta.MultipleObjectsReturned: get() returned more than one SettingMeta -- it returned 2!

而表结构的主键是:unique_together = ("key", "namespace", "category_type")

而lookup_field的值是key:lookup_field = "key"

因为调用的是get_object(),这个最终调用的是django--->shortcuts.py的

return queryset.get(*args, **kwargs)

建议修改如下:
文件:bkuser_core/user_settings/views.py
原内容:

class SettingMetaViewSet(AdvancedModelViewSet, AdvancedListAPIView):
    """配置信息"""

    queryset = SettingMeta.objects.all()
    serializer_class = serializers.SettingMetaSerializer
    lookup_field = "key"

修改内容:

class SettingMetaViewSet(AdvancedModelViewSet, AdvancedListAPIView):
    """配置信息"""

    queryset = SettingMeta.objects.all()
    serializer_class = serializers.SettingMetaSerializer
    lookup_field = "id"

未关联部门的用户应该有地方展示

当前用户如果没有关联到具体的部门,在 SaaS 的组织架构上是无法展示的(可以搜索到),应该有一个专门的地方用来展示这些孤立的用户,并支持管理员手动给他们关联到具体部门。

数值型自定义字段在页面上输入时没有异常提示

复现方法:

  • 添加一个数值型自定义字段
  • 在组织架构页面编辑用户,在对应字段的编辑框输入

image

当输入不为数值时(例如任意字母),页面和输入框没有任何提示,输入数值时能正常显示

输入 e 时反而能正常显示

image

文字按钮的风格保持统一

image

image

二者的字号、颜色应该保持统一

(个人认为 “数据更新记录” 的色号更合适,而 “审计导出” 的字号更合适)

增加组织架构数据正确性检测脚本

在企业中部署时,常常会手动修改数据库情况,由于代码本身无法感知,会引发一些程序异常。

例如:

  • departmentenabled 字段手动修改成 0,但仍被 enabled=1 的子部门关联,此时权限中心同步接口将出现无法建树的情况

可以提供一个扫描脚本,对于有关联关系的数据模型,判断其关联是否有效,输出一个报告,辅助实施侧运维判断。

管理员可以在类似回收站的地方,恢复已(软)删除数据

现状

当前主要的资源数据: 用户 目录 部门 都是通过标记的方式(enabled=0)进行软删除,不同的是,创建时同步冲突的出路策略并不相同:

  • 前两者(用户 目录)创建遇到同 key 的已删除资源时,程序直接抛出错误
  • 部门 创建遇到同 key 的已删除资源时,则会恢复当前已删除的资源

这样的策略会带来一些使用上的困扰:当用户删除了某个用户名的用户,是无法通过产品重新添加同 username 的用户,需要手动在 DB 中删除数据才能重新添加。

为什么不直接复用?

因为 用户目录 资源是“权限敏感”的,它们常常会被关联到具体的权限项。

在后续的计划中,我们会在同一个目录支持不同的数据源,那么很可能存在一个情况,不同的自然人拥有相同的系统 username,这时候直接恢复已删除的用户,就可能出现权限转移的风险。

手动恢复

相较于程序后台静默地直接复用,提供一个产品功能,显式地告之操作者数据恢复的风险——恢复数据同时权限也将恢复,会更加明智。

API 支持传递参数,可以拉取已软删除数据

当前所有的 API 请求,都会默认过滤掉 enabled=false 的数据,可能存在一些场景,用户想拉取被删除的数据,比如用来做审计。

一个想法:

显示声明返回结果需要包含软删除的数据

/api/v2/profiles?include_disabled=1&fields=username,id

返回数据:

"results": [
      {
        "id": 1,
        "username": "admin",
        # 即使在 fields 参数中没有 enabled, 但是由于 include_deleted 的存在,会默认添加该字段返回
        "enabled": true,
      },
      # 已软删除的数据
      {
        "id": 2,
        "username": "zhangsan",
        # 软删除判断标志
        "enabled": false,
      }
]     

支持记录 LDAP/AD 同步组织架构/人员信息的结构化日志

功能需求背景,例如你遇到了什么问题

当前用户管理的目录同步是一个很缓慢的操作, 而且一旦用户刷新页面, 就会让同步完成与否充满不确定性,这样的用户体验很不友好。
因此希望能在产品(前端)上能看到 LADP/AD 同步组织架构和人员信息的过程和报错信息,而不是只能看机器的标准输出。

描述你觉得更好的解决方案

当前在同步 LDAP/AD 的组织架构和人员信息时, 未完全理清 DU 的含义, 导致代码像意大利面一样, 可读性较差,而目前的 Custom Plugin 类型的用户目录已经支持记录同步过程,如果能先重构 LADP/AD 的同步过程,使得流程更清晰,那么就可以更方便地记录同步过程了。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.