perf: rename ad to ds

This commit is contained in:
ibuler 2025-04-07 19:10:12 +08:00 committed by 老广
parent 3f452daee8
commit acaa4cf2d5
35 changed files with 391 additions and 267 deletions

View File

@ -139,17 +139,25 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount, JSONFilterMixin):
return self.id return self.id
@lazyproperty @lazyproperty
def ad_domain(self): def ds_id(self):
if self.username.startswith('@'): if self.username.startswith('@'):
return None return None
if self.platform.category == 'ad': if self.platform.category == 'ds':
return self.asset.ad.domain_name 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 return None
@lazyproperty @lazyproperty
def full_username(self): def full_username(self):
if self.ad_domain: if self.ds_domain:
return '{}@{}'.format(self.username, self.ad_domain) return '{}@{}'.format(self.username, self.ds_domain)
return self.username return self.username
@lazyproperty @lazyproperty

View File

@ -241,7 +241,7 @@ class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerialize
'date_change_secret', 'change_secret_status' 'date_change_secret', 'change_secret_status'
] ]
fields = BaseAccountSerializer.Meta.fields + [ fields = BaseAccountSerializer.Meta.fields + [
'su_from', 'asset', 'version', "ad_domain", 'su_from', 'asset', 'version', 'ds_domain',
'source', 'source_id', 'secret_reset', 'source', 'source_id', 'secret_reset',
] + AccountCreateUpdateSerializerMixin.Meta.fields + automation_fields ] + AccountCreateUpdateSerializerMixin.Meta.fields + automation_fields
read_only_fields = BaseAccountSerializer.Meta.read_only_fields + automation_fields read_only_fields = BaseAccountSerializer.Meta.read_only_fields + automation_fields

View File

@ -87,7 +87,7 @@ class BaseAccountSerializer(
"username": { "username": {
"help_text": _( "help_text": _(
"* If no username is required for authentication, enter null. " "* 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."
) )
}, },
} }

View File

@ -21,7 +21,7 @@ class VirtualAccountSerializer(serializers.ModelSerializer):
'username': {'label': _('Username')}, 'username': {'label': _('Username')},
'secret_from_login': { 'secret_from_login': {
'help_text': _( '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/ >' 'Same account in asset secret > Login secret > Manual input. <br/ >'
'For security, please set config CACHE_LOGIN_PASSWORD_ENABLED to true' 'For security, please set config CACHE_LOGIN_PASSWORD_ENABLED to true'
) )

View File

@ -3,8 +3,8 @@ from .cloud import *
from .custom import * from .custom import *
from .database import * from .database import *
from .device import * from .device import *
from .ds import *
from .gpt import * from .gpt import *
from .host import * from .host import *
from .permission import * from .permission import *
from .web import * from .web import *
from .ad import *

View File

@ -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

View File

@ -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

View File

@ -112,8 +112,7 @@ class BaseType(TextChoices):
@classmethod @classmethod
def get_choices(cls): def get_choices(cls):
if not settings.XPACK_LICENSE_IS_VALID: 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()] choices = [(tp.value, tp.label) for tp in cls.get_community_types()]
else:
choices = cls.choices
return choices return choices

View File

@ -12,7 +12,7 @@ class Category(ChoicesMixin, models.TextChoices):
DATABASE = 'database', _("Database") DATABASE = 'database', _("Database")
CLOUD = 'cloud', _("Cloud service") CLOUD = 'cloud', _("Cloud service")
WEB = 'web', _("Web") WEB = 'web', _("Web")
AD = 'ad', _("Active Directory") DS = 'ds', _("Directory service")
CUSTOM = 'custom', _("Custom type") CUSTOM = 'custom', _("Custom type")
@classmethod @classmethod

View File

@ -20,7 +20,7 @@ class DeviceTypes(BaseType):
'*': { '*': {
'charset_enabled': False, 'charset_enabled': False,
'domain_enabled': True, 'domain_enabled': True,
'ad_enabled': False, 'ds_enabled': False,
'su_enabled': True, 'su_enabled': True,
'su_methods': ['enable', 'super', 'super_level'] 'su_methods': ['enable', 'super', 'super_level']
} }

View File

@ -3,11 +3,13 @@ from django.utils.translation import gettext_lazy as _
from .base import BaseType from .base import BaseType
class ADTypes(BaseType): class DirectoryTypes(BaseType):
AD = 'ad', _('Active Directory') GENERAL = 'general', _('General')
# LDAP = 'ldap', _('LDAP')
# AD = 'ad', _('Active Directory')
WINDOWS_AD = 'windows_ad', _('Windows 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 @classmethod
def _get_base_constrains(cls) -> dict: def _get_base_constrains(cls) -> dict:
@ -15,7 +17,7 @@ class ADTypes(BaseType):
'*': { '*': {
'charset_enabled': False, 'charset_enabled': False,
'domain_enabled': True, 'domain_enabled': True,
'ad_enabled': False, 'ds_enabled': False,
'su_enabled': True, 'su_enabled': True,
} }
} }
@ -24,6 +26,9 @@ class ADTypes(BaseType):
def _get_automation_constrains(cls) -> dict: def _get_automation_constrains(cls) -> dict:
constrains = { constrains = {
'*': { '*': {
'ansible_enabled': False,
},
cls.WINDOWS_AD: {
'ansible_enabled': True, 'ansible_enabled': True,
'ping_enabled': True, 'ping_enabled': True,
'gather_facts_enabled': False, 'gather_facts_enabled': False,
@ -38,36 +43,24 @@ class ADTypes(BaseType):
@classmethod @classmethod
def _get_protocol_constrains(cls) -> dict: def _get_protocol_constrains(cls) -> dict:
return { return {
cls.GENERAL: {
'choices': ['ssh']
},
cls.WINDOWS_AD: { cls.WINDOWS_AD: {
'choices': ['rdp', 'ssh', 'vnc', 'winrm'] 'choices': ['rdp', 'ssh', 'vnc', 'winrm']
}, },
cls.LDAP: {
'choices': ['ssh', 'ldap']
},
cls.AZURE_AD: {
'choices': ['ldap']
}
} }
@classmethod @classmethod
def internal_platforms(cls): def internal_platforms(cls):
return { return {
cls.AD: [
{'name': 'Active Directory'}
],
cls.WINDOWS_AD: [ cls.WINDOWS_AD: [
{'name': 'Windows Active Directory'} {'name': 'Windows Active Directory'}
], ],
cls.LDAP: [
{'name': 'LDAP'}
],
cls.AZURE_AD: [
{'name': 'Azure Active Directory'}
],
} }
@classmethod @classmethod
def get_community_types(cls): def get_community_types(cls):
return [ return [
cls.LDAP, cls.GENERAL,
] ]

View File

@ -20,7 +20,7 @@ class HostTypes(BaseType):
'charset': 'utf-8', # default 'charset': 'utf-8', # default
'domain_enabled': True, 'domain_enabled': True,
'su_enabled': True, 'su_enabled': True,
'ad_enabled': True, 'ds_enabled': True,
'su_methods': ['sudo', 'su', 'only_sudo', 'only_su'], 'su_methods': ['sudo', 'su', 'only_sudo', 'only_su'],
}, },
cls.WINDOWS: { cls.WINDOWS: {

View File

@ -13,10 +13,10 @@ from .cloud import CloudTypes
from .custom import CustomTypes from .custom import CustomTypes
from .database import DatabaseTypes from .database import DatabaseTypes
from .device import DeviceTypes from .device import DeviceTypes
from .ds import DirectoryTypes
from .gpt import GPTTypes from .gpt import GPTTypes
from .host import HostTypes from .host import HostTypes
from .web import WebTypes from .web import WebTypes
from .ad import ADTypes
class AllTypes(ChoicesMixin): class AllTypes(ChoicesMixin):
@ -24,7 +24,7 @@ class AllTypes(ChoicesMixin):
includes = [ includes = [
HostTypes, DeviceTypes, DatabaseTypes, HostTypes, DeviceTypes, DatabaseTypes,
CloudTypes, WebTypes, CustomTypes, CloudTypes, WebTypes, CustomTypes,
ADTypes, GPTTypes DirectoryTypes, GPTTypes
] ]
_category_constrains = {} _category_constrains = {}
_automation_methods = None _automation_methods = None
@ -175,7 +175,7 @@ class AllTypes(ChoicesMixin):
(Category.DATABASE, DatabaseTypes), (Category.DATABASE, DatabaseTypes),
(Category.WEB, WebTypes), (Category.WEB, WebTypes),
(Category.CLOUD, CloudTypes), (Category.CLOUD, CloudTypes),
(Category.AD, ADTypes), (Category.DS, DirectoryTypes),
(Category.CUSTOM, CustomTypes) (Category.CUSTOM, CustomTypes)
] ]
return types return types

View File

@ -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"),
),
]

View File

@ -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"),
),
]

View File

@ -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",
),
),
]

View File

@ -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)
]

View File

@ -1,9 +1,9 @@
from .ad import *
from .cloud import * from .cloud import *
from .common import * from .common import *
from .custom import * from .custom import *
from .database import * from .database import *
from .device import * from .device import *
from .ds import *
from .gpt import * from .gpt import *
from .host import * from .host import *
from .web import * from .web import *

View File

@ -247,10 +247,10 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
@property @property
def all_accounts(self): def all_accounts(self):
if not self.joined_ad_id: if not self.joined_dir_svc_id:
queryset = self.accounts.all() queryset = self.accounts.all()
else: 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 return queryset
@lazyproperty @lazyproperty
@ -273,15 +273,15 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin,
protocol = self.protocols.all().filter(name=protocol).first() protocol = self.protocols.all().filter(name=protocol).first()
return protocol.port if protocol else 0 return protocol.port if protocol else 0
def is_ad(self): def is_dir_svc(self):
return self.category == const.Category.AD return self.category == const.Category.DS
@property @property
def joined_ad_id(self): def joined_dir_svc_id(self):
return self.platform.ad_id return self.platform.ds_id
def is_joined_ad(self): def is_joined_ad(self):
if self.joined_ad_id: if self.joined_dir_svc_id:
return True return True
else: else:
return False return False

View File

@ -3,11 +3,11 @@ from django.utils.translation import gettext_lazy as _
from .common import Asset 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")) domain_name = models.CharField(max_length=128, blank=True, default='', verbose_name=_("Domain name"))
class Meta: class Meta:
verbose_name = _("Active Directory") verbose_name = _("Directory service")

View File

@ -102,10 +102,10 @@ class Platform(LabeledMixin, JMSBaseModel):
max_length=8, verbose_name=_("Charset") max_length=8, verbose_name=_("Charset")
) )
domain_enabled = models.BooleanField(default=True, verbose_name=_("Gateway enabled")) domain_enabled = models.BooleanField(default=True, verbose_name=_("Gateway enabled"))
ad_enabled = models.BooleanField(default=False, verbose_name=_("AD enabled")) ds_enabled = models.BooleanField(default=False, verbose_name=_("DS enabled"))
ad = models.ForeignKey( ds = models.ForeignKey(
'assets.AD', on_delete=models.SET_NULL, null=True, blank=True, 'DirectoryService', on_delete=models.SET_NULL, null=True, blank=True,
verbose_name=_("Active Directory"), related_name='ad_platforms' verbose_name=_("Directory service"), related_name='ds_platforms'
) )
# 账号有关的 # 账号有关的
su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled")) su_enabled = models.BooleanField(default=False, verbose_name=_("Su enabled"))
@ -121,8 +121,8 @@ class Platform(LabeledMixin, JMSBaseModel):
return self.assets.count() return self.assets.count()
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.ad_enabled: if not self.ds_enabled:
self.ad = None self.ds = None
super().save(*args, **kwargs) super().save(*args, **kwargs)
@classmethod @classmethod

View File

@ -4,7 +4,7 @@ from .common import *
from .custom import * from .custom import *
from .database import * from .database import *
from .device import * from .device import *
from .ds import *
from .gpt import * from .gpt import *
from .host import * from .host import *
from .web import * from .web import *
from .ad import *

View File

@ -1,15 +1,14 @@
from django.utils.translation import gettext_lazy as _ 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 from .common import AssetSerializer
__all__ = ['ADSerializer'] __all__ = ['DSSerializer']
class ADSerializer(AssetSerializer): class DSSerializer(AssetSerializer):
class Meta(AssetSerializer.Meta): class Meta(AssetSerializer.Meta):
model = AD model = DirectoryService
fields = AssetSerializer.Meta.fields + [ fields = AssetSerializer.Meta.fields + [
'domain_name', 'domain_name',
] ]

View File

@ -194,8 +194,8 @@ class PlatformSerializer(ResourceLabelsMixin, CommonSerializerMixin, WritableNes
] ]
fields_m2m = ['assets', 'assets_amount'] fields_m2m = ['assets', 'assets_amount']
fields = fields_small + fields_m2m + [ fields = fields_small + fields_m2m + [
"protocols", "domain_enabled", "su_enabled", "su_method", "ad_enabled", "ad", "protocols", "domain_enabled", "su_enabled", "su_method",
"automation", "comment", "custom_fields", "labels" "ds_enabled", "ds", "automation", "comment", "custom_fields", "labels"
] + read_only_fields ] + read_only_fields
extra_kwargs = { extra_kwargs = {
"su_enabled": { "su_enabled": {

View File

@ -16,7 +16,7 @@ router.register(r'databases', api.DatabaseViewSet, 'database')
router.register(r'webs', api.WebViewSet, 'web') router.register(r'webs', api.WebViewSet, 'web')
router.register(r'clouds', api.CloudViewSet, 'cloud') router.register(r'clouds', api.CloudViewSet, 'cloud')
router.register(r'gpts', api.GPTViewSet, 'gpt') 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'customs', api.CustomViewSet, 'custom')
router.register(r'platforms', api.AssetPlatformViewSet, 'platform') router.register(r'platforms', api.AssetPlatformViewSet, 'platform')
router.register(r'nodes', api.NodeViewSet, 'node') router.register(r'nodes', api.NodeViewSet, 'node')

View File

@ -8,7 +8,6 @@ from django.db import models
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.forms.models import model_to_dict
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from accounts.models import VirtualAccount from accounts.models import VirtualAccount
@ -267,7 +266,7 @@ class ConnectionToken(JMSOrgBaseModel):
input_secret=self.input_secret, from_permed=False input_secret=self.input_secret, from_permed=False
) )
else: 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: if not account.secret and self.input_secret:
account.secret = self.input_secret account.secret = self.input_secret
return account return account

View File

@ -40,6 +40,7 @@ class _ConnectionTokenAssetSerializer(serializers.ModelSerializer):
class _SimpleAccountSerializer(serializers.ModelSerializer): class _SimpleAccountSerializer(serializers.ModelSerializer):
secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type')) secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type'))
username = serializers.CharField(label=_('Username'), source='full_username', read_only=True)
class Meta: class Meta:
model = Account model = Account
@ -49,6 +50,7 @@ class _SimpleAccountSerializer(serializers.ModelSerializer):
class _ConnectionTokenAccountSerializer(serializers.ModelSerializer): class _ConnectionTokenAccountSerializer(serializers.ModelSerializer):
su_from = serializers.SerializerMethodField(label=_('Su from')) su_from = serializers.SerializerMethodField(label=_('Su from'))
secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type')) secret_type = LabeledChoiceField(choices=SecretType.choices, required=False, label=_('Secret type'))
username = serializers.CharField(label=_('Username'), source='full_username', read_only=True)
class Meta: class Meta:
model = Account model = Account

View File

@ -8,7 +8,7 @@ FILE_END_GUARD = ">>> Content End <<<"
celery_task_pre_key = "CELERY_" celery_task_pre_key = "CELERY_"
KEY_CACHE_RESOURCE_IDS = "RESOURCE_IDS_{}" 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 # https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
LDAP_AD_ACCOUNT_DISABLE = 2 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}') 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}')

View File

@ -121,8 +121,8 @@ LOGGING = {
if CONFIG.DEBUG_DEV: if CONFIG.DEBUG_DEV:
LOGGING['loggers']['django.db'] = { LOGGING['loggers']['django.db'] = {
'handlers': ['console', 'file'], 'handlers': ['console', 'file'],
'level': 'DEBUG' 'level': 'DEBUG'
} }
SYSLOG_ENABLE = CONFIG.SYSLOG_ENABLE SYSLOG_ENABLE = CONFIG.SYSLOG_ENABLE
@ -138,4 +138,3 @@ if CONFIG.SYSLOG_ADDR != '' and len(CONFIG.SYSLOG_ADDR.split(':')) == 2:
if not os.path.isdir(LOG_DIR): if not os.path.isdir(LOG_DIR):
os.makedirs(LOG_DIR, mode=0o755) os.makedirs(LOG_DIR, mode=0o755)

View File

@ -20,7 +20,7 @@ class TerminalSettingSerializer(serializers.Serializer):
) )
SECURITY_SERVICE_ACCOUNT_REGISTRATION = serializers.ChoiceField( SECURITY_SERVICE_ACCOUNT_REGISTRATION = serializers.ChoiceField(
choices=[ choices=[
('auto', _('Auto(Enabled for the first 5 minutes after startup, then disabled.)')), ('auto', _('Auto(Enabled for the first 5 minutes after startup, then disabled.)')),
(True, _('Enable')), (False, _('Disable')) (True, _('Enable')), (False, _('Disable'))
], ],
required=True, label=_('Registration'), required=True, label=_('Registration'),
@ -29,7 +29,7 @@ class TerminalSettingSerializer(serializers.Serializer):
) )
) )
TERMINAL_PASSWORD_AUTH = serializers.BooleanField( TERMINAL_PASSWORD_AUTH = serializers.BooleanField(
required=False, label=_("Password"), required=False, label=_("Password"),
help_text=_( help_text=_(
'* Allow users to log in to the KoKo component via password authentication' '* Allow users to log in to the KoKo component via password authentication'
) )
@ -39,8 +39,8 @@ class TerminalSettingSerializer(serializers.Serializer):
help_text=_( help_text=_(
'* Allow users to log in to the KoKo component via Public key authentication' '* Allow users to log in to the KoKo component via Public key authentication'
'<br/>' '<br/>'
'If third-party authentication services, such as AD/LDAP, are enabled, you should ' '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 AD/LDAP server' 'disable this option to prevent users from logging in after being deleted from the DS/LDAP server'
) )
) )
TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField( TERMINAL_ASSET_LIST_SORT_BY = serializers.ChoiceField(
@ -50,7 +50,7 @@ class TerminalSettingSerializer(serializers.Serializer):
PAGE_SIZE_CHOICES, required=False, label=_('Asset page size') PAGE_SIZE_CHOICES, required=False, label=_('Asset page size')
) )
TERMINAL_MAGNUS_ENABLED = serializers.BooleanField( TERMINAL_MAGNUS_ENABLED = serializers.BooleanField(
label="Magnus", label="Magnus",
help_text=_( help_text=_(
'* You can individually configure the service address and port in the service endpoint' '* You can individually configure the service address and port in the service endpoint'
'<br/>' '<br/>'

View File

@ -197,7 +197,7 @@ class LDAPServerUtil(object):
value = is_true(value) value = is_true(value)
if attr == 'groups' and mapping.lower() == 'memberof': if attr == 'groups' and mapping.lower() == 'memberof':
# AD: {'groups': 'memberOf'} # DS: {'groups': 'memberOf'}
if isinstance(value, str) and value: if isinstance(value, str) and value:
value = [value] value = [value]
if not isinstance(value, list): if not isinstance(value, list):
@ -366,7 +366,7 @@ class LDAPSyncUtil(object):
class LDAPImportUtil(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): def __init__(self, category=User.Source.ldap.value, is_sync_all=True):
self.category = category self.category = category
@ -399,7 +399,7 @@ class LDAPImportUtil(object):
continue continue
if not isinstance(group, str): if not isinstance(group, str):
continue 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 = group.split(',')[0].split('=')[-1]
group_name = f'{self.user_group_name_prefix}{group_name}'.strip() group_name = f'{self.user_group_name_prefix}{group_name}'.strip()
group_names.append(group_name) group_names.append(group_name)

View File

@ -81,7 +81,7 @@ class Migration(migrations.Migration):
models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date password last updated')), 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')), ('need_update_password', models.BooleanField(default=False, verbose_name='Need update password')),
('source', models.CharField( ('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'), ('radius', 'Radius'), ('cas', 'CAS'), ('saml2', 'SAML2'), ('oauth2', 'OAuth2'),
('wecom', 'WeCom'), ('dingtalk', 'DingTalk'), ('feishu', 'FeiShu'), ('lark', 'Lark'), ('wecom', 'WeCom'), ('dingtalk', 'DingTalk'), ('feishu', 'FeiShu'), ('lark', 'Lark'),
('slack', 'Slack'), ('custom', 'Custom')], default='local', max_length=30, ('slack', 'Slack'), ('custom', 'Custom')], default='local', max_length=30,

View File

@ -9,8 +9,8 @@ from django.utils.translation import gettext_lazy as _
class Source(models.TextChoices): class Source(models.TextChoices):
local = "local", _("Local") local = "local", _("Local")
ldap = "ldap", "LDAP/AD" ldap = "ldap", "LDAP/DS"
ldap_ha = "ldap_ha", "LDAP/AD (HA)" ldap_ha = "ldap_ha", "LDAP/DS (HA)"
openid = "openid", "OpenID" openid = "openid", "OpenID"
radius = "radius", "Radius" radius = "radius", "Radius"
cas = "cas", "CAS" cas = "cas", "CAS"

View File

@ -247,7 +247,7 @@ class UserSerializer(
}, },
"source": { "source": {
"help_text": _( "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." "There are security settings that can restrict users to log in to the system only from the sources."
), ),
}, },

View File

@ -53,7 +53,7 @@ REDIS_PORT: 6379
# REDIS_DB_CELERY: 3 # REDIS_DB_CELERY: 3
# REDIS_DB_CACHE: 4 # REDIS_DB_CACHE: 4
# LDAP/AD settings # LDAP/DS settings
# LDAP 搜索分页数量 # LDAP 搜索分页数量
# AUTH_LDAP_SEARCH_PAGED_SIZE: 1000 # AUTH_LDAP_SEARCH_PAGED_SIZE: 1000
# #
@ -99,4 +99,4 @@ REDIS_PORT: 6379
# 开启人脸识别 XPACK 功能 # 开启人脸识别 XPACK 功能
#FACE_RECOGNITION_ENABLED: true #FACE_RECOGNITION_ENABLED: true
#FACE_RECOGNITION_DISTANCE_THRESHOLD': 0.35 #FACE_RECOGNITION_DISTANCE_THRESHOLD': 0.35
#FACE_RECOGNITION_COSINE_THRESHOLD': 0.95 #FACE_RECOGNITION_COSINE_THRESHOLD': 0.95