예제 #1
0
class AuthSerializer(serializers.ModelSerializer):
    password = EncryptedField(required=False,
                              allow_blank=True,
                              allow_null=True,
                              max_length=1024,
                              label=_('Password'))
    private_key = EncryptedField(required=False,
                                 allow_blank=True,
                                 allow_null=True,
                                 max_length=4096,
                                 label=_('Private key'))

    def gen_keys(self, private_key=None, password=None):
        if private_key is None:
            return None, None
        public_key = ssh_pubkey_gen(private_key=private_key, password=password)
        return private_key, public_key

    def save(self, **kwargs):
        password = self.validated_data.pop('password', None) or None
        private_key = self.validated_data.pop('private_key', None) or None
        self.instance = super().save(**kwargs)
        if password or private_key:
            private_key, public_key = self.gen_keys(private_key, password)
            self.instance.set_auth(password=password,
                                   private_key=private_key,
                                   public_key=public_key)
        return self.instance
예제 #2
0
class AuthSerializerMixin(serializers.ModelSerializer):
    password = EncryptedField(label=_('Password'),
                              required=False,
                              allow_blank=True,
                              allow_null=True,
                              max_length=1024,
                              validators=[validate_password_for_ansible])
    private_key = EncryptedField(label=_('SSH private key'),
                                 required=False,
                                 allow_blank=True,
                                 allow_null=True,
                                 max_length=4096)
    passphrase = serializers.CharField(allow_blank=True,
                                       allow_null=True,
                                       required=False,
                                       max_length=512,
                                       write_only=True,
                                       label=_('Key password'))

    def validate_password(self, password):
        return password

    def validate_private_key(self, private_key):
        if not private_key:
            return
        passphrase = self.initial_data.get('passphrase')
        passphrase = passphrase if passphrase else None
        valid = validate_ssh_private_key(private_key, password=passphrase)
        if not valid:
            raise serializers.ValidationError(
                _("private key invalid or passphrase error"))

        private_key = ssh_private_key_gen(private_key, password=passphrase)
        string_io = StringIO()
        private_key.write_private_key(string_io)
        private_key = string_io.getvalue()
        return private_key

    def validate_public_key(self, public_key):
        return public_key

    @staticmethod
    def clean_auth_fields(validated_data):
        for field in ('password', 'private_key', 'public_key'):
            value = validated_data.get(field)
            if not value:
                validated_data.pop(field, None)
        validated_data.pop('passphrase', None)

    def create(self, validated_data):
        self.clean_auth_fields(validated_data)
        return super().create(validated_data)

    def update(self, instance, validated_data):
        self.clean_auth_fields(validated_data)
        return super().update(instance, validated_data)
예제 #3
0
class VMwareClientSerializer(RemoteAppSerializer):
    PATH = r'''
    C:\Program Files (x86)\VMware\Infrastructure\Virtual Infrastructure Client\Launcher\VpxClient
    .exe
    '''
    VMWARE_CLIENT_PATH = ''.join(PATH.split())

    path = serializers.CharField(max_length=128,
                                 label=_('Application path'),
                                 default=VMWARE_CLIENT_PATH,
                                 allow_null=True)
    vmware_target = serializers.CharField(max_length=128,
                                          allow_blank=True,
                                          required=False,
                                          label=_('Target URL'),
                                          allow_null=True)
    vmware_username = serializers.CharField(max_length=128,
                                            allow_blank=True,
                                            required=False,
                                            label=_('Vmware username'),
                                            allow_null=True)
    vmware_password = EncryptedField(max_length=128,
                                     allow_blank=True,
                                     required=False,
                                     label=_('Vmware password'),
                                     allow_null=True)
예제 #4
0
class CustomSerializer(RemoteAppSerializer):
    custom_cmdline = serializers.CharField(
        max_length=128,
        allow_blank=True,
        required=False,
        label=_('Operating parameter'),
        allow_null=True,
    )
    custom_target = serializers.CharField(
        max_length=128,
        allow_blank=True,
        required=False,
        label=_('Target url'),
        allow_null=True,
    )
    custom_username = serializers.CharField(
        max_length=128,
        allow_blank=True,
        required=False,
        label=_('Custom Username'),
        allow_null=True,
    )
    custom_password = EncryptedField(
        max_length=128,
        allow_blank=True,
        required=False,
        label=_('Custom password'),
        allow_null=True,
    )
예제 #5
0
class MySQLWorkbenchSerializer(RemoteAppSerializer):
    MYSQL_WORKBENCH_PATH = 'C:\Program Files\MySQL\MySQL Workbench 8.0 CE\MySQLWorkbench.exe'

    path = serializers.CharField(
        max_length=128, label=_('Application path'), default=MYSQL_WORKBENCH_PATH,
        allow_null=True,
    )
    mysql_workbench_ip = serializers.CharField(
        max_length=128, allow_blank=True, required=False, label=_('IP'),
        allow_null=True,
    )
    mysql_workbench_port = serializers.IntegerField(
        required=False, label=_('Port'),
        allow_null=True,
    )
    mysql_workbench_name = serializers.CharField(
        max_length=128, allow_blank=True, required=False, label=_('Database'),
        allow_null=True,
    )
    mysql_workbench_username = serializers.CharField(
        max_length=128, allow_blank=True, required=False, label=_('Mysql workbench username'),
        allow_null=True,
    )
    mysql_workbench_password = EncryptedField(
        max_length=128, allow_blank=True, required=False,
        label=_('Mysql workbench password'), allow_null=True,
    )
예제 #6
0
class EmailSettingSerializer(serializers.Serializer):
    # encrypt_fields 现在使用 write_only 来判断了

    EMAIL_HOST = serializers.CharField(max_length=1024, required=True, label=_("SMTP host"))
    EMAIL_PORT = serializers.CharField(max_length=5, required=True, label=_("SMTP port"))
    EMAIL_HOST_USER = serializers.CharField(max_length=128, required=True, label=_("SMTP account"))
    EMAIL_HOST_PASSWORD = EncryptedField(
        max_length=1024, required=False, label=_("SMTP password"),
        help_text=_("Tips: Some provider use token except password")
    )
    EMAIL_FROM = serializers.CharField(
        max_length=128, allow_blank=True, required=False, label=_('Send user'),
        help_text=_('Tips: Send mail account, default SMTP account as the send account')
    )
    EMAIL_RECIPIENT = serializers.CharField(
        max_length=128, allow_blank=True, required=False, label=_('Test recipient'),
        help_text=_('Tips: Used only as a test mail recipient')
    )
    EMAIL_USE_SSL = serializers.BooleanField(
        required=False, label=_('Use SSL'),
        help_text=_('If SMTP port is 465, may be select')
    )
    EMAIL_USE_TLS = serializers.BooleanField(
        required=False, label=_("Use TLS"),
        help_text=_('If SMTP port is 587, may be select')
    )
    EMAIL_SUBJECT_PREFIX = serializers.CharField(
        max_length=1024, required=True, label=_('Subject prefix')
    )
예제 #7
0
class CommonSettingSerializer(serializers.Serializer):
    # OpenID 公有配置参数 (version <= 1.5.8 或 version >= 1.5.8)
    BASE_SITE_URL = serializers.CharField(
        required=False, allow_null=True, max_length=1024, label=_('Base site url')
    )
    AUTH_OPENID_CLIENT_ID = serializers.CharField(
        required=False, max_length=1024, label=_('Client Id')
    )
    AUTH_OPENID_CLIENT_SECRET = EncryptedField(
        required=False, max_length=1024, label=_('Client Secret')
    )
    AUTH_OPENID_CLIENT_AUTH_METHOD = serializers.ChoiceField(
        default='client_secret_basic',
        choices=(
            ('client_secret_basic', 'Client Secret Basic'),
            ('client_secret_post', 'Client Secret Post')
        ),
        label=_('Client authentication method')
    )
    AUTH_OPENID_SHARE_SESSION = serializers.BooleanField(required=False, label=_('Share session'))
    AUTH_OPENID_IGNORE_SSL_VERIFICATION = serializers.BooleanField(
        required=False, label=_('Ignore ssl verification')
    )
    AUTH_OPENID_USER_ATTR_MAP = serializers.DictField(
        required=True, label=_('User attr map'),
        help_text=_('User attr map present how to map OpenID user attr to '
                    'jumpserver, username,name,email is jumpserver attr')
    )
예제 #8
0
class VMwareClientSecretSerializer(RemoteAppSerializer):
    vmware_password = EncryptedField(max_length=128,
                                     allow_blank=True,
                                     required=False,
                                     write_only=False,
                                     label=_('Vmware password'),
                                     allow_null=True)
예제 #9
0
class RadiusSettingSerializer(serializers.Serializer):
    AUTH_RADIUS = serializers.BooleanField(required=False, label=_('Enable Radius Auth'))
    RADIUS_SERVER = serializers.CharField(required=False, allow_blank=True, max_length=1024, label=_('Host'))
    RADIUS_PORT = serializers.IntegerField(required=False, label=_('Port'))
    RADIUS_SECRET = EncryptedField(
        required=False, max_length=1024, allow_null=True, label=_('Secret'),
    )
    OTP_IN_RADIUS = serializers.BooleanField(required=False, label=_('OTP in Radius'))
예제 #10
0
class FeiShuSettingSerializer(serializers.Serializer):
    FEISHU_APP_ID = serializers.CharField(max_length=256,
                                          required=True,
                                          label='App ID')
    FEISHU_APP_SECRET = EncryptedField(max_length=256,
                                       required=False,
                                       label='App Secret')
    AUTH_FEISHU = serializers.BooleanField(default=False,
                                           label=_('Enable FeiShu Auth'))
예제 #11
0
class CustomSecretSerializer(RemoteAppSerializer):
    custom_password = EncryptedField(
        max_length=128,
        allow_blank=True,
        required=False,
        write_only=False,
        label=_('Custom password'),
        allow_null=True,
    )
예제 #12
0
class LDAPTestConfigSerializer(serializers.Serializer):
    AUTH_LDAP_SERVER_URI = serializers.CharField(max_length=1024)
    AUTH_LDAP_BIND_DN = serializers.CharField(max_length=1024, required=False, allow_blank=True)
    AUTH_LDAP_BIND_PASSWORD = EncryptedField(required=False, allow_blank=True)
    AUTH_LDAP_SEARCH_OU = serializers.CharField()
    AUTH_LDAP_SEARCH_FILTER = serializers.CharField()
    AUTH_LDAP_USER_ATTR_MAP = serializers.CharField()
    AUTH_LDAP_START_TLS = serializers.BooleanField(required=False)
    AUTH_LDAP = serializers.BooleanField(required=False)
예제 #13
0
class WeComSettingSerializer(serializers.Serializer):
    WECOM_CORPID = serializers.CharField(max_length=256,
                                         required=True,
                                         label='corpid')
    WECOM_AGENTID = serializers.CharField(max_length=256,
                                          required=True,
                                          label='agentid')
    WECOM_SECRET = EncryptedField(max_length=256,
                                  required=False,
                                  label='secret')
    AUTH_WECOM = serializers.BooleanField(default=False,
                                          label=_('Enable WeCom Auth'))
예제 #14
0
class DingTalkSettingSerializer(serializers.Serializer):
    DINGTALK_AGENTID = serializers.CharField(max_length=256,
                                             required=True,
                                             label='AgentId')
    DINGTALK_APPKEY = serializers.CharField(max_length=256,
                                            required=True,
                                            label='AppKey')
    DINGTALK_APPSECRET = EncryptedField(max_length=256,
                                        required=False,
                                        label='AppSecret')
    AUTH_DINGTALK = serializers.BooleanField(default=False,
                                             label=_('Enable DingTalk Auth'))
예제 #15
0
class UserUpdateSecretKeySerializer(serializers.ModelSerializer):
    new_secret_key = EncryptedField(required=True, max_length=128)
    new_secret_key_again = EncryptedField(required=True, max_length=128)

    class Meta:
        model = User
        fields = ['new_secret_key', 'new_secret_key_again']

    def validate(self, values):
        new_secret_key = values.get('new_secret_key', '')
        new_secret_key_again = values.get('new_secret_key_again', '')
        if new_secret_key != new_secret_key_again:
            msg = _('The newly set password is inconsistent')
            raise serializers.ValidationError({'new_secret_key_again': msg})
        return values

    def update(self, instance, validated_data):
        new_secret_key = self.validated_data.get('new_secret_key')
        instance.secret_key = new_secret_key
        instance.save()
        return instance
예제 #16
0
class UserUpdatePasswordSerializer(serializers.ModelSerializer):
    old_password = EncryptedField(required=True, max_length=128)
    new_password = EncryptedField(required=True, max_length=128)
    new_password_again = EncryptedField(required=True, max_length=128)

    class Meta:
        model = User
        fields = ['old_password', 'new_password', 'new_password_again']

    def validate_old_password(self, value):
        if not self.instance.check_password(value):
            msg = _('The old password is incorrect')
            raise serializers.ValidationError(msg)
        return value

    def validate_new_password(self, value):
        from ..utils import check_password_rules
        if not check_password_rules(value,
                                    is_org_admin=self.instance.is_org_admin):
            msg = _('Password does not match security rules')
            raise serializers.ValidationError(msg)
        if self.instance.is_history_password(value):
            limit_count = settings.OLD_PASSWORD_HISTORY_LIMIT_COUNT
            msg = _('The new password cannot be the last {} passwords').format(
                limit_count)
            raise serializers.ValidationError(msg)
        return value

    def validate(self, values):
        new_password = values.get('new_password', '')
        new_password_again = values.get('new_password_again', '')
        if new_password != new_password_again:
            msg = _('The newly set password is inconsistent')
            raise serializers.ValidationError({'new_password_again': msg})
        return values

    def update(self, instance, validated_data):
        new_password = self.validated_data.get('new_password')
        instance.reset_password(new_password)
        return instance
예제 #17
0
class ReplayStorageTypeAzureSerializer(serializers.Serializer):
    class EndpointSuffixChoices(TextChoices):
        china = 'core.chinacloudapi.cn', 'core.chinacloudapi.cn'
        international = 'core.windows.net', 'core.windows.net'

    CONTAINER_NAME = serializers.CharField(
        max_length=1024, label=_('Container name'), allow_null=True
    )
    ACCOUNT_NAME = serializers.CharField(max_length=1024, label=_('Account name'), allow_null=True)
    ACCOUNT_KEY = EncryptedField(max_length=1024, label=_('Account key'), allow_null=True)
    ENDPOINT_SUFFIX = serializers.ChoiceField(
        choices=EndpointSuffixChoices.choices, default=EndpointSuffixChoices.china.value,
        label=_('Endpoint suffix'), allow_null=True,
    )
예제 #18
0
class LDAPSettingSerializer(serializers.Serializer):
    # encrypt_fields 现在使用 write_only 来判断了

    AUTH_LDAP_SERVER_URI = serializers.CharField(
        required=True, max_length=1024, label=_('LDAP server'),
        help_text=_('eg: ldap://localhost:389')
    )
    AUTH_LDAP_BIND_DN = serializers.CharField(required=False, max_length=1024, label=_('Bind DN'))
    AUTH_LDAP_BIND_PASSWORD = EncryptedField(
        max_length=1024, required=False, label=_('Password')
    )
    AUTH_LDAP_SEARCH_OU = serializers.CharField(
        max_length=1024, allow_blank=True, required=False, label=_('User OU'),
        help_text=_('Use | split multi OUs')
    )
    AUTH_LDAP_SEARCH_FILTER = serializers.CharField(
        max_length=1024, required=True, label=_('User search filter'),
        help_text=_('Choice may be (cn|uid|sAMAccountName)=%(user)s)')
    )
    AUTH_LDAP_USER_ATTR_MAP = serializers.DictField(
        required=True, label=_('User attr map'),
        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(
        required=False, label=_('Organization'), max_length=36
    )
    AUTH_LDAP_SYNC_IS_PERIODIC = serializers.BooleanField(
        required=False, label=_('Periodic perform')
    )
    AUTH_LDAP_SYNC_CRONTAB = serializers.CharField(
        required=False, max_length=128, allow_null=True, allow_blank=True,
        label=_('Regularly perform')
    )
    AUTH_LDAP_SYNC_INTERVAL = serializers.IntegerField(
        required=False, default=24, allow_null=True, label=_('Cycle perform')
    )
    AUTH_LDAP_CONNECT_TIMEOUT = serializers.IntegerField(
        min_value=1, max_value=300,
        required=False, label=_('Connect timeout'),
    )
    AUTH_LDAP_SEARCH_PAGED_SIZE = serializers.IntegerField(required=False, label=_('Search paged size'))

    AUTH_LDAP = serializers.BooleanField(required=False, label=_('Enable LDAP auth'))

    @staticmethod
    def post_save():
        from users.tasks import import_ldap_user_periodic
        import_ldap_user_periodic()
예제 #19
0
class ReplayStorageTypeBaseSerializer(serializers.Serializer):
    BUCKET = serializers.CharField(
        required=True, max_length=1024, label=_('Bucket'), allow_null=True
    )
    ACCESS_KEY = serializers.CharField(
        max_length=1024, required=False, allow_blank=True,
        label=_('Access key id'), allow_null=True,
    )
    SECRET_KEY = EncryptedField(
        max_length=1024, required=False, allow_blank=True,
        label=_('Access key secret'), allow_null=True,
    )
    ENDPOINT = serializers.CharField(
        validators=[replay_storage_endpoint_format_validator],
        required=True, max_length=1024, label=_('Endpoint'), allow_null=True,
    )
예제 #20
0
class ChromeSerializer(RemoteAppSerializer):
    CHROME_PATH = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe'

    path = serializers.CharField(
        max_length=128, label=_('Application path'), default=CHROME_PATH, allow_null=True,
    )
    chrome_target = serializers.CharField(
        max_length=128, allow_blank=True, required=False,
        label=_('Target URL'), allow_null=True,
    )
    chrome_username = serializers.CharField(
        max_length=128, allow_blank=True, required=False,
        label=_('Chrome username'), allow_null=True,
    )
    chrome_password = EncryptedField(
        max_length=128, allow_blank=True, required=False,
        label=_('Chrome password'), allow_null=True
    )
예제 #21
0
class ConfirmSerializer(serializers.Serializer):
    confirm_type = serializers.ChoiceField(required=True,
                                           choices=ConfirmType.choices)
    secret_key = EncryptedField()
예제 #22
0
class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin,
                     serializers.ModelSerializer):
    password_strategy = serializers.ChoiceField(
        choices=PasswordStrategy.choices,
        default=PasswordStrategy.email,
        required=False,
        write_only=True,
        label=_('Password strategy'))
    mfa_enabled = serializers.BooleanField(read_only=True,
                                           label=_('MFA enabled'))
    mfa_force_enabled = serializers.BooleanField(read_only=True,
                                                 label=_('MFA force enabled'))
    mfa_level_display = serializers.ReadOnlyField(
        source='get_mfa_level_display', label=_('MFA level display'))
    login_blocked = serializers.BooleanField(read_only=True,
                                             label=_('Login blocked'))
    is_expired = serializers.BooleanField(read_only=True,
                                          label=_('Is expired'))
    can_public_key_auth = serializers.ReadOnlyField(
        source='can_use_ssh_key_login',
        label=_('Can public key authentication'))
    password = EncryptedField(label=_('Password'),
                              required=False,
                              allow_blank=True,
                              allow_null=True,
                              max_length=1024)
    # Todo: 这里看看该怎么搞
    # can_update = serializers.SerializerMethodField(label=_('Can update'))
    # can_delete = serializers.SerializerMethodField(label=_('Can delete'))
    custom_m2m_fields = {
        'system_roles': [BuiltinRole.system_user],
        'org_roles': [BuiltinRole.org_user]
    }

    class Meta:
        model = User
        # mini 是指能识别对象的最小单元
        fields_mini = ['id', 'name', 'username']
        # 只能写的字段, 这个虽然无法在框架上生效,但是更多对我们是提醒
        fields_write_only = [
            'password',
            'public_key',
        ]
        # small 指的是 不需要计算的直接能从一张表中获取到的数据
        fields_small = fields_mini + fields_write_only + [
            'email',
            'wechat',
            'phone',
            'mfa_level',
            'source',
            'source_display',
            'can_public_key_auth',
            'need_update_password',
            'mfa_enabled',
            'is_service_account',
            'is_valid',
            'is_expired',
            'is_active',  # 布尔字段
            'date_expired',
            'date_joined',
            'last_login',  # 日期字段
            'created_by',
            'comment',  # 通用字段
            'is_wecom_bound',
            'is_dingtalk_bound',
            'is_feishu_bound',
            'is_otp_secret_key_bound',
            'wecom_id',
            'dingtalk_id',
            'feishu_id'
        ]
        # 包含不太常用的字段,可以没有
        fields_verbose = fields_small + [
            'mfa_level_display',
            'mfa_force_enabled',
            'is_first_login',
            'date_password_last_updated',
            'avatar_url',
        ]
        # 外键的字段
        fields_fk = []
        # 多对多字段
        fields_m2m = [
            'groups', 'groups_display', 'system_roles', 'org_roles',
            'system_roles_display', 'org_roles_display'
        ]
        # 在serializer 上定义的字段
        fields_custom = ['login_blocked', 'password_strategy']
        fields = fields_verbose + fields_fk + fields_m2m + fields_custom

        read_only_fields = [
            'date_joined', 'last_login', 'created_by', 'is_first_login',
            'wecom_id', 'dingtalk_id', 'feishu_id'
        ]
        disallow_self_update_fields = ['is_active']
        extra_kwargs = {
            'password': {
                'write_only': True,
                'required': False,
                'allow_null': True,
                'allow_blank': True
            },
            'public_key': {
                'write_only': True
            },
            'is_first_login': {
                'label': _('Is first login'),
                'read_only': True
            },
            'is_valid': {
                'label': _('Is valid')
            },
            'is_service_account': {
                'label': _('Is service account')
            },
            'is_expired': {
                'label': _('Is expired')
            },
            'avatar_url': {
                'label': _('Avatar url')
            },
            'created_by': {
                'read_only': True,
                'allow_blank': True
            },
            'groups_display': {
                'label': _('Groups name')
            },
            'source_display': {
                'label': _('Source name')
            },
            'org_role_display': {
                'label': _('Organization role name')
            },
            'role_display': {
                'label': _('Super role name')
            },
            'total_role_display': {
                'label': _('Total role name')
            },
            'role': {
                'default': "User"
            },
            'is_wecom_bound': {
                'label': _('Is wecom bound')
            },
            'is_dingtalk_bound': {
                'label': _('Is dingtalk bound')
            },
            'is_feishu_bound': {
                'label': _('Is feishu bound')
            },
            'is_otp_secret_key_bound': {
                'label': _('Is OTP bound')
            },
            'phone': {
                'validators': [PhoneValidator()]
            },
            'system_role_display': {
                'label': _('System role name')
            },
        }

    def validate_password(self, password):
        password_strategy = self.initial_data.get('password_strategy')
        if self.instance is None and password_strategy != PasswordStrategy.custom:
            # 创建用户,使用邮件设置密码
            return
        if self.instance and not password:
            # 更新用户, 未设置密码
            return
        return password

    @staticmethod
    def change_password_to_raw(attrs):
        password = attrs.pop('password', None)
        if password:
            attrs['password_raw'] = password
        return attrs

    @staticmethod
    def clean_auth_fields(attrs):
        for field in ('password', 'public_key'):
            value = attrs.get(field)
            if not value:
                attrs.pop(field, None)
        return attrs

    def check_disallow_self_update_fields(self, attrs):
        request = self.context.get('request')
        if not request or not request.user.is_authenticated:
            return attrs
        if not self.instance:
            return attrs
        if request.user.id != self.instance.id:
            return attrs
        disallow_fields = set(list(attrs.keys())) & set(
            self.Meta.disallow_self_update_fields)
        if not disallow_fields:
            return attrs
        # 用户自己不能更新自己的一些字段
        error = _('User cannot self-update fields: {}').format(disallow_fields)
        raise serializers.ValidationError(error)

    def validate(self, attrs):
        attrs = self.check_disallow_self_update_fields(attrs)
        attrs = self.change_password_to_raw(attrs)
        attrs = self.clean_auth_fields(attrs)
        attrs.pop('password_strategy', None)
        return attrs

    def save_and_set_custom_m2m_fields(self, validated_data, save_handler,
                                       created):
        m2m_values = {}
        for f, default_roles in self.custom_m2m_fields.items():
            roles = validated_data.pop(f, None)
            if created and not roles:
                roles = [
                    Role.objects.filter(id=role.id).first()
                    for role in default_roles
                ]
            m2m_values[f] = roles

        instance = save_handler(validated_data)
        for field_name, value in m2m_values.items():
            if value is None:
                continue
            field = getattr(instance, field_name)
            field.set(value)
        return instance

    def update(self, instance, validated_data):
        save_handler = partial(super().update, instance)
        instance = self.save_and_set_custom_m2m_fields(validated_data,
                                                       save_handler,
                                                       created=False)
        return instance

    def create(self, validated_data):
        save_handler = super().create
        instance = self.save_and_set_custom_m2m_fields(validated_data,
                                                       save_handler,
                                                       created=True)
        return instance
예제 #23
0
class SystemUserSerializer(AuthSerializerMixin, BulkOrgResourceModelSerializer):
    """
    系统用户
    """
    password = EncryptedField(
        label=_('Password'), required=False, allow_blank=True, allow_null=True, max_length=1024,
        trim_whitespace=False, validators=[validate_password_for_ansible],
        write_only=True
    )
    auto_generate_key = serializers.BooleanField(initial=True, required=False, write_only=True)
    type_display = serializers.ReadOnlyField(source='get_type_display', label=_('Type display'))
    ssh_key_fingerprint = serializers.ReadOnlyField(label=_('SSH key fingerprint'))
    token = EncryptedField(
        label=_('Token'), required=False, write_only=True, style={'base_template': 'textarea.html'}
    )
    applications_amount = serializers.IntegerField(
        source='apps_amount', read_only=True, label=_('Apps amount')
    )

    class Meta:
        model = SystemUser
        fields_mini = ['id', 'name', 'username']
        fields_write_only = ['password', 'public_key', 'private_key', 'passphrase']
        fields_small = fields_mini + fields_write_only + [
            'token', 'ssh_key_fingerprint',
            'type', 'type_display', 'protocol', 'is_asset_protocol',
            'login_mode', 'login_mode_display', 'priority',
            'sudo', 'shell', 'sftp_root', 'home', 'system_groups', 'ad_domain',
            'username_same_with_user', 'auto_push', 'auto_generate_key',
            'su_enabled', 'su_from',
            'date_created', 'date_updated', 'comment', 'created_by',
        ]
        fields_m2m = ['cmd_filters', 'assets_amount', 'applications_amount', 'nodes']
        fields = fields_small + fields_m2m
        extra_kwargs = {
            'cmd_filters': {"required": False, 'label': _('Command filter')},
            'public_key': {"write_only": True},
            'private_key': {"write_only": True},
            'nodes_amount': {'label': _('Nodes amount')},
            'assets_amount': {'label': _('Assets amount')},
            'login_mode_display': {'label': _('Login mode display')},
            'created_by': {'read_only': True},
            'ad_domain': {'required': False, 'allow_blank': True, 'label': _('Ad domain')},
            'is_asset_protocol': {'label': _('Is asset protocol')},
            'su_from': {'help_text': _('Only ssh and automatic login system users are supported')}
        }

    def validate_auto_push(self, value):
        login_mode = self.get_initial_value("login_mode")
        protocol = self.get_initial_value("protocol")

        if login_mode == SystemUser.LOGIN_MANUAL:
            value = False
        elif protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS:
            value = False
        return value

    def validate_auto_generate_key(self, value):
        login_mode = self.get_initial_value("login_mode")
        protocol = self.get_initial_value("protocol")

        if self.context["request"].method.lower() != "post":
            value = False
        elif self.instance:
            value = False
        elif login_mode == SystemUser.LOGIN_MANUAL:
            value = False
        elif protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS:
            value = False
        return value

    def validate_username_same_with_user(self, username_same_with_user):
        if not username_same_with_user:
            return username_same_with_user
        protocol = self.get_initial_value("protocol", "ssh")
        queryset = SystemUser.objects.filter(
            protocol=protocol,
            username_same_with_user=True
        )
        if self.instance:
            queryset = queryset.exclude(id=self.instance.id)
        exists = queryset.exists()
        if not exists:
            return username_same_with_user
        error = _("Username same with user with protocol {} only allow 1").format(protocol)
        raise serializers.ValidationError(error)

    def validate_username(self, username):
        protocol = self.get_initial_value("protocol")
        if username:
            if protocol == SystemUser.Protocol.telnet:
                regx = alphanumeric_cn_re
            elif protocol == SystemUser.Protocol.rdp:
                regx = alphanumeric_win_re
            else:
                regx = alphanumeric_re
            if not regx.match(username):
                raise serializers.ValidationError(_('Special char not allowed'))
            return username

        username_same_with_user = self.get_initial_value("username_same_with_user")
        if username_same_with_user:
            return ''

        login_mode = self.get_initial_value("login_mode")
        if login_mode == SystemUser.LOGIN_AUTO and protocol != SystemUser.Protocol.vnc \
                and protocol != SystemUser.Protocol.redis:
            msg = _('* Automatic login mode must fill in the username.')
            raise serializers.ValidationError(msg)
        return username

    def validate_home(self, home):
        username_same_with_user = self.get_initial_value("username_same_with_user")
        if username_same_with_user:
            return ''
        return home

    @staticmethod
    def validate_sftp_root(value):
        if value in ['home', 'tmp']:
            return value
        if not value.startswith('/'):
            error = _("Path should starts with /")
            raise serializers.ValidationError(error)
        return value

    def validate_password(self, password):
        super().validate_password(password)
        auto_gen_key = self.get_initial_value('auto_generate_key', False)
        private_key = self.get_initial_value('private_key')
        login_mode = self.get_initial_value('login_mode')

        if not self.instance and not auto_gen_key and not password and \
                not private_key and login_mode == SystemUser.LOGIN_AUTO:
            raise serializers.ValidationError(_("Password or private key required"))
        return password

    def validate_su_from(self, su_from: SystemUser):
        # self: su enabled
        su_enabled = self.get_initial_value('su_enabled', default=False)
        if not su_enabled:
            return
        if not su_from:
            error = _('This field is required.')
            raise serializers.ValidationError(error)
        # self: protocol ssh
        protocol = self.get_initial_value('protocol', default=SystemUser.Protocol.ssh.value)
        if protocol not in [SystemUser.Protocol.ssh.value]:
            error = _('Only ssh protocol system users are allowed')
            raise serializers.ValidationError(error)
        # su_from: protocol same
        if su_from.protocol != protocol:
            error = _('The protocol must be consistent with the current user: {}').format(protocol)
            raise serializers.ValidationError(error)
        # su_from: login model auto
        if su_from.login_mode != su_from.LOGIN_AUTO:
            error = _('Only system users with automatic login are allowed')
            raise serializers.ValidationError(error)
        return su_from

    def _validate_admin_user(self, attrs):
        if self.instance:
            tp = self.instance.type
        else:
            tp = attrs.get('type')
        if tp != SystemUser.Type.admin:
            return attrs
        attrs['protocol'] = SystemUser.Protocol.ssh
        attrs['login_mode'] = SystemUser.LOGIN_AUTO
        attrs['username_same_with_user'] = False
        attrs['auto_push'] = False
        return attrs

    def _validate_gen_key(self, attrs):
        username = attrs.get('username', 'manual')
        auto_gen_key = attrs.pop('auto_generate_key', False)
        protocol = attrs.get('protocol')

        if protocol not in SystemUser.SUPPORT_PUSH_PROTOCOLS:
            return attrs

        # 自动生成
        if auto_gen_key and not self.instance:
            password = SystemUser.gen_password()
            attrs['password'] = password
            if protocol == SystemUser.Protocol.ssh:
                private_key, public_key = SystemUser.gen_key(username)
                attrs['private_key'] = private_key
                attrs['public_key'] = public_key
        # 如果设置了private key,没有设置public key则生成
        elif attrs.get('private_key'):
            private_key = attrs['private_key']
            password = attrs.get('password')
            public_key = ssh_pubkey_gen(private_key, password=password, username=username)
            attrs['public_key'] = public_key
        return attrs

    def _validate_login_mode(self, attrs):
        if 'login_mode' in attrs:
            login_mode = attrs['login_mode']
        else:
            login_mode = self.instance.login_mode if self.instance else SystemUser.LOGIN_AUTO

        if login_mode == SystemUser.LOGIN_MANUAL:
            attrs['password'] = ''
            attrs['private_key'] = ''
            attrs['public_key'] = ''

        return attrs

    def validate(self, attrs):
        attrs = self._validate_admin_user(attrs)
        attrs = self._validate_gen_key(attrs)
        attrs = self._validate_login_mode(attrs)
        return attrs

    @classmethod
    def setup_eager_loading(cls, queryset):
        """ Perform necessary eager loading of data. """
        queryset = queryset \
            .annotate(assets_amount=Count("assets")) \
            .prefetch_related('nodes', 'cmd_filters')
        return queryset
예제 #24
0
class LDAPTestLoginSerializer(serializers.Serializer):
    username = serializers.CharField(max_length=1024, required=True)
    password = EncryptedField(max_length=2014, required=True, label=_("Password"))
예제 #25
0
class MySQLWorkbenchSecretSerializer(RemoteAppSerializer):
    mysql_workbench_password = EncryptedField(
        max_length=128, allow_blank=True, required=False, write_only=False,
        label=_('Mysql workbench password'), allow_null=True,
    )
예제 #26
0
class PasswordVerifySerializer(serializers.Serializer):
    password = EncryptedField()
예제 #27
0
class ChromeSecretSerializer(ChromeSerializer):
    chrome_password = EncryptedField(
        max_length=128, allow_blank=True, required=False,
        label=_('Chrome password'), allow_null=True, write_only=False
    )