1
+ import logging
2
+
1
3
from django .apps import apps
2
4
from django .core .exceptions import ObjectDoesNotExist
3
5
from django .db import transaction
18
20
19
21
from ..models import DABContentType , DABPermission
20
22
from ..remote import RemoteObject
23
+ from .fields import ActorAnsibleIdField
21
24
from .queries import assignment_qs_user_to_obj , assignment_qs_user_to_obj_perm
22
25
26
+ logger = logging .getLogger (__name__ )
27
+
23
28
24
29
class RoleDefinitionSerializer (CommonModelSerializer ):
25
30
permissions = serializers .SlugRelatedField (
@@ -112,17 +117,19 @@ def get_by_ansible_id(self, ansible_id, requesting_user, for_field):
112
117
raise ValidationError ({for_field : msg .format (pk_value = ansible_id )})
113
118
return resource .content_object
114
119
115
- def get_actor_from_data (self , validated_data , requesting_user ):
120
+ def validate (self , attrs ):
121
+ """Validate that exactly one of actor or actor_ansible_id is provided"""
116
122
actor_aid_field = f'{ self .actor_field } _ansible_id'
117
- if validated_data .get (self .actor_field ) and validated_data .get (actor_aid_field ):
123
+
124
+ # Check what was actually provided in the request
125
+ has_actor_in_request = self .actor_field in self .initial_data
126
+ has_actor_aid_in_request = actor_aid_field in self .initial_data
127
+
128
+ # If both actor and actor_ansible_id are present or both not present than we error out
129
+ if has_actor_in_request == has_actor_aid_in_request :
118
130
self .raise_id_fields_error (self .actor_field , actor_aid_field )
119
- elif validated_data .get (self .actor_field ):
120
- actor = validated_data [self .actor_field ]
121
- elif ansible_id := validated_data .get (actor_aid_field ):
122
- actor = self .get_by_ansible_id (ansible_id , requesting_user , for_field = actor_aid_field )
123
- else :
124
- self .raise_id_fields_error (self .actor_field , f'{ self .actor_field } _ansible_id' )
125
- return actor
131
+
132
+ return super ().validate (attrs )
126
133
127
134
def get_object_from_data (self , validated_data , role_definition , requesting_user ):
128
135
obj = None
@@ -145,10 +152,11 @@ def get_object_from_data(self, validated_data, role_definition, requesting_user)
145
152
elif validated_data .get ('object_ansible_id' ):
146
153
obj = self .get_by_ansible_id (validated_data .get ('object_ansible_id' ), requesting_user , for_field = 'object_ansible_id' )
147
154
if permission_registry .content_type_model .objects .get_for_model (obj ) != role_definition .content_type :
155
+ model_name = getattr (role_definition .content_type , 'model' , 'global' )
148
156
raise ValidationError (
149
157
{
150
158
'object_ansible_id' : _ ('Object type of %(model_name)s does not match role type of %(role_definition)s' )
151
- % {'model_name' : obj ._meta .model_name , 'role_definition' : role_definition . content_type . model }
159
+ % {'model_name' : obj ._meta .model_name , 'role_definition' : model_name }
152
160
}
153
161
)
154
162
return obj
@@ -158,7 +166,7 @@ def create(self, validated_data):
158
166
requesting_user = self .context ['view' ].request .user
159
167
160
168
# Resolve actor - team or user
161
- actor = self .get_actor_from_data ( validated_data , requesting_user )
169
+ actor = validated_data [ self .actor_field ]
162
170
163
171
# Resolve object
164
172
obj = self .get_object_from_data (validated_data , rd , requesting_user )
@@ -216,7 +224,8 @@ def _get_summary_fields(self, obj) -> dict[str, dict]:
216
224
217
225
class RoleUserAssignmentSerializer (BaseAssignmentSerializer ):
218
226
actor_field = 'user'
219
- user_ansible_id = serializers .UUIDField (
227
+ user_ansible_id = ActorAnsibleIdField (
228
+ source = 'user' ,
220
229
required = False ,
221
230
help_text = _ ('The resource ID of the user who will receive permissions from this assignment. An alternative to user field.' ),
222
231
allow_null = True , # for ease of use of the browseable API
@@ -232,7 +241,8 @@ def get_actor_queryset(self, requesting_user):
232
241
233
242
class RoleTeamAssignmentSerializer (BaseAssignmentSerializer ):
234
243
actor_field = 'team'
235
- team_ansible_id = serializers .UUIDField (
244
+ team_ansible_id = ActorAnsibleIdField (
245
+ source = 'team' ,
236
246
required = False ,
237
247
help_text = _ ('The resource ID of the team who will receive permissions from this assignment. An alternative to team field.' ),
238
248
allow_null = True ,
@@ -257,9 +267,21 @@ def _get_related(self, obj) -> dict[str, str]:
257
267
return {}
258
268
related_fields = {}
259
269
actor_cls = self .Meta .model
270
+
271
+ # Use ansible_id if available, otherwise fall back to pk
272
+ actor_identifier = obj .pk
273
+ try :
274
+ if hasattr (obj , 'resource' ) and obj .resource :
275
+ actor_identifier = str (obj .resource .ansible_id )
276
+ except ObjectDoesNotExist :
277
+ # Resource doesn't exist, stick with pk
278
+ logger .warning (
279
+ f"No resource for { self .Meta .model } { obj .pk } due to internal error. Linking role-{ actor_cls ._meta .model_name } -access-assignments as pk."
280
+ )
281
+
260
282
related_fields ['details' ] = get_relative_url (
261
283
f'role-{ actor_cls ._meta .model_name } -access-assignments' ,
262
- kwargs = {'model_name' : self .context .get ("content_type" ).api_slug , 'pk' : self .context .get ("related_object" ).pk , 'actor_pk' : obj . pk },
284
+ kwargs = {'model_name' : self .context .get ("content_type" ).api_slug , 'pk' : self .context .get ("related_object" ).pk , 'actor_pk' : actor_identifier },
263
285
)
264
286
return related_fields
265
287
0 commit comments