Files
jumpserver/apps/settings/api/ldap.py
fit2bot e259d2a9e9 fix: fix rbac to dev (#7636)
* feat: 添加 RBAC 应用模块

* feat: 添加 RBAC Model、API

* feat: 添加 RBAC Model、API 2

* feat: 添加 RBAC Model、API 3

* feat: 添加 RBAC Model、API 4

* feat: RBAC

* feat: RBAC

* feat: RBAC

* feat: RBAC

* feat: RBAC

* feat: RBAC 整理权限位

* feat: RBAC 整理权限位2

* feat: RBAC 整理权限位2

* feat: RBAC 整理权限位

* feat: RBAC 添加默认角色

* feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定

* feat: RBAC 添加迁移文件;迁移用户角色->用户角色绑定

* feat: RBAC 修改用户模块API

* feat: RBAC 添加组织模块迁移文件 & 修改组织模块API

* feat: RBAC 添加组织模块迁移文件 & 修改组织模块API

* feat: RBAC 修改用户角色属性的使用

* feat: RBAC No.1

* xxx

* perf: 暂存

* perf: ...

* perf(rbac): 添加 perms 到 profile serializer 中

* stash

* perf: 使用init

* perf: 修改migrations

* perf: rbac

* stash

* stash

* pref: 修改rbac

* stash it

* stash: 先去修复其他bug

* perf: 修改 role 添加 users

* pref: 修改 RBAC Model

* feat: 添加权限的 tree api

* stash: 暂存一下

* stash: 暂存一下

* perf: 修改 model verbose name

* feat: 添加model各种 verbose name

* perf: 生成 migrations

* perf: 优化权限位

* perf: 添加迁移脚本

* feat: 添加组织角色迁移

* perf: 添加迁移脚本

* stash

* perf: 添加migrateion

* perf: 暂存一下

* perf: 修改rbac

* perf: stash it

* fix: 迁移冲突

* fix: 迁移冲突

* perf: 暂存一下

* perf: 修改 rbac 逻辑

* stash: 暂存一下

* perf: 修改内置角色

* perf: 解决 root 组织的问题

* perf: stash it

* perf: 优化 rbac

* perf: 优化 rolebinding 处理

* perf: 完成用户离开组织的问题

* perf: 暂存一下

* perf: 修改翻译

* perf: 去掉了 IsSuperUser

* perf: IsAppUser 去掉完成

* perf: 修改 connection token 的权限

* perf: 去掉导入的问题

* perf: perms define 格式,修改 app 用户 的全新啊

* perf: 修改 permission

* perf: 去掉一些 org admin

* perf: 去掉部分 org admin

* perf: 再去掉点 org admin role

* perf: 再去掉部分 org admin

* perf: user 角色搜索

* perf: 去掉很多 js

* perf: 添加权限位

* perf: 修改权限

* perf: 去掉一个 todo

* merge: with dev

* fix: 修复冲突

Co-authored-by: Bai <bugatti_it@163.com>
Co-authored-by: Michael Bai <baijiangjie@gmail.com>
Co-authored-by: ibuler <ibuler@qq.com>
2022-02-17 20:13:31 +08:00

224 lines
7.6 KiB
Python

# -*- coding: utf-8 -*-
#
import threading
from rest_framework import generics
from rest_framework.views import Response, APIView
from orgs.models import Organization
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from ..utils import (
LDAPServerUtil, LDAPCacheUtil, LDAPImportUtil, LDAPSyncUtil,
LDAP_USE_CACHE_FLAGS, LDAPTestUtil
)
from ..tasks import sync_ldap_user
from common.utils import get_logger, is_uuid
from ..serializers import (
LDAPTestConfigSerializer, LDAPUserSerializer,
LDAPTestLoginSerializer
)
from orgs.utils import current_org
from users.models import User
logger = get_logger(__file__)
class LDAPTestingConfigAPI(APIView):
serializer_class = LDAPTestConfigSerializer
rbac_perms = {
'POST': 'settings.change_setting'
}
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return Response({"error": str(serializer.errors)}, status=400)
config = self.get_ldap_config(serializer)
ok, msg = LDAPTestUtil(config).test_config()
status = 200 if ok else 400
return Response(msg, status=status)
@staticmethod
def get_ldap_config(serializer):
server_uri = serializer.validated_data["AUTH_LDAP_SERVER_URI"]
bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"]
password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"]
use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False)
search_ou = serializer.validated_data["AUTH_LDAP_SEARCH_OU"]
search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"]
attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"]
auth_ldap = serializer.validated_data.get('AUTH_LDAP', False)
if not password:
password = settings.AUTH_LDAP_BIND_PASSWORD
config = {
'server_uri': server_uri,
'bind_dn': bind_dn,
'password': password,
'use_ssl': use_ssl,
'search_ou': search_ou,
'search_filter': search_filter,
'attr_map': attr_map,
'auth_ldap': auth_ldap
}
return config
class LDAPTestingLoginAPI(APIView):
serializer_class = LDAPTestLoginSerializer
rbac_perms = {
'POST': 'settings.change_setting'
}
def post(self, request):
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return Response({"error": str(serializer.errors)}, status=400)
username = serializer.validated_data['username']
password = serializer.validated_data['password']
ok, msg = LDAPTestUtil().test_login(username, password)
status = 200 if ok else 400
return Response(msg, status=status)
class LDAPUserListApi(generics.ListAPIView):
serializer_class = LDAPUserSerializer
rbac_perms = {
'POST': 'settings.change_setting'
}
def get_queryset_from_cache(self):
search_value = self.request.query_params.get('search')
users = LDAPCacheUtil().search(search_value=search_value)
return users
def get_queryset_from_server(self):
search_value = self.request.query_params.get('search')
users = LDAPServerUtil().search(search_value=search_value)
return users
def get_queryset(self):
if hasattr(self, 'swagger_fake_view'):
return []
cache_police = self.request.query_params.get('cache_police', True)
if cache_police in LDAP_USE_CACHE_FLAGS:
users = self.get_queryset_from_cache()
else:
users = self.get_queryset_from_server()
return users
@staticmethod
def processing_queryset(queryset):
db_username_list = User.objects.all().values_list('username', flat=True)
for q in queryset:
q['id'] = q['username']
q['existing'] = q['username'] in db_username_list
return queryset
def sort_queryset(self, queryset):
order_by = self.request.query_params.get('order')
if not order_by:
order_by = 'existing'
if order_by.startswith('-'):
order_by = order_by.lstrip('-')
reverse = True
else:
reverse = False
queryset = sorted(queryset, key=lambda x: x[order_by], reverse=reverse)
return queryset
def filter_queryset(self, queryset):
if queryset is None:
return queryset
queryset = self.processing_queryset(queryset)
queryset = self.sort_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs):
cache_police = self.request.query_params.get('cache_police', True)
# 不是用缓存
if cache_police not in LDAP_USE_CACHE_FLAGS:
return super().list(request, *args, **kwargs)
try:
queryset = self.get_queryset()
except Exception as e:
data = {'error': str(e)}
return Response(data=data, status=400)
# 缓存有数据
if queryset is not None:
return super().list(request, *args, **kwargs)
sync_util = LDAPSyncUtil()
# 还没有同步任务
if sync_util.task_no_start:
# 任务外部设置 task running 状态
sync_util.set_task_status(sync_util.TASK_STATUS_IS_RUNNING)
t = threading.Thread(target=sync_ldap_user)
t.start()
data = {'msg': _('Synchronization start, please wait.')}
return Response(data=data, status=409)
# 同步任务正在执行
if sync_util.task_is_running:
data = {'msg': _('Synchronization is running, please wait.')}
return Response(data=data, status=409)
# 同步任务执行结束
if sync_util.task_is_over:
msg = sync_util.get_task_error_msg()
data = {'error': _('Synchronization error: {}'.format(msg))}
return Response(data=data, status=400)
return super().list(request, *args, **kwargs)
class LDAPUserImportAPI(APIView):
def get_org(self):
org_id = self.request.data.get('org_id')
if is_uuid(org_id):
org = Organization.objects.get(id=org_id)
else:
org = current_org
return org
def get_ldap_users(self):
username_list = self.request.data.get('username_list', [])
cache_police = self.request.query_params.get('cache_police', True)
if cache_police in LDAP_USE_CACHE_FLAGS:
users = LDAPCacheUtil().search(search_users=username_list)
else:
users = LDAPServerUtil().search(search_users=username_list)
return users
def post(self, request):
try:
users = self.get_ldap_users()
except Exception as e:
return Response({'error': str(e)}, status=400)
if users is None:
return Response({'msg': _('Get ldap users is None')}, status=400)
org = self.get_org()
errors = LDAPImportUtil().perform_import(users, org)
if errors:
return Response({'errors': errors}, status=400)
count = users if users is None else len(users)
return Response({
'msg': _('Imported {} users successfully (Organization: {})').format(count, org)
})
class LDAPCacheRefreshAPI(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs):
try:
LDAPSyncUtil().clear_cache()
except Exception as e:
logger.error(str(e))
return Response(data={'msg': str(e)}, status=400)
return Response(data={'msg': 'success'})