mirror of https://github.com/jumpserver/jumpserver
				
				
				
			perf: rename ad to ds
							parent
							
								
									3f452daee8
								
							
						
					
					
						commit
						acaa4cf2d5
					
				| 
						 | 
				
			
			@ -139,17 +139,25 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
 | 
			
		|||
        return self.id
 | 
			
		||||
 | 
			
		||||
    @lazyproperty
 | 
			
		||||
    def ad_domain(self):
 | 
			
		||||
    def ds_id(self):
 | 
			
		||||
        if self.username.startswith('@'):
 | 
			
		||||
            return None
 | 
			
		||||
        if self.platform.category == 'ad':
 | 
			
		||||
            return self.asset.ad.domain_name
 | 
			
		||||
        if self.platform.category == 'ds':
 | 
			
		||||
            return self.asset.directoryservice.id
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    @lazyproperty
 | 
			
		||||
    def ds_domain(self):
 | 
			
		||||
        if self.username.startswith('@'):
 | 
			
		||||
            return None
 | 
			
		||||
        if self.ds_id:
 | 
			
		||||
            return self.asset.ds.domain_name
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
    @lazyproperty
 | 
			
		||||
    def full_username(self):
 | 
			
		||||
        if self.ad_domain:
 | 
			
		||||
            return '{}@{}'.format(self.username, self.ad_domain)
 | 
			
		||||
        if self.ds_domain:
 | 
			
		||||
            return '{}@{}'.format(self.username, self.ds_domain)
 | 
			
		||||
        return self.username
 | 
			
		||||
 | 
			
		||||
    @lazyproperty
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -241,7 +241,7 @@ class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerialize
 | 
			
		|||
            'date_change_secret', 'change_secret_status'
 | 
			
		||||
        ]
 | 
			
		||||
        fields = BaseAccountSerializer.Meta.fields + [
 | 
			
		||||
            'su_from', 'asset', 'version', "ad_domain",
 | 
			
		||||
            'su_from', 'asset', 'version', 'ds_domain',
 | 
			
		||||
            'source', 'source_id', 'secret_reset',
 | 
			
		||||
        ] + AccountCreateUpdateSerializerMixin.Meta.fields + automation_fields
 | 
			
		||||
        read_only_fields = BaseAccountSerializer.Meta.read_only_fields + automation_fields
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -87,7 +87,7 @@ class BaseAccountSerializer(
 | 
			
		|||
            "username": {
 | 
			
		||||
                "help_text": _(
 | 
			
		||||
                    "* If no username is required for authentication, enter null.  "
 | 
			
		||||
                    "For AD accounts, use the format username@domain."
 | 
			
		||||
                    "For DS accounts, use the format username@domain."
 | 
			
		||||
                )
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,7 +21,7 @@ class VirtualAccountSerializer(serializers.ModelSerializer):
 | 
			
		|||
            'username': {'label': _('Username')},
 | 
			
		||||
            'secret_from_login': {
 | 
			
		||||
                'help_text': _(
 | 
			
		||||
                    'Current only support login from AD/LDAP. Secret priority: '
 | 
			
		||||
                    'Current only support login from DS/LDAP. Secret priority: '
 | 
			
		||||
                    'Same account in asset secret > Login secret > Manual input. <br/ >'
 | 
			
		||||
                    'For security, please set config CACHE_LOGIN_PASSWORD_ENABLED to true'
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,8 +3,8 @@ from .cloud import *
 | 
			
		|||
from .custom import *
 | 
			
		||||
from .database import *
 | 
			
		||||
from .device import *
 | 
			
		||||
from .ds import *
 | 
			
		||||
from .gpt import *
 | 
			
		||||
from .host import *
 | 
			
		||||
from .permission import *
 | 
			
		||||
from .web import *
 | 
			
		||||
from .ad import *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +0,0 @@
 | 
			
		|||
from assets.models import AD, Asset
 | 
			
		||||
from assets.serializers import ADSerializer
 | 
			
		||||
 | 
			
		||||
from .asset import AssetViewSet
 | 
			
		||||
 | 
			
		||||
__all__ = ['ADViewSet']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ADViewSet(AssetViewSet):
 | 
			
		||||
    model = AD
 | 
			
		||||
    perm_model = Asset
 | 
			
		||||
 | 
			
		||||
    def get_serializer_classes(self):
 | 
			
		||||
        serializer_classes = super().get_serializer_classes()
 | 
			
		||||
        serializer_classes['default'] = ADSerializer
 | 
			
		||||
        return serializer_classes
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
from assets.models import DirectoryService, Asset
 | 
			
		||||
from assets.serializers import DSSerializer
 | 
			
		||||
 | 
			
		||||
from .asset import AssetViewSet
 | 
			
		||||
 | 
			
		||||
__all__ = ['DSViewSet']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DSViewSet(AssetViewSet):
 | 
			
		||||
    model = DirectoryService
 | 
			
		||||
    perm_model = Asset
 | 
			
		||||
 | 
			
		||||
    def get_serializer_classes(self):
 | 
			
		||||
        serializer_classes = super().get_serializer_classes()
 | 
			
		||||
        serializer_classes['default'] = DSSerializer
 | 
			
		||||
        return serializer_classes
 | 
			
		||||
| 
						 | 
				
			
			@ -112,8 +112,7 @@ class BaseType(TextChoices):
 | 
			
		|||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_choices(cls):
 | 
			
		||||
        if not settings.XPACK_LICENSE_IS_VALID:
 | 
			
		||||
            choices = [(tp.value, tp.label) for tp in cls.get_community_types()]
 | 
			
		||||
        else:
 | 
			
		||||
        choices = cls.choices
 | 
			
		||||
        if not settings.XPACK_LICENSE_IS_VALID and hasattr(cls, 'get_community_types'):
 | 
			
		||||
            choices = [(tp.value, tp.label) for tp in cls.get_community_types()]
 | 
			
		||||
        return choices
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ class Category(ChoicesMixin, models.TextChoices):
 | 
			
		|||
    DATABASE = 'database', _("Database")
 | 
			
		||||
    CLOUD = 'cloud', _("Cloud service")
 | 
			
		||||
    WEB = 'web', _("Web")
 | 
			
		||||
    AD = 'ad', _("Active Directory")
 | 
			
		||||
    DS = 'ds', _("Directory service")
 | 
			
		||||
    CUSTOM = 'custom', _("Custom type")
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ class DeviceTypes(BaseType):
 | 
			
		|||
            '*': {
 | 
			
		||||
                'charset_enabled': False,
 | 
			
		||||
                'domain_enabled': True,
 | 
			
		||||
                'ad_enabled': False,
 | 
			
		||||
                'ds_enabled': False,
 | 
			
		||||
                'su_enabled': True,
 | 
			
		||||
                'su_methods': ['enable', 'super', 'super_level']
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,11 +3,13 @@ from django.utils.translation import gettext_lazy as _
 | 
			
		|||
from .base import BaseType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ADTypes(BaseType):
 | 
			
		||||
    AD = 'ad', _('Active Directory')
 | 
			
		||||
class DirectoryTypes(BaseType):
 | 
			
		||||
    GENERAL = 'general', _('General')
 | 
			
		||||
    # LDAP = 'ldap', _('LDAP')
 | 
			
		||||
    # AD = 'ad', _('Active Directory')
 | 
			
		||||
    WINDOWS_AD = 'windows_ad', _('Windows Active Directory')
 | 
			
		||||
    LDAP = 'ldap', _('LDAP')
 | 
			
		||||
    AZURE_AD = 'azure_ad', _('Azure Active Directory')
 | 
			
		||||
 | 
			
		||||
    # AZURE_AD = 'azure_ad', _('Azure Active Directory')
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _get_base_constrains(cls) -> dict:
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +17,7 @@ class ADTypes(BaseType):
 | 
			
		|||
            '*': {
 | 
			
		||||
                'charset_enabled': False,
 | 
			
		||||
                'domain_enabled': True,
 | 
			
		||||
                'ad_enabled': False,
 | 
			
		||||
                'ds_enabled': False,
 | 
			
		||||
                'su_enabled': True,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +26,9 @@ class ADTypes(BaseType):
 | 
			
		|||
    def _get_automation_constrains(cls) -> dict:
 | 
			
		||||
        constrains = {
 | 
			
		||||
            '*': {
 | 
			
		||||
                'ansible_enabled': False,
 | 
			
		||||
            },
 | 
			
		||||
            cls.WINDOWS_AD: {
 | 
			
		||||
                'ansible_enabled': True,
 | 
			
		||||
                'ping_enabled': True,
 | 
			
		||||
                'gather_facts_enabled': False,
 | 
			
		||||
| 
						 | 
				
			
			@ -38,36 +43,24 @@ class ADTypes(BaseType):
 | 
			
		|||
    @classmethod
 | 
			
		||||
    def _get_protocol_constrains(cls) -> dict:
 | 
			
		||||
        return {
 | 
			
		||||
            cls.GENERAL: {
 | 
			
		||||
                'choices': ['ssh']
 | 
			
		||||
            },
 | 
			
		||||
            cls.WINDOWS_AD: {
 | 
			
		||||
                'choices': ['rdp', 'ssh', 'vnc', 'winrm']
 | 
			
		||||
            },
 | 
			
		||||
            cls.LDAP: {
 | 
			
		||||
                'choices': ['ssh', 'ldap']
 | 
			
		||||
            },
 | 
			
		||||
            cls.AZURE_AD: {
 | 
			
		||||
                'choices': ['ldap']
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def internal_platforms(cls):
 | 
			
		||||
        return {
 | 
			
		||||
            cls.AD: [
 | 
			
		||||
                {'name': 'Active Directory'}
 | 
			
		||||
            ],
 | 
			
		||||
            cls.WINDOWS_AD: [
 | 
			
		||||
                {'name': 'Windows Active Directory'}
 | 
			
		||||
            ],
 | 
			
		||||
            cls.LDAP: [
 | 
			
		||||
                {'name': 'LDAP'}
 | 
			
		||||
            ],
 | 
			
		||||
            cls.AZURE_AD: [
 | 
			
		||||
                {'name': 'Azure Active Directory'}
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_community_types(cls):
 | 
			
		||||
        return [
 | 
			
		||||
            cls.LDAP,
 | 
			
		||||
            cls.GENERAL,
 | 
			
		||||
        ]
 | 
			
		||||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ class HostTypes(BaseType):
 | 
			
		|||
                'charset': 'utf-8',  # default
 | 
			
		||||
                'domain_enabled': True,
 | 
			
		||||
                'su_enabled': True,
 | 
			
		||||
                'ad_enabled': True,
 | 
			
		||||
                'ds_enabled': True,
 | 
			
		||||
                'su_methods': ['sudo', 'su', 'only_sudo', 'only_su'],
 | 
			
		||||
            },
 | 
			
		||||
            cls.WINDOWS: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,10 +13,10 @@ from .cloud import CloudTypes
 | 
			
		|||
from .custom import CustomTypes
 | 
			
		||||
from .database import DatabaseTypes
 | 
			
		||||
from .device import DeviceTypes
 | 
			
		||||
from .ds import DirectoryTypes
 | 
			
		||||
from .gpt import GPTTypes
 | 
			
		||||
from .host import HostTypes
 | 
			
		||||
from .web import WebTypes
 | 
			
		||||
from .ad import ADTypes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AllTypes(ChoicesMixin):
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ class AllTypes(ChoicesMixin):
 | 
			
		|||
    includes = [
 | 
			
		||||
        HostTypes, DeviceTypes, DatabaseTypes,
 | 
			
		||||
        CloudTypes, WebTypes, CustomTypes,
 | 
			
		||||
        ADTypes, GPTTypes
 | 
			
		||||
        DirectoryTypes, GPTTypes
 | 
			
		||||
    ]
 | 
			
		||||
    _category_constrains = {}
 | 
			
		||||
    _automation_methods = None
 | 
			
		||||
| 
						 | 
				
			
			@ -175,7 +175,7 @@ class AllTypes(ChoicesMixin):
 | 
			
		|||
            (Category.DATABASE, DatabaseTypes),
 | 
			
		||||
            (Category.WEB, WebTypes),
 | 
			
		||||
            (Category.CLOUD, CloudTypes),
 | 
			
		||||
            (Category.AD, ADTypes),
 | 
			
		||||
            (Category.DS, DirectoryTypes),
 | 
			
		||||
            (Category.CUSTOM, CustomTypes)
 | 
			
		||||
        ]
 | 
			
		||||
        return types
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
# Generated by Django 4.1.13 on 2025-03-31 02:49
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
import django
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
from assets.const.types import AllTypes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("assets", "0015_automationexecution_type"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RunPython(add_ad_host_type),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="DS",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "asset_ptr",
 | 
			
		||||
                    models.OneToOneField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                        parent_link=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        to="assets.asset",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "domain_name",
 | 
			
		||||
                    models.CharField(
 | 
			
		||||
                        blank=True,
 | 
			
		||||
                        default="",
 | 
			
		||||
                        max_length=128,
 | 
			
		||||
                        verbose_name="Domain name",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Active Directory",
 | 
			
		||||
            },
 | 
			
		||||
            bases=("assets.asset",),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="platform",
 | 
			
		||||
            name="ds",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                related_name="ad_platforms",
 | 
			
		||||
                to="assets.ds",
 | 
			
		||||
                verbose_name="Active Directory",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="platform",
 | 
			
		||||
            name="ds_enabled",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="DS enabled"),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,166 +0,0 @@
 | 
			
		|||
# Generated by Django 4.1.13 on 2025-03-31 02:49
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
import django
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
from assets.const.types import AllTypes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_ad_host_type(apps, schema_editor):
 | 
			
		||||
    data = """
 | 
			
		||||
     [
 | 
			
		||||
       {
 | 
			
		||||
          "created_by": "system",
 | 
			
		||||
          "updated_by": "system",
 | 
			
		||||
          "comment": "",
 | 
			
		||||
          "name": "Windows AD",
 | 
			
		||||
          "category": "ad",
 | 
			
		||||
          "type": "windows_ad",
 | 
			
		||||
          "meta": {},
 | 
			
		||||
          "internal": true,
 | 
			
		||||
          "domain_enabled": true,
 | 
			
		||||
          "su_enabled": false,
 | 
			
		||||
          "su_method": null,
 | 
			
		||||
          "custom_fields": [],
 | 
			
		||||
          "automation": {
 | 
			
		||||
              "ansible_enabled": true,
 | 
			
		||||
              "ansible_config": {
 | 
			
		||||
                  "ansible_shell_type": "cmd",
 | 
			
		||||
                  "ansible_connection": "ssh"
 | 
			
		||||
              },
 | 
			
		||||
              "ping_enabled": true,
 | 
			
		||||
              "ping_method": "ping_by_rdp",
 | 
			
		||||
              "ping_params": {},
 | 
			
		||||
              "gather_facts_enabled": true,
 | 
			
		||||
              "gather_facts_method": "gather_facts_windows",
 | 
			
		||||
              "gather_facts_params": {},
 | 
			
		||||
              "change_secret_enabled": true,
 | 
			
		||||
              "change_secret_method": "change_secret_ad_windows",
 | 
			
		||||
              "change_secret_params": {},
 | 
			
		||||
              "push_account_enabled": true,
 | 
			
		||||
              "push_account_method": "push_account_ad_windows",
 | 
			
		||||
              "push_account_params": {},
 | 
			
		||||
              "verify_account_enabled": true,
 | 
			
		||||
              "verify_account_method": "verify_account_by_rdp",
 | 
			
		||||
              "verify_account_params": {},
 | 
			
		||||
              "gather_accounts_enabled": true,
 | 
			
		||||
              "gather_accounts_method": "gather_accounts_ad_windows",
 | 
			
		||||
              "gather_accounts_params": {},
 | 
			
		||||
              "remove_account_enabled": true,
 | 
			
		||||
              "remove_account_method": "remove_account_ad_windows",
 | 
			
		||||
              "remove_account_params": {}
 | 
			
		||||
          },
 | 
			
		||||
          "protocols": [
 | 
			
		||||
              {
 | 
			
		||||
                  "name": "rdp",
 | 
			
		||||
                  "port": 3389,
 | 
			
		||||
                  "primary": true,
 | 
			
		||||
                  "required": false,
 | 
			
		||||
                  "default": false,
 | 
			
		||||
                  "public": true,
 | 
			
		||||
                  "setting": {
 | 
			
		||||
                      "console": false,
 | 
			
		||||
                      "security": "any"
 | 
			
		||||
                  }
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                  "name": "ssh",
 | 
			
		||||
                  "port": 22,
 | 
			
		||||
                  "primary": false,
 | 
			
		||||
                  "required": false,
 | 
			
		||||
                  "default": false,
 | 
			
		||||
                  "public": true,
 | 
			
		||||
                  "setting": {
 | 
			
		||||
                      "sftp_enabled": true,
 | 
			
		||||
                      "sftp_home": "/tmp"
 | 
			
		||||
                  }
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                  "name": "vnc",
 | 
			
		||||
                  "port": 5900,
 | 
			
		||||
                  "primary": false,
 | 
			
		||||
                  "required": false,
 | 
			
		||||
                  "default": false,
 | 
			
		||||
                  "public": true,
 | 
			
		||||
                  "setting": {}
 | 
			
		||||
              },
 | 
			
		||||
              {
 | 
			
		||||
                  "name": "winrm",
 | 
			
		||||
                  "port": 5985,
 | 
			
		||||
                  "primary": false,
 | 
			
		||||
                  "required": false,
 | 
			
		||||
                  "default": false,
 | 
			
		||||
                  "public": false,
 | 
			
		||||
                  "setting": {
 | 
			
		||||
                      "use_ssl": false
 | 
			
		||||
                  }
 | 
			
		||||
              }
 | 
			
		||||
          ]
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
    """
 | 
			
		||||
    platform_model = apps.get_model('assets', 'Platform')
 | 
			
		||||
    automation_cls = apps.get_model('assets', 'PlatformAutomation')
 | 
			
		||||
    platform_datas = json.loads(data)
 | 
			
		||||
 | 
			
		||||
    for platform_data in platform_datas:
 | 
			
		||||
        AllTypes.create_or_update_by_platform_data(platform_data, platform_cls=platform_model,
 | 
			
		||||
                                                   automation_cls=automation_cls)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("assets", "0015_automationexecution_type"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RunPython(add_ad_host_type),
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="AD",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "asset_ptr",
 | 
			
		||||
                    models.OneToOneField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                        parent_link=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        to="assets.asset",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "domain_name",
 | 
			
		||||
                    models.CharField(
 | 
			
		||||
                        blank=True,
 | 
			
		||||
                        default="",
 | 
			
		||||
                        max_length=128,
 | 
			
		||||
                        verbose_name="Domain name",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Active Directory",
 | 
			
		||||
            },
 | 
			
		||||
            bases=("assets.asset",),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="platform",
 | 
			
		||||
            name="ad",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                related_name="ad_platforms",
 | 
			
		||||
                to="assets.ad",
 | 
			
		||||
                verbose_name="Active Directory",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="platform",
 | 
			
		||||
            name="ad_enabled",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="AD enabled"),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,59 @@
 | 
			
		|||
# Generated by Django 4.1.13 on 2025-04-03 09:51
 | 
			
		||||
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("assets", "0015_automationexecution_type"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="DirectoryService",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "asset_ptr",
 | 
			
		||||
                    models.OneToOneField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                        parent_link=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        to="assets.asset",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "domain_name",
 | 
			
		||||
                    models.CharField(
 | 
			
		||||
                        blank=True,
 | 
			
		||||
                        default="",
 | 
			
		||||
                        max_length=128,
 | 
			
		||||
                        verbose_name="Domain name",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Directory service",
 | 
			
		||||
            },
 | 
			
		||||
            bases=("assets.asset",),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="platform",
 | 
			
		||||
            name="ds_enabled",
 | 
			
		||||
            field=models.BooleanField(default=False, verbose_name="DS enabled"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="platform",
 | 
			
		||||
            name="ds",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                related_name="ds_platforms",
 | 
			
		||||
                to="assets.directoryservice",
 | 
			
		||||
                verbose_name="Active Directory",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,168 @@
 | 
			
		|||
# Generated by Django 4.1.13 on 2025-04-07 03:24
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
from assets.const import AllTypes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def add_ds_platforms(apps, schema_editor):
 | 
			
		||||
    data = """
 | 
			
		||||
[
 | 
			
		||||
    {
 | 
			
		||||
        "created_by": "system",
 | 
			
		||||
        "updated_by": "system",
 | 
			
		||||
        "comment": "",
 | 
			
		||||
        "name": "Windows active directory",
 | 
			
		||||
        "category": "ds",
 | 
			
		||||
        "type": "windows_ad",
 | 
			
		||||
        "meta": {},
 | 
			
		||||
        "internal": true,
 | 
			
		||||
        "domain_enabled": true,
 | 
			
		||||
        "su_enabled": false,
 | 
			
		||||
        "su_method": null,
 | 
			
		||||
        "custom_fields": [],
 | 
			
		||||
        "automation": {
 | 
			
		||||
            "ansible_enabled": true,
 | 
			
		||||
            "ansible_config": {
 | 
			
		||||
                "ansible_shell_type": "cmd",
 | 
			
		||||
                "ansible_connection": "ssh"
 | 
			
		||||
            },
 | 
			
		||||
            "ping_enabled": true,
 | 
			
		||||
            "ping_method": "ping_by_rdp",
 | 
			
		||||
            "ping_params": {},
 | 
			
		||||
            "gather_facts_enabled": true,
 | 
			
		||||
            "gather_facts_method": "gather_facts_windows",
 | 
			
		||||
            "gather_facts_params": {},
 | 
			
		||||
            "change_secret_enabled": true,
 | 
			
		||||
            "change_secret_method": "change_secret_ad_windows",
 | 
			
		||||
            "change_secret_params": {
 | 
			
		||||
            },
 | 
			
		||||
            "push_account_enabled": true,
 | 
			
		||||
            "push_account_method": "push_account_ad_windows",
 | 
			
		||||
            "push_account_params": {},
 | 
			
		||||
            "verify_account_enabled": true,
 | 
			
		||||
            "verify_account_method": "verify_account_by_rdp",
 | 
			
		||||
            "verify_account_params": {
 | 
			
		||||
 | 
			
		||||
            },
 | 
			
		||||
            "gather_accounts_enabled": true,
 | 
			
		||||
            "gather_accounts_method": "gather_accounts_ad_windows",
 | 
			
		||||
            "gather_accounts_params": {
 | 
			
		||||
 | 
			
		||||
            },
 | 
			
		||||
            "remove_account_enabled": true,
 | 
			
		||||
            "remove_account_method": "remove_account_ad_windows",
 | 
			
		||||
            "remove_account_params": {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "protocols": [
 | 
			
		||||
            {
 | 
			
		||||
                "name": "rdp",
 | 
			
		||||
                "port": 3389,
 | 
			
		||||
                "primary": true,
 | 
			
		||||
                "required": false,
 | 
			
		||||
                "default": false,
 | 
			
		||||
                "public": true,
 | 
			
		||||
                "setting": {
 | 
			
		||||
                    "console": false,
 | 
			
		||||
                    "security": "any"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "name": "ssh",
 | 
			
		||||
                "port": 22,
 | 
			
		||||
                "primary": false,
 | 
			
		||||
                "required": false,
 | 
			
		||||
                "default": false,
 | 
			
		||||
                "public": true,
 | 
			
		||||
                "setting": {
 | 
			
		||||
                    "sftp_enabled": true,
 | 
			
		||||
                    "sftp_home": "/tmp"
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "name": "vnc",
 | 
			
		||||
                "port": 5900,
 | 
			
		||||
                "primary": false,
 | 
			
		||||
                "required": false,
 | 
			
		||||
                "default": false,
 | 
			
		||||
                "public": true,
 | 
			
		||||
                "setting": {
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                "name": "winrm",
 | 
			
		||||
                "port": 5985,
 | 
			
		||||
                "primary": false,
 | 
			
		||||
                "required": false,
 | 
			
		||||
                "default": false,
 | 
			
		||||
                "public": false,
 | 
			
		||||
                "setting": {
 | 
			
		||||
                    "use_ssl": false
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        "created_by": "system",
 | 
			
		||||
        "updated_by": "system",
 | 
			
		||||
        "comment": "",
 | 
			
		||||
        "name": "General",
 | 
			
		||||
        "category": "ds",
 | 
			
		||||
        "type": "general",
 | 
			
		||||
        "meta": {
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
        "internal": true,
 | 
			
		||||
        "domain_enabled": false,
 | 
			
		||||
        "su_enabled": false,
 | 
			
		||||
        "su_method": null,
 | 
			
		||||
        "custom_fields": [
 | 
			
		||||
 | 
			
		||||
        ],
 | 
			
		||||
        "automation": {
 | 
			
		||||
            "ansible_enabled": false,
 | 
			
		||||
            "ansible_config": {
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "protocols": [
 | 
			
		||||
            {
 | 
			
		||||
                "name": "ssh",
 | 
			
		||||
                "port": 22,
 | 
			
		||||
                "primary": true,
 | 
			
		||||
                "required": false,
 | 
			
		||||
                "default": false,
 | 
			
		||||
                "public": true,
 | 
			
		||||
                "setting": {
 | 
			
		||||
                    "sftp_enabled": true,
 | 
			
		||||
                    "sftp_home": "/tmp"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
    """
 | 
			
		||||
    platform_model = apps.get_model('assets', 'Platform')
 | 
			
		||||
    automation_cls = apps.get_model('assets', 'PlatformAutomation')
 | 
			
		||||
    platform_datas = json.loads(data)
 | 
			
		||||
 | 
			
		||||
    for platform_data in platform_datas:
 | 
			
		||||
        AllTypes.create_or_update_by_platform_data(
 | 
			
		||||
            platform_data, platform_cls=platform_model,
 | 
			
		||||
            automation_cls=automation_cls
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("assets", "0016_directory_service"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RunPython(add_ds_platforms)
 | 
			
		||||
    ]
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +1,9 @@
 | 
			
		|||
from .ad import *
 | 
			
		||||
from .cloud import *
 | 
			
		||||
from .common import *
 | 
			
		||||
from .custom import *
 | 
			
		||||
from .database import *
 | 
			
		||||
from .device import *
 | 
			
		||||
from .ds import *
 | 
			
		||||
from .gpt import *
 | 
			
		||||
from .host import *
 | 
			
		||||
from .web import *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -247,10 +247,10 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
 | 
			
		|||
 | 
			
		||||
    @property
 | 
			
		||||
    def all_accounts(self):
 | 
			
		||||
        if not self.joined_ad_id:
 | 
			
		||||
        if not self.joined_dir_svc_id:
 | 
			
		||||
            queryset = self.accounts.all()
 | 
			
		||||
        else:
 | 
			
		||||
            queryset = self.accounts.model.objects.filter(asset__in=[self.id, self.joined_ad_id])
 | 
			
		||||
            queryset = self.accounts.model.objects.filter(asset__in=[self.id, self.joined_dir_svc_id])
 | 
			
		||||
        return queryset
 | 
			
		||||
 | 
			
		||||
    @lazyproperty
 | 
			
		||||
| 
						 | 
				
			
			@ -273,15 +273,15 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
 | 
			
		|||
        protocol = self.protocols.all().filter(name=protocol).first()
 | 
			
		||||
        return protocol.port if protocol else 0
 | 
			
		||||
 | 
			
		||||
    def is_ad(self):
 | 
			
		||||
        return self.category == const.Category.AD
 | 
			
		||||
    def is_dir_svc(self):
 | 
			
		||||
        return self.category == const.Category.DS
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def joined_ad_id(self):
 | 
			
		||||
        return self.platform.ad_id
 | 
			
		||||
    def joined_dir_svc_id(self):
 | 
			
		||||
        return self.platform.ds_id
 | 
			
		||||
 | 
			
		||||
    def is_joined_ad(self):
 | 
			
		||||
        if self.joined_ad_id:
 | 
			
		||||
        if self.joined_dir_svc_id:
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            return False
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,11 +3,11 @@ from django.utils.translation import gettext_lazy as _
 | 
			
		|||
 | 
			
		||||
from .common import Asset
 | 
			
		||||
 | 
			
		||||
__all__ = ['AD']
 | 
			
		||||
__all__ = ['DirectoryService']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AD(Asset):
 | 
			
		||||
class DirectoryService(Asset):
 | 
			
		||||
    domain_name = models.CharField(max_length=128, blank=True, default='', verbose_name=_("Domain name"))
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("Active Directory")
 | 
			
		||||
        verbose_name = _("Directory service")
 | 
			
		||||
| 
						 | 
				
			
			@ -102,10 +102,10 @@ class Platform(LabeledMixin, JMSBaseModel):
 | 
			
		|||
        max_length=8, verbose_name=_("Charset")
 | 
			
		||||
    )
 | 
			
		||||
    domain_enabled = models.BooleanField(default=True, verbose_name=_("Gateway enabled"))
 | 
			
		||||
    ad_enabled = models.BooleanField(default=False, verbose_name=_("AD enabled"))
 | 
			
		||||
    ad = models.ForeignKey(
 | 
			
		||||
        'assets.AD', on_delete=models.SET_NULL, null=True, blank=True,
 | 
			
		||||
        verbose_name=_("Active Directory"), related_name='ad_platforms'
 | 
			
		||||
    ds_enabled = models.BooleanField(default=False, verbose_name=_("DS enabled"))
 | 
			
		||||
    ds = models.ForeignKey(
 | 
			
		||||
        'DirectoryService', on_delete=models.SET_NULL, null=True, blank=True,
 | 
			
		||||
        verbose_name=_("Directory service"), related_name='ds_platforms'
 | 
			
		||||
    )
 | 
			
		||||
    # 账号有关的
 | 
			
		||||
    su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
 | 
			
		||||
| 
						 | 
				
			
			@ -121,8 +121,8 @@ class Platform(LabeledMixin, JMSBaseModel):
 | 
			
		|||
        return self.assets.count()
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        if not self.ad_enabled:
 | 
			
		||||
            self.ad = None
 | 
			
		||||
        if not self.ds_enabled:
 | 
			
		||||
            self.ds = None
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ from .common import *
 | 
			
		|||
from .custom import *
 | 
			
		||||
from .database import *
 | 
			
		||||
from .device import *
 | 
			
		||||
from .ds import *
 | 
			
		||||
from .gpt import *
 | 
			
		||||
from .host import *
 | 
			
		||||
from .web import *
 | 
			
		||||
from .ad import *
 | 
			
		||||
| 
						 | 
				
			
			@ -1,15 +1,14 @@
 | 
			
		|||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from rest_framework import serializers
 | 
			
		||||
 | 
			
		||||
from assets.models import AD
 | 
			
		||||
from assets.models import DirectoryService
 | 
			
		||||
from .common import AssetSerializer
 | 
			
		||||
 | 
			
		||||
__all__ = ['ADSerializer']
 | 
			
		||||
__all__ = ['DSSerializer']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ADSerializer(AssetSerializer):
 | 
			
		||||
class DSSerializer(AssetSerializer):
 | 
			
		||||
    class Meta(AssetSerializer.Meta):
 | 
			
		||||
        model = AD
 | 
			
		||||
        model = DirectoryService
 | 
			
		||||
        fields = AssetSerializer.Meta.fields + [
 | 
			
		||||
            'domain_name',
 | 
			
		||||
        ]
 | 
			
		||||
| 
						 | 
				
			
			@ -194,8 +194,8 @@ class PlatformSerializer(ResourceLabelsMixin, CommonSerializerMixin, WritableNes
 | 
			
		|||
        ]
 | 
			
		||||
        fields_m2m = ['assets', 'assets_amount']
 | 
			
		||||
        fields = fields_small + fields_m2m + [
 | 
			
		||||
            "protocols", "domain_enabled", "su_enabled", "su_method", "ad_enabled", "ad",
 | 
			
		||||
            "automation", "comment", "custom_fields", "labels"
 | 
			
		||||
            "protocols", "domain_enabled", "su_enabled", "su_method",
 | 
			
		||||
            "ds_enabled", "ds", "automation", "comment", "custom_fields", "labels"
 | 
			
		||||
        ] + read_only_fields
 | 
			
		||||
        extra_kwargs = {
 | 
			
		||||
            "su_enabled": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ router.register(r'databases', api.DatabaseViewSet, 'database')
 | 
			
		|||
router.register(r'webs', api.WebViewSet, 'web')
 | 
			
		||||
router.register(r'clouds', api.CloudViewSet, 'cloud')
 | 
			
		||||
router.register(r'gpts', api.GPTViewSet, 'gpt')
 | 
			
		||||
router.register(r'directories', api.ADViewSet, 'ad')
 | 
			
		||||
router.register(r'directories', api.DSViewSet, 'ds')
 | 
			
		||||
router.register(r'customs', api.CustomViewSet, 'custom')
 | 
			
		||||
router.register(r'platforms', api.AssetPlatformViewSet, 'platform')
 | 
			
		||||
router.register(r'nodes', api.NodeViewSet, 'node')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,6 @@ from django.db import models
 | 
			
		|||
from django.shortcuts import get_object_or_404
 | 
			
		||||
from django.utils import timezone
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from django.forms.models import model_to_dict
 | 
			
		||||
from rest_framework.exceptions import PermissionDenied
 | 
			
		||||
 | 
			
		||||
from accounts.models import VirtualAccount
 | 
			
		||||
| 
						 | 
				
			
			@ -267,7 +266,7 @@ class ConnectionToken(JMSOrgBaseModel):
 | 
			
		|||
                input_secret=self.input_secret, from_permed=False
 | 
			
		||||
            )
 | 
			
		||||
        else:
 | 
			
		||||
            account = self.asset.accounts.filter(name=self.account).first()
 | 
			
		||||
            account = self.asset.all_valid_accounts.filter(id=self.account).first()
 | 
			
		||||
            if not account.secret and self.input_secret:
 | 
			
		||||
                account.secret = self.input_secret
 | 
			
		||||
        return account
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,7 @@ class _ConnectionTokenAssetSerializer(serializers.ModelSerializer):
 | 
			
		|||
 | 
			
		||||
class _SimpleAccountSerializer(serializers.ModelSerializer):
 | 
			
		||||
    secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type'))
 | 
			
		||||
    username = serializers.CharField(label=_('Username'), source='full_username', read_only=True)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Account
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +50,7 @@ class _SimpleAccountSerializer(serializers.ModelSerializer):
 | 
			
		|||
class _ConnectionTokenAccountSerializer(serializers.ModelSerializer):
 | 
			
		||||
    su_from = serializers.SerializerMethodField(label=_('Su from'))
 | 
			
		||||
    secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type'))
 | 
			
		||||
    username = serializers.CharField(label=_('Username'), source='full_username', read_only=True)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Account
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ FILE_END_GUARD = ">>> Content End <<<"
 | 
			
		|||
celery_task_pre_key = "CELERY_"
 | 
			
		||||
KEY_CACHE_RESOURCE_IDS = "RESOURCE_IDS_{}"
 | 
			
		||||
 | 
			
		||||
# AD User AccountDisable
 | 
			
		||||
# DS User AccountDisable
 | 
			
		||||
# https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
 | 
			
		||||
LDAP_AD_ACCOUNT_DISABLE = 2
 | 
			
		||||
UUID_PATTERN = re.compile(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -138,4 +138,3 @@ if CONFIG.SYSLOG_ADDR != '' and len(CONFIG.SYSLOG_ADDR.split(':')) == 2:
 | 
			
		|||
 | 
			
		||||
if not os.path.isdir(LOG_DIR):
 | 
			
		||||
    os.makedirs(LOG_DIR, mode=0o755)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,8 +39,8 @@ class TerminalSettingSerializer(serializers.Serializer):
 | 
			
		|||
        help_text=_(
 | 
			
		||||
            '* Allow users to log in to the KoKo component via Public key authentication'
 | 
			
		||||
            '<br/>'
 | 
			
		||||
            'If third-party authentication services, such as AD/LDAP, are enabled, you should '
 | 
			
		||||
            'disable this option to prevent users from logging in after being deleted from the AD/LDAP server'
 | 
			
		||||
            'If third-party authentication services, such as DS/LDAP, are enabled, you should '
 | 
			
		||||
            'disable this option to prevent users from logging in after being deleted from the DS/LDAP server'
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -197,7 +197,7 @@ class LDAPServerUtil(object):
 | 
			
		|||
                    value = is_true(value)
 | 
			
		||||
 | 
			
		||||
            if attr == 'groups' and mapping.lower() == 'memberof':
 | 
			
		||||
                # AD: {'groups': 'memberOf'}
 | 
			
		||||
                # DS: {'groups': 'memberOf'}
 | 
			
		||||
                if isinstance(value, str) and value:
 | 
			
		||||
                    value = [value]
 | 
			
		||||
                if not isinstance(value, list):
 | 
			
		||||
| 
						 | 
				
			
			@ -366,7 +366,7 @@ class LDAPSyncUtil(object):
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
class LDAPImportUtil(object):
 | 
			
		||||
    user_group_name_prefix = 'AD '
 | 
			
		||||
    user_group_name_prefix = 'DS '
 | 
			
		||||
 | 
			
		||||
    def __init__(self, category=User.Source.ldap.value, is_sync_all=True):
 | 
			
		||||
        self.category = category
 | 
			
		||||
| 
						 | 
				
			
			@ -399,7 +399,7 @@ class LDAPImportUtil(object):
 | 
			
		|||
                continue
 | 
			
		||||
            if not isinstance(group, str):
 | 
			
		||||
                continue
 | 
			
		||||
            # get group name for AD, Such as: CN=Users,CN=Builtin,DC=jms,DC=com
 | 
			
		||||
            # get group name for DS, Such as: CN=Users,CN=Builtin,DC=jms,DC=com
 | 
			
		||||
            group_name = group.split(',')[0].split('=')[-1]
 | 
			
		||||
            group_name = f'{self.user_group_name_prefix}{group_name}'.strip()
 | 
			
		||||
            group_names.append(group_name)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,7 +81,7 @@ class Migration(migrations.Migration):
 | 
			
		|||
                 models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date password last updated')),
 | 
			
		||||
                ('need_update_password', models.BooleanField(default=False, verbose_name='Need update password')),
 | 
			
		||||
                ('source', models.CharField(
 | 
			
		||||
                    choices=[('local', 'Local'), ('ldap', 'LDAP/AD'), ('ldap_ha', 'LDAP/AD (HA)'), ('openid', 'OpenID'),
 | 
			
		||||
                    choices=[('local', 'Local'), ('ldap', 'LDAP/DS'), ('ldap_ha', 'LDAP/DS (HA)'), ('openid', 'OpenID'),
 | 
			
		||||
                             ('radius', 'Radius'), ('cas', 'CAS'), ('saml2', 'SAML2'), ('oauth2', 'OAuth2'),
 | 
			
		||||
                             ('wecom', 'WeCom'), ('dingtalk', 'DingTalk'), ('feishu', 'FeiShu'), ('lark', 'Lark'),
 | 
			
		||||
                             ('slack', 'Slack'), ('custom', 'Custom')], default='local', max_length=30,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,8 +9,8 @@ from django.utils.translation import gettext_lazy as _
 | 
			
		|||
 | 
			
		||||
class Source(models.TextChoices):
 | 
			
		||||
    local = "local", _("Local")
 | 
			
		||||
    ldap = "ldap", "LDAP/AD"
 | 
			
		||||
    ldap_ha = "ldap_ha", "LDAP/AD (HA)"
 | 
			
		||||
    ldap = "ldap", "LDAP/DS"
 | 
			
		||||
    ldap_ha = "ldap_ha", "LDAP/DS (HA)"
 | 
			
		||||
    openid = "openid", "OpenID"
 | 
			
		||||
    radius = "radius", "Radius"
 | 
			
		||||
    cas = "cas", "CAS"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -247,7 +247,7 @@ class UserSerializer(
 | 
			
		|||
            },
 | 
			
		||||
            "source": {
 | 
			
		||||
                "help_text": _(
 | 
			
		||||
                    "User source identifies where the user was created, which could be AD or other sources."
 | 
			
		||||
                    "User source identifies where the user was created, which could be DS or other sources."
 | 
			
		||||
                    "There are security settings that can restrict users to log in to the system only from the sources."
 | 
			
		||||
                ),
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ REDIS_PORT: 6379
 | 
			
		|||
# REDIS_DB_CELERY: 3
 | 
			
		||||
# REDIS_DB_CACHE: 4
 | 
			
		||||
 | 
			
		||||
# LDAP/AD settings
 | 
			
		||||
# LDAP/DS settings
 | 
			
		||||
# LDAP 搜索分页数量
 | 
			
		||||
# AUTH_LDAP_SEARCH_PAGED_SIZE: 1000
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue