@@ -46,6 +46,51 @@ def get_content_type(self, apps):
4646 content_type_cls = apps .get_model ('contenttypes' , 'ContentType' )
4747 return content_type_cls .objects .get_for_model (model )
4848
49+ def refresh_permissions (self , rd , apps ):
50+ """Make role permissions equal what the managed definition specifies"""
51+ permission_cls = apps .get_model ('dab_rbac' , 'DABPermission' )
52+
53+ # Desired permission codenames from managed source
54+ desired_codenames = set (self .get_permissions (apps ))
55+
56+ # Resolve to actual permission objects or error
57+ desired_permissions = []
58+ for codename in desired_codenames :
59+ try :
60+ if '.' in codename :
61+ perm = permission_cls .objects .get (api_slug = codename )
62+ else :
63+ perm = permission_cls .objects .get (codename = codename )
64+ desired_permissions .append (perm )
65+ except permission_cls .DoesNotExist :
66+ db_codenames = list (permission_cls .objects .values_list ('codename' , flat = True ))
67+ raise permission_cls .DoesNotExist (
68+ f'Permission codename "{ codename } " does not exist.\n '
69+ f'Managed role: { self } \n Expected: { sorted (desired_codenames )} \n '
70+ f'Available in DB: { sorted (db_codenames )} '
71+ )
72+
73+ desired_set = set (desired_permissions )
74+ current_set = set (rd .permissions .all ())
75+
76+ to_add = desired_set - current_set
77+ to_remove = current_set - desired_set
78+
79+ if not to_add and not to_remove :
80+ logger .info (f'No permission changes needed for role "{ self .name } "' )
81+ else :
82+ if to_add :
83+ rd .permissions .add (* to_add )
84+ added_codenames = sorted (p .codename for p in to_add )
85+ logger .info (f'Added permissions to role "{ self .name } ": { added_codenames } ' )
86+
87+ if to_remove :
88+ rd .permissions .remove (* to_remove )
89+ removed_codenames = sorted (p .codename for p in to_remove )
90+ logger .info (f'Removed permissions from role "{ self .name } ": { removed_codenames } ' )
91+
92+ logger .debug (f'Final permissions for role "{ self .name } ": ' f'{ sorted (p .codename for p in rd .permissions .all ())} ' )
93+
4994 def get_or_create (self , apps ):
5095 "Create from a list of text-type permissions and do validation"
5196 role_definition_cls = apps .get_model ('dab_rbac' , 'RoleDefinition' )
@@ -57,36 +102,33 @@ def get_or_create(self, apps):
57102 rd , created = role_definition_cls .objects .get_or_create (name = self .name , defaults = defaults )
58103
59104 if created :
60- permissions = self .get_permissions (apps )
61- permission_cls = apps .get_model ('dab_rbac' , 'DABPermission' )
62- perm_list = []
63- for str_perm in permissions :
64- try :
65- perm_list .append (permission_cls .objects .get (codename = str_perm ))
66- except permission_cls .DoesNotExist :
67- # Better error handling for debugging
68- db_codenames = list (permission_cls .objects .values_list ('codename' , flat = True ))
69- raise permission_cls .DoesNotExist (
70- f'Permission codename { str_perm } does not exist. Manged role { self } \n expected: { permissions } \n Database permissions: { db_codenames } '
71- )
72- rd .permissions .add (* perm_list )
73- logger .info (f'Created { self .shortname } managed role definition, name={ self .name } ' )
105+ self .refresh_permissions (rd , apps = apps )
74106 logger .debug (f'Data of { self .name } role definition: { defaults } ' )
75- logger .debug (f'Permissions of { self .name } role definition: { permissions } ' )
76107 return rd , created
77108
78- def allowed_permissions (self , model : Optional [Type [Model ]]) -> set [str ]:
79- from ansible_base .rbac .validators import combine_values , permissions_allowed_for_role
109+ def allowed_permissions_by_model (self , model : Optional [Type [Model ]]) -> dict [Type , list [str ]]:
110+ from ansible_base .rbac .validators import permissions_allowed_for_role
111+
112+ return permissions_allowed_for_role (model )
113+
114+ def allowed_permissions_slug_list (self , model : Optional [Type [Model ]]) -> set [str ]:
115+ "Returns all possible permissions for model in terms format of awx.change_inventory"
116+ from ansible_base .rbac .remote import get_resource_prefix
80117
81- return combine_values (permissions_allowed_for_role (model ))
118+ slug_list = set ()
119+ for child_model , child_codenames in self .allowed_permissions_by_model (model ).items ():
120+ prefix = get_resource_prefix (child_model )
121+ for codename in child_codenames :
122+ slug_list .add (f'{ prefix } .{ codename } ' )
123+ return slug_list
82124
83125
84126class ManagedAdminBase (ManagedRoleConstructor ):
85127 description = gettext_noop ("Has all permissions to a single {model_name_verbose}" )
86128
87129 def get_permissions (self , apps ) -> set [str ]:
88130 """All permissions possible for the associated model"""
89- return self .allowed_permissions (self .get_model (apps ))
131+ return self .allowed_permissions_slug_list (self .get_model (apps ))
90132
91133
92134class ManagedActionBase (ManagedRoleConstructor ):
@@ -108,7 +150,7 @@ class ManagedReadOnlyBase(ManagedRoleConstructor):
108150 description = gettext_noop ("Has all viewing related permissions that can be delegated via {model_name_verbose}" )
109151
110152 def get_permissions (self , apps ) -> set [str ]:
111- return {codename for codename in self .allowed_permissions (self .get_model (apps )) if codename . startswith ( ' view') }
153+ return {api_slug for api_slug in self .allowed_permissions_slug_list (self .get_model (apps )) if '. view' in api_slug }
112154
113155
114156class OrganizationMixin :
0 commit comments