jumpserver/apps/common/drf/fields.py

173 lines
5.4 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
#
2022-08-09 08:53:43 +00:00
import six
2022-09-01 06:46:31 +00:00
from django.core.exceptions import ObjectDoesNotExist
2022-11-11 07:04:31 +00:00
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.fields import ChoiceField, empty
2020-07-20 02:42:22 +00:00
2022-11-11 09:28:13 +00:00
from common.db.fields import BitChoices
from common.utils import decrypt_password
from common.local import add_encrypted_field_set
2020-07-20 02:42:22 +00:00
__all__ = [
2022-11-11 07:04:31 +00:00
"ReadableHiddenField",
"EncryptedField",
"LabeledChoiceField",
"ObjectRelatedField",
"BitChoicesField",
2022-11-11 09:28:13 +00:00
"TreeChoicesMixin"
]
# ReadableHiddenField
# -------------------
class ReadableHiddenField(serializers.HiddenField):
2022-11-11 07:04:31 +00:00
"""可读的 HiddenField"""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.write_only = False
def to_representation(self, value):
2022-11-11 07:04:31 +00:00
if hasattr(value, "id"):
return getattr(value, "id")
return value
class EncryptedField(serializers.CharField):
2022-05-17 11:34:15 +00:00
def __init__(self, write_only=None, **kwargs):
if write_only is None:
write_only = True
2022-11-11 07:04:31 +00:00
kwargs["write_only"] = write_only
2022-11-14 00:19:32 +00:00
encrypted_key = kwargs.pop('encrypted_key', None)
2022-05-13 09:10:29 +00:00
super().__init__(**kwargs)
2022-11-14 00:19:32 +00:00
add_encrypted_field_set(encrypted_key or self.label)
2022-05-13 09:10:29 +00:00
def to_internal_value(self, value):
value = super().to_internal_value(value)
return decrypt_password(value)
2022-08-09 08:53:43 +00:00
2022-09-01 06:46:31 +00:00
class LabeledChoiceField(ChoiceField):
2022-08-09 08:53:43 +00:00
def __init__(self, *args, **kwargs):
2022-09-01 06:46:31 +00:00
super(LabeledChoiceField, self).__init__(*args, **kwargs)
2022-08-09 08:53:43 +00:00
self.choice_mapper = {
six.text_type(key): value for key, value in self.choices.items()
}
def to_representation(self, value):
2022-11-02 11:07:07 +00:00
if value is None:
2022-08-09 08:53:43 +00:00
return value
return {
2022-11-11 07:04:31 +00:00
"value": value,
"label": self.choice_mapper.get(six.text_type(value), value),
2022-08-09 08:53:43 +00:00
}
def to_internal_value(self, data):
if isinstance(data, dict):
2022-11-11 07:04:31 +00:00
return data.get("value")
2022-09-01 06:46:31 +00:00
return super(LabeledChoiceField, self).to_internal_value(data)
2022-09-01 13:00:04 +00:00
class ObjectRelatedField(serializers.RelatedField):
2022-09-06 05:27:47 +00:00
default_error_messages = {
2022-11-11 07:04:31 +00:00
"required": _("This field is required."),
"does_not_exist": _('Invalid pk "{pk_value}" - object does not exist.'),
"incorrect_type": _("Incorrect type. Expected pk value, received {data_type}."),
2022-09-06 05:27:47 +00:00
}
2022-09-01 06:46:31 +00:00
def __init__(self, **kwargs):
2022-11-11 07:04:31 +00:00
self.attrs = kwargs.pop("attrs", None) or ("id", "name")
self.many = kwargs.get("many", False)
2022-09-01 06:46:31 +00:00
super().__init__(**kwargs)
def to_representation(self, value):
data = {}
for attr in self.attrs:
data[attr] = getattr(value, attr)
return data
def to_internal_value(self, data):
2022-09-06 05:27:47 +00:00
if not isinstance(data, dict):
2022-09-01 06:46:31 +00:00
pk = data
2022-09-06 05:27:47 +00:00
else:
2022-11-11 07:04:31 +00:00
pk = data.get("id") or data.get("pk") or data.get(self.attrs[0])
2022-09-01 06:46:31 +00:00
queryset = self.get_queryset()
try:
if isinstance(data, bool):
raise TypeError
return queryset.get(pk=pk)
except ObjectDoesNotExist:
2022-11-11 07:04:31 +00:00
self.fail("does_not_exist", pk_value=pk)
2022-09-01 06:46:31 +00:00
except (TypeError, ValueError):
2022-11-11 07:04:31 +00:00
self.fail("incorrect_type", data_type=type(pk).__name__)
2022-11-11 09:28:13 +00:00
class TreeChoicesMixin:
tree = []
class BitChoicesField(TreeChoicesMixin, serializers.MultipleChoiceField):
2022-11-11 07:04:31 +00:00
"""
位字段
"""
def __init__(self, choice_cls, **kwargs):
2022-11-11 09:28:13 +00:00
assert issubclass(choice_cls, BitChoices)
2022-11-11 07:04:31 +00:00
choices = [(c.name, c.label) for c in choice_cls]
2022-11-11 09:28:13 +00:00
self.tree = choice_cls.tree()
2022-11-11 07:04:31 +00:00
self._choice_cls = choice_cls
super().__init__(choices=choices, **kwargs)
def to_representation(self, value):
if isinstance(value, list) and len(value) == 1:
# Swagger 会使用 field.choices.keys() 迭代传递进来
return [
{"value": c.name, "label": c.label}
for c in self._choice_cls
if c.name == value[0]
]
2022-11-11 07:04:31 +00:00
return [
{"value": c.name, "label": c.label}
for c in self._choice_cls
if c.value & value == c.value
]
def to_internal_value(self, data):
if not isinstance(data, list):
raise serializers.ValidationError(_("Invalid data type, should be list"))
value = 0
if not data:
return value
if isinstance(data[0], dict):
data = [d["value"] for d in data]
# 所有的
if "all" in data:
for c in self._choice_cls:
value |= c.value
return value
name_value_map = {c.name: c.value for c in self._choice_cls}
for name in data:
if name not in name_value_map:
raise serializers.ValidationError(_("Invalid choice: {}").format(name))
value |= name_value_map[name]
return value
def run_validation(self, data=empty):
"""
备注:
创建授权规则不包含 actions 字段时, 会使用默认值(AssetPermission 中设置),
会直接使用 ['connect', '...'] 等字段保存到数据库导致类型错误
这里将获取到的值再执行一下 to_internal_value 方法, 转化为内部值
"""
data = super().run_validation(data)
2022-11-16 11:35:16 +00:00
if isinstance(data, int):
return data
value = self.to_internal_value(data)
self.run_validators(value)
return value