class ApplySerializer(serializers.Serializer): # 申请信息 apply_ip_group = serializers.ListField( required=False, child=serializers.IPAddressField(), label=_('IP group'), default=list, allow_null=True, ) apply_hostname_group = serializers.ListField( required=False, child=serializers.CharField(), label=_('Hostname group'), default=list, allow_null=True, ) apply_system_user_group = serializers.ListField( required=False, child=serializers.CharField(), label=_('System user group'), default=list, allow_null=True) apply_actions = ActionsField(required=True, allow_null=True) apply_actions_display = serializers.ListField( required=False, read_only=True, child=serializers.CharField(), label=_('Approve assets display'), allow_null=True, default=list, ) apply_date_start = serializers.DateTimeField( required=True, label=_('Date start'), allow_null=True, ) apply_date_expired = serializers.DateTimeField( required=True, label=_('Date expired'), allow_null=True, )
class ApproveSerializer(serializers.Serializer): # 审批信息 approve_permission_name = serializers.CharField( max_length=128, default=DefaultPermissionName(), label=_('Permission name')) approve_assets = serializers.ListField(required=True, allow_null=True, child=serializers.UUIDField(), label=_('Approve assets')) approve_assets_display = serializers.ListField( required=False, read_only=True, child=serializers.CharField(), label=_('Approve assets display'), allow_null=True, default=list, ) approve_system_users = serializers.ListField( required=True, allow_null=True, child=serializers.UUIDField(), label=_('Approve system users')) approve_system_users_display = serializers.ListField( required=False, read_only=True, child=serializers.CharField(), label=_('Approve assets display'), allow_null=True, default=list, ) approve_actions = ActionsField( required=True, allow_null=True, ) approve_actions_display = serializers.ListField( required=False, read_only=True, child=serializers.CharField(), label=_('Approve assets display'), allow_null=True, default=list, ) approve_date_start = serializers.DateTimeField( required=True, label=_('Date start'), allow_null=True, ) approve_date_expired = serializers.DateTimeField(required=True, label=_('Date expired'), allow_null=True) def validate_approve_permission_name(self, permission_name): if not isinstance(self.root.instance, Ticket): return permission_name with tmp_to_org(self.root.instance.org_id): already_exists = AssetPermission.objects.filter( name=permission_name).exists() if not already_exists: return permission_name raise serializers.ValidationError( _('Permission named `{}` already exists'.format(permission_name))) def validate_approve_assets(self, approve_assets): if not isinstance(self.root.instance, Ticket): return [] with tmp_to_org(self.root.instance.org_id): assets_id = Asset.objects.filter( id__in=approve_assets).values_list('id', flat=True) assets_id = [str(asset_id) for asset_id in assets_id] if assets_id: return assets_id raise serializers.ValidationError( _('No `Asset` are found under Organization `{}`'.format( self.root.instance.org_name))) def validate_approve_system_users(self, approve_system_users): if not isinstance(self.root.instance, Ticket): return [] with tmp_to_org(self.root.instance.org_id): queries = Q(protocol__in=SystemUser.ASSET_CATEGORY_PROTOCOLS) queries &= Q(id__in=approve_system_users) system_users_id = SystemUser.objects.filter(queries).values_list( 'id', flat=True) system_users_id = [ str(system_user_id) for system_user_id in system_users_id ] if system_users_id: return system_users_id raise serializers.ValidationError( _('No `SystemUser` are found under Organization `{}`'.format( self.root.instance.org_name)))
class ApplySerializer(serializers.Serializer): apply_permission_name = serializers.CharField( max_length=128, default=DefaultPermissionName(), label=_('Apply name')) # 申请信息 apply_assets = serializers.ListField(required=True, allow_null=True, child=serializers.UUIDField(), label=_('Apply assets')) apply_assets_display = serializers.ListField( required=False, read_only=True, child=serializers.CharField(), label=_('Approve assets display'), allow_null=True, default=list, ) apply_system_users = serializers.ListField(required=True, allow_null=True, child=serializers.UUIDField(), label=_('Approve system users')) apply_system_users_display = serializers.ListField( required=False, read_only=True, child=serializers.CharField(), label=_('Apply assets display'), allow_null=True, default=list, ) apply_actions = ActionsField(required=True, allow_null=True) apply_actions_display = serializers.ListField( required=False, read_only=True, child=serializers.CharField(), label=_('Apply assets display'), allow_null=True, default=list, ) apply_date_start = serializers.DateTimeField( required=True, label=_('Date start'), allow_null=True, ) apply_date_expired = serializers.DateTimeField( required=True, label=_('Date expired'), allow_null=True, ) def validate_approve_permission_name(self, permission_name): if not isinstance(self.root.instance, Ticket): return permission_name with tmp_to_org(self.root.instance.org_id): already_exists = AssetPermission.objects.filter( name=permission_name).exists() if not already_exists: return permission_name raise serializers.ValidationError( _('Permission named `{}` already exists'.format(permission_name))) def validate(self, attrs): apply_date_start = attrs['apply_date_start'] apply_date_expired = attrs['apply_date_expired'] if apply_date_expired <= apply_date_start: error = _( 'The expiration date should be greater than the start date') raise serializers.ValidationError({'apply_date_expired': error}) return attrs
class RequestAssetPermTicketSerializer(serializers.ModelSerializer): actions = ActionsField(source='meta.actions', choices=Action.DB_CHOICES, default=Action.CONNECT) ips = serializers.ListField(child=serializers.IPAddressField(), source='meta.ips', default=list, label=_('IP group')) hostname = serializers.CharField(max_length=256, source='meta.hostname', default='', allow_blank=True, label=_('Hostname')) system_user = serializers.CharField(max_length=256, source='meta.system_user', default='', allow_blank=True, label=_('System user')) date_start = serializers.DateTimeField(source='meta.date_start', allow_null=True, required=False, label=_('Date start')) date_expired = serializers.DateTimeField(source='meta.date_expired', allow_null=True, required=False, label=_('Date expired')) confirmed_assets = serializers.ListField(child=serializers.UUIDField(), source='meta.confirmed_assets', default=list, required=False, label=_('Confirmed assets')) confirmed_system_users = serializers.ListField( child=serializers.UUIDField(), source='meta.confirmed_system_users', default=list, required=False, label=_('Confirmed system user')) assets_waitlist_url = serializers.SerializerMethodField() system_users_waitlist_url = serializers.SerializerMethodField() class Meta: model = Ticket mini_fields = ['id', 'title'] small_fields = [ 'status', 'action', 'date_created', 'date_updated', 'system_users_waitlist_url', 'type', 'type_display', 'action_display', 'ips', 'confirmed_assets', 'date_start', 'date_expired', 'confirmed_system_users', 'hostname', 'assets_waitlist_url', 'system_user', 'org_id', 'actions', 'comment' ] m2m_fields = [ 'user', 'user_display', 'assignees', 'assignees_display', 'assignee', 'assignee_display' ] fields = mini_fields + small_fields + m2m_fields read_only_fields = [ 'user_display', 'assignees_display', 'type', 'user', 'status', 'date_created', 'date_updated', 'action', 'id', 'assignee', 'assignee_display', ] extra_kwargs = { 'status': { 'label': _('Status') }, 'action': { 'label': _('Action') }, 'user_display': { 'label': _('User') }, 'org_id': { 'required': True } } def validate(self, attrs): org_id = attrs.get('org_id') assignees = attrs.get('assignees') instance = self.instance if instance is not None: if org_id and not assignees: assignees = list(instance.assignees.all()) elif assignees and not org_id: org_id = instance.org_id elif assignees and org_id: pass else: return attrs user = self.context['request'].user org = Organization.get_instance(org_id) if org is None: raise serializers.ValidationError(_('Invalid `org_id`')) q = Q(role=User.ROLE.ADMIN) if not org.is_default(): q |= Q(m2m_org_members__role=ORG_ROLE.ADMIN, orgs__id=org_id, orgs__members=user) q &= Q(id__in=[assignee.id for assignee in assignees]) count = User.objects.filter(q).distinct().count() if count != len(assignees): raise serializers.ValidationError( _('Field `assignees` must be organization admin or superuser')) return attrs def get_system_users_waitlist_url(self, instance: Ticket): if not self._is_assignee(instance): return None return reverse('api-assets:system-user-list') def get_assets_waitlist_url(self, instance: Ticket): if not self._is_assignee(instance): return None asset_api = reverse('api-assets:asset-list') query = '' meta = instance.meta hostname = meta.get('hostname') if hostname: query = '?search=%s' % hostname return asset_api + query def _recommend_assets(self, data, instance): confirmed_assets = data.get('confirmed_assets') if not confirmed_assets and self._is_assignee(instance): ips = data.get('ips') hostname = data.get('hostname') limit = 5 q = Q(id=None) if ips: limit = len(ips) + 2 q |= Q(ip__in=ips) if hostname: q |= Q(hostname__icontains=hostname) recomand_assets_id = Asset.objects.filter(q)[:limit].values_list( 'id', flat=True) data['confirmed_assets'] = [str(i) for i in recomand_assets_id] def _recommend_system_users(self, data, instance): confirmed_system_users = data.get('confirmed_system_users') if not confirmed_system_users and self._is_assignee(instance): system_user = data.get('system_user') recomand_system_users_id = SystemUser.objects.filter( name__icontains=system_user)[:3].values_list('id', flat=True) data['confirmed_system_users'] = [ str(i) for i in recomand_system_users_id ] def to_representation(self, instance): data = super().to_representation(instance) self._recommend_assets(data, instance) self._recommend_system_users(data, instance) return data def _create_body(self, validated_data): meta = validated_data['meta'] type = Ticket.TYPE.get(validated_data.get('type', '')) date_start = dt_parser(meta.get('date_start')).strftime( settings.DATETIME_DISPLAY_FORMAT) date_expired = dt_parser(meta.get('date_expired')).strftime( settings.DATETIME_DISPLAY_FORMAT) validated_data['body'] = _(''' Type: {type}<br> User: {username}<br> Ip group: {ips}<br> Hostname: {hostname}<br> System user: {system_user}<br> Date start: {date_start}<br> Date expired: {date_expired}<br> ''').format(type=type, username=validated_data.get('user', ''), ips=', '.join(meta.get('ips', [])), hostname=meta.get('hostname', ''), system_user=meta.get('system_user', ''), date_start=date_start, date_expired=date_expired) def create(self, validated_data): # `type` 与 `user` 用户不可提交, validated_data['type'] = self.Meta.model.TYPE.REQUEST_ASSET_PERM validated_data['user'] = self.context['request'].user # `confirmed` 相关字段只能审批人修改,所以创建时直接清理掉 self._pop_confirmed_fields() self._create_body(validated_data) return super().create(validated_data) def save(self, **kwargs): """ 做了一些数据转换 """ meta = self.validated_data.get('meta', {}) org_id = self.validated_data.get('org_id') if org_id is not None and org_id == Organization.DEFAULT_ID: self.validated_data['org_id'] = '' # 时间的转换,好烦😭,可能有更好的办法吧 date_start = meta.get('date_start') if date_start: meta['date_start'] = dt_formater(date_start) date_expired = meta.get('date_expired') if date_expired: meta['date_expired'] = dt_formater(date_expired) # UUID 的转换 confirmed_system_users = meta.get('confirmed_system_users') if confirmed_system_users: meta['confirmed_system_users'] = [ str(system_user) for system_user in confirmed_system_users ] confirmed_assets = meta.get('confirmed_assets') if confirmed_assets: meta['confirmed_assets'] = [ str(asset) for asset in confirmed_assets ] with tmp_to_root_org(): return super().save(**kwargs) def update(self, instance, validated_data): new_meta = validated_data['meta'] if not self._is_assignee(instance): self._pop_confirmed_fields() # Json 字段保存的坑😭 old_meta = instance.meta meta = {} meta.update(old_meta) meta.update(new_meta) validated_data['meta'] = meta return super().update(instance, validated_data) def _pop_confirmed_fields(self): meta = self.validated_data['meta'] meta.pop('confirmed_assets', None) meta.pop('confirmed_system_users', None) def _is_assignee(self, obj: Ticket): user = self.context['request'].user return obj.is_assignee(user)