mirror of https://github.com/jumpserver/jumpserver
perf: ldap 能多组织同步用户 (#10543)
Co-authored-by: feng <1304903146@qq.com> Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>pull/10556/head
parent
fa21c83db3
commit
a6366a2dd4
|
@ -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,
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
# ==============================================================================
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
]
|
|
@ -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(
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue