serializers.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. from django.conf import settings
  2. from django.contrib.auth import authenticate
  3. from django.contrib.auth import get_user_model
  4. from drf_spectacular.utils import extend_schema_field
  5. from drf_spectacular.types import OpenApiTypes
  6. from rest_framework import serializers
  7. from rest_framework.exceptions import AuthenticationFailed, PermissionDenied
  8. from core.models import ObjectType
  9. from netbox.api.fields import ContentTypeField, IPNetworkSerializer, SerializedPKRelatedField
  10. from netbox.api.serializers import ValidatedModelSerializer
  11. from users.models import Group, ObjectPermission, Token
  12. from .nested_serializers import *
  13. __all__ = (
  14. 'GroupSerializer',
  15. 'ObjectPermissionSerializer',
  16. 'TokenSerializer',
  17. 'UserSerializer',
  18. )
  19. class UserSerializer(ValidatedModelSerializer):
  20. url = serializers.HyperlinkedIdentityField(view_name='users-api:user-detail')
  21. groups = SerializedPKRelatedField(
  22. queryset=Group.objects.all(),
  23. serializer=NestedGroupSerializer,
  24. required=False,
  25. many=True
  26. )
  27. class Meta:
  28. model = get_user_model()
  29. fields = (
  30. 'id', 'url', 'display', 'username', 'password', 'first_name', 'last_name', 'email', 'is_staff', 'is_active',
  31. 'date_joined', 'last_login', 'groups',
  32. )
  33. brief_fields = ('id', 'url', 'display', 'username')
  34. extra_kwargs = {
  35. 'password': {'write_only': True}
  36. }
  37. def create(self, validated_data):
  38. """
  39. Extract the password from validated data and set it separately to ensure proper hash generation.
  40. """
  41. password = validated_data.pop('password')
  42. user = super().create(validated_data)
  43. user.set_password(password)
  44. user.save()
  45. return user
  46. def update(self, instance, validated_data):
  47. """
  48. Ensure proper updated password hash generation.
  49. """
  50. password = validated_data.pop('password', None)
  51. if password is not None:
  52. instance.set_password(password)
  53. return super().update(instance, validated_data)
  54. @extend_schema_field(OpenApiTypes.STR)
  55. def get_display(self, obj):
  56. if full_name := obj.get_full_name():
  57. return f"{obj.username} ({full_name})"
  58. return obj.username
  59. class GroupSerializer(ValidatedModelSerializer):
  60. url = serializers.HyperlinkedIdentityField(view_name='users-api:group-detail')
  61. user_count = serializers.IntegerField(read_only=True)
  62. class Meta:
  63. model = Group
  64. fields = ('id', 'url', 'display', 'name', 'user_count')
  65. brief_fields = ('id', 'url', 'display', 'name')
  66. class TokenSerializer(ValidatedModelSerializer):
  67. url = serializers.HyperlinkedIdentityField(view_name='users-api:token-detail')
  68. key = serializers.CharField(
  69. min_length=40,
  70. max_length=40,
  71. allow_blank=True,
  72. required=False,
  73. write_only=not settings.ALLOW_TOKEN_RETRIEVAL
  74. )
  75. user = NestedUserSerializer()
  76. allowed_ips = serializers.ListField(
  77. child=IPNetworkSerializer(),
  78. required=False,
  79. allow_empty=True,
  80. default=[]
  81. )
  82. class Meta:
  83. model = Token
  84. fields = (
  85. 'id', 'url', 'display', 'user', 'created', 'expires', 'last_used', 'key', 'write_enabled', 'description',
  86. 'allowed_ips',
  87. )
  88. brief_fields = ('id', 'url', 'display', 'key', 'write_enabled', 'description')
  89. def to_internal_value(self, data):
  90. if 'key' not in data:
  91. data['key'] = Token.generate_key()
  92. return super().to_internal_value(data)
  93. def validate(self, data):
  94. # If the Token is being created on behalf of another user, enforce the grant_token permission.
  95. request = self.context.get('request')
  96. token_user = data.get('user')
  97. if token_user and token_user != request.user and not request.user.has_perm('users.grant_token'):
  98. raise PermissionDenied("This user does not have permission to create tokens for other users.")
  99. return super().validate(data)
  100. class TokenProvisionSerializer(TokenSerializer):
  101. user = NestedUserSerializer(
  102. read_only=True
  103. )
  104. username = serializers.CharField(
  105. write_only=True
  106. )
  107. password = serializers.CharField(
  108. write_only=True
  109. )
  110. last_used = serializers.DateTimeField(
  111. read_only=True
  112. )
  113. key = serializers.CharField(
  114. read_only=True
  115. )
  116. class Meta:
  117. model = Token
  118. fields = (
  119. 'id', 'url', 'display', 'user', 'created', 'expires', 'last_used', 'key', 'write_enabled', 'description',
  120. 'allowed_ips', 'username', 'password',
  121. )
  122. def validate(self, data):
  123. # Validate the username and password
  124. username = data.pop('username')
  125. password = data.pop('password')
  126. user = authenticate(request=self.context.get('request'), username=username, password=password)
  127. if user is None:
  128. raise AuthenticationFailed("Invalid username/password")
  129. # Inject the user into the validated data
  130. data['user'] = user
  131. return data
  132. class ObjectPermissionSerializer(ValidatedModelSerializer):
  133. url = serializers.HyperlinkedIdentityField(view_name='users-api:objectpermission-detail')
  134. object_types = ContentTypeField(
  135. queryset=ObjectType.objects.all(),
  136. many=True
  137. )
  138. groups = SerializedPKRelatedField(
  139. queryset=Group.objects.all(),
  140. serializer=NestedGroupSerializer,
  141. required=False,
  142. many=True
  143. )
  144. users = SerializedPKRelatedField(
  145. queryset=get_user_model().objects.all(),
  146. serializer=NestedUserSerializer,
  147. required=False,
  148. many=True
  149. )
  150. class Meta:
  151. model = ObjectPermission
  152. fields = (
  153. 'id', 'url', 'display', 'name', 'description', 'enabled', 'object_types', 'groups', 'users', 'actions',
  154. 'constraints',
  155. )
  156. brief_fields = (
  157. 'id', 'url', 'display', 'name', 'description', 'enabled', 'object_types', 'groups', 'users', 'actions',
  158. )