perf: ldap 能多组织同步用户 (#10543)

Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
pull/10556/head
fit2bot 2023-05-25 17:35:36 +08:00 committed by GitHub
parent fa21c83db3
commit a6366a2dd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 72 additions and 36 deletions

View File

@ -255,7 +255,7 @@ class Config(dict):
'AUTH_LDAP_SYNC_IS_PERIODIC': False,
'AUTH_LDAP_SYNC_INTERVAL': None,
'AUTH_LDAP_SYNC_CRONTAB': None,
'AUTH_LDAP_SYNC_ORG_ID': '00000000-0000-0000-0000-000000000002',
'AUTH_LDAP_SYNC_ORG_IDS': ['00000000-0000-0000-0000-000000000002'],
'AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS': False,
'AUTH_LDAP_OPTIONS_OPT_REFERRALS': -1,

View File

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
#
import os
import ldap
from ..const import CONFIG, PROJECT_DIR, BASE_DIR
@ -48,7 +49,7 @@ AUTH_LDAP_SEARCH_PAGED_SIZE = CONFIG.AUTH_LDAP_SEARCH_PAGED_SIZE
AUTH_LDAP_SYNC_IS_PERIODIC = CONFIG.AUTH_LDAP_SYNC_IS_PERIODIC
AUTH_LDAP_SYNC_INTERVAL = CONFIG.AUTH_LDAP_SYNC_INTERVAL
AUTH_LDAP_SYNC_CRONTAB = CONFIG.AUTH_LDAP_SYNC_CRONTAB
AUTH_LDAP_SYNC_ORG_ID = CONFIG.AUTH_LDAP_SYNC_ORG_ID
AUTH_LDAP_SYNC_ORG_IDS = CONFIG.AUTH_LDAP_SYNC_ORG_IDS
AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS = CONFIG.AUTH_LDAP_USER_LOGIN_ONLY_IN_USERS
# ==============================================================================

View File

@ -62,7 +62,7 @@ class OrgViewSet(JMSBulkModelViewSet):
msg = _('The current organization ({}) cannot be deleted').format(current_org)
raise PermissionDenied(detail=msg)
if str(instance.id) == settings.AUTH_LDAP_SYNC_ORG_ID:
if str(instance.id) in settings.AUTH_LDAP_SYNC_ORG_IDS:
msg = _(
'LDAP synchronization is set to the current organization. '
'Please switch to another organization before deleting'

View File

@ -191,13 +191,13 @@ class LDAPUserImportAPI(APIView):
'POST': 'settings.change_auth'
}
def get_org(self):
org_id = self.request.data.get('org_id')
if is_uuid(org_id):
org = Organization.objects.get(id=org_id)
def get_orgs(self):
org_ids = self.request.data.get('org_ids')
if org_ids:
orgs = list(Organization.objects.filter(id__in=org_ids))
else:
org = current_org
return org
orgs = [current_org]
return orgs
def get_ldap_users(self):
username_list = self.request.data.get('username_list', [])
@ -219,14 +219,15 @@ class LDAPUserImportAPI(APIView):
if users is None:
return Response({'msg': _('Get ldap users is None')}, status=400)
org = self.get_org()
errors = LDAPImportUtil().perform_import(users, org)
orgs = self.get_orgs()
errors = LDAPImportUtil().perform_import(users, orgs)
if errors:
return Response({'errors': errors}, status=400)
count = users if users is None else len(users)
orgs_name = ', '.join([str(org) for org in orgs])
return Response({
'msg': _('Imported {} users successfully (Organization: {})').format(count, org)
'msg': _('Imported {} users successfully (Organization: {})').format(count, orgs_name)
})

View File

@ -0,0 +1,29 @@
# Generated by Django 3.2.19 on 2023-05-25 09:00
import json
from django.db import migrations
def migrate_ldap_sync_org_ids(apps, schema_editor):
setting_model = apps.get_model("settings", "Setting")
db_alias = schema_editor.connection.alias
instance = setting_model.objects.using(db_alias).filter(name='AUTH_LDAP_SYNC_ORG_ID').first()
if not instance:
return
ldap_sync_org_id = json.loads(instance.value)
setting_model.objects.using(db_alias).update_or_create(
name='AUTH_LDAP_SYNC_ORG_IDS', category='ldap',
value=json.dumps([ldap_sync_org_id])
)
instance.delete()
class Migration(migrations.Migration):
dependencies = [
('settings', '0006_remove_setting_enabled'),
]
operations = [
migrations.RunPython(migrate_ldap_sync_org_ids)
]

View File

@ -59,7 +59,7 @@ class LDAPSettingSerializer(serializers.Serializer):
help_text=_('User attr map present how to map LDAP user attr to '
'jumpserver, username,name,email is jumpserver attr')
)
AUTH_LDAP_SYNC_ORG_ID = serializers.CharField(
AUTH_LDAP_SYNC_ORG_IDS = serializers.ListField(
required=False, label=_('Organization'), max_length=36
)
AUTH_LDAP_SYNC_IS_PERIODIC = serializers.BooleanField(

View File

@ -16,7 +16,7 @@ class PrivateSettingSerializer(PublicSettingSerializer):
OLD_PASSWORD_HISTORY_LIMIT_COUNT = serializers.IntegerField()
TICKET_AUTHORIZE_DEFAULT_TIME = serializers.IntegerField()
TICKET_AUTHORIZE_DEFAULT_TIME_UNIT = serializers.CharField()
AUTH_LDAP_SYNC_ORG_ID = serializers.CharField()
AUTH_LDAP_SYNC_ORG_IDS = serializers.ListField()
SECURITY_MAX_IDLE_TIME = serializers.IntegerField()
SECURITY_VIEW_AUTH_NEED_MFA = serializers.BooleanField()
SECURITY_MFA_VERIFY_TTL = serializers.IntegerField()

View File

@ -2,8 +2,8 @@
#
from celery import shared_task
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.db import transaction
from django.utils.translation import ugettext_lazy as _
from common.utils import get_logger
from ops.celery.decorator import after_app_ready_start
@ -30,14 +30,14 @@ def import_ldap_user():
util_import = LDAPImportUtil()
users = util_server.search()
if settings.XPACK_ENABLED:
org_id = settings.AUTH_LDAP_SYNC_ORG_ID
org_ids = settings.AUTH_LDAP_SYNC_ORG_IDS
default_org = None
else:
# 社区版默认导入Default组织
org_id = Organization.DEFAULT_ID
org_ids = [Organization.DEFAULT_ID]
default_org = Organization.default()
org = Organization.get_instance(org_id, default=default_org)
errors = util_import.perform_import(users, org)
orgs = list(set([Organization.get_instance(org_id, default=default_org) for org_id in org_ids]))
errors = util_import.perform_import(users, orgs)
if errors:
logger.error("Imported LDAP users errors: {}".format(errors))
else:

View File

@ -1,8 +1,13 @@
# coding: utf-8
#
import os
import json
from collections import defaultdict
from copy import deepcopy
from django.conf import settings
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from ldap3 import Server, Connection, SIMPLE
from ldap3.core.exceptions import (
LDAPSocketOpenError,
@ -18,19 +23,14 @@ from ldap3.core.exceptions import (
LDAPConfigurationError,
LDAPAttributeError,
)
from django.conf import settings
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from copy import deepcopy
from collections import defaultdict
from orgs.utils import tmp_to_org
from common.const import LDAP_AD_ACCOUNT_DISABLE
from common.utils import timeit, get_logger
from common.db.utils import close_old_connections
from users.utils import construct_user_email
from users.models import User, UserGroup
from authentication.backends.ldap import LDAPAuthorizationBackend, LDAPUser
from common.const import LDAP_AD_ACCOUNT_DISABLE
from common.db.utils import close_old_connections
from common.utils import timeit, get_logger
from orgs.utils import tmp_to_org
from users.models import User, UserGroup
from users.utils import construct_user_email
logger = get_logger(__file__)
@ -394,7 +394,7 @@ class LDAPImportUtil(object):
group_names.append(group_name)
return group_names
def perform_import(self, users, org=None):
def perform_import(self, users, orgs):
logger.info('Start perform import ldap users, count: {}'.format(len(users)))
errors = []
objs = []
@ -416,13 +416,20 @@ class LDAPImportUtil(object):
errors.append({user['username']: str(e)})
logger.error(e)
continue
for org in orgs:
self.bind_org(org, objs, group_users_mapper)
logger.info('End perform import ldap users')
return errors
@staticmethod
def bind_org(org, users, group_users_mapper):
if not org:
return
if org.is_root():
return
# add user to org
for obj in objs:
org.add_member(obj)
for user in users:
org.add_member(user)
# add user to group
with tmp_to_org(org):
for group_name, users in group_users_mapper.items():
@ -430,8 +437,6 @@ class LDAPImportUtil(object):
name=group_name, defaults={'name': group_name}
)
group.users.add(*users)
logger.info('End perform import ldap users')
return errors
class LDAPTestUtil(object):