4545]
4646
4747
48+ OPENSHIFT_ROLES = ["admin" , "edit" , "view" ]
49+
50+
4851def clean_openshift_metadata (obj ):
4952 if "metadata" in obj :
5053 for attr in IGNORED_ATTRIBUTES :
@@ -262,25 +265,51 @@ def create_federated_user(self, unique_id):
262265 logger .info (f"User { unique_id } successfully created" )
263266
264267 def assign_role_on_user (self , username , project_id ):
265- # /users/<user_name>/projects/<project>/roles/<role>
266- url = (
267- f"{ self .auth_url } /users/{ username } /projects/{ project_id } "
268- f"/roles/{ self .member_role_name } "
269- )
268+ """Assign a role to a user in a project using direct OpenShift API calls"""
270269 try :
271- r = self .session .put (url )
272- self .check_response (r )
273- except Conflict :
270+ # Try to get existing rolebindings with same name
271+ # as the role name in project's namespace
272+ rolebinding = self ._openshift_get_rolebindings (
273+ project_id , self .member_role_name
274+ )
275+
276+ if not self ._user_in_rolebinding (username , rolebinding ):
277+ # Add user to existing rolebinding
278+ if "subjects" not in rolebinding :
279+ rolebinding ["subjects" ] = []
280+ rolebinding ["subjects" ].append ({"kind" : "User" , "name" : username })
281+ self ._openshift_update_rolebindings (project_id , rolebinding )
282+
283+ except kexc .NotFoundError :
284+ # Create new rolebinding if it doesn't exist
285+ self ._openshift_create_rolebindings (
286+ project_id , username , self .member_role_name
287+ )
288+ except kexc .ConflictError :
289+ # Role already exists, ignore
274290 pass
275291
276292 def remove_role_from_user (self , username , project_id ):
277- # /users/<user_name>/projects/<project>/roles/<role>
278- url = (
279- f"{ self .auth_url } /users/{ username } /projects/{ project_id } "
280- f"/roles/{ self .member_role_name } "
281- )
282- r = self .session .delete (url )
283- self .check_response (r )
293+ """Remove a role from a user in a project using direct OpenShift API calls"""
294+ try :
295+ rolebinding = self ._openshift_get_rolebindings (
296+ project_id , self .member_role_name
297+ )
298+
299+ if "subjects" in rolebinding :
300+ rolebinding ["subjects" ] = [
301+ subject
302+ for subject in rolebinding ["subjects" ]
303+ if not (
304+ subject .get ("kind" ) == "User"
305+ and subject .get ("name" ) == username
306+ )
307+ ]
308+ self ._openshift_update_rolebindings (project_id , rolebinding )
309+
310+ except kexc .NotFoundError :
311+ # Rolebinding doesn't exist, nothing to remove
312+ pass
284313
285314 def _create_project (self , project_name , project_id ):
286315 pi_username = self .allocation .project .pi .username
@@ -306,13 +335,13 @@ def _create_project(self, project_name, project_id):
306335 logger .info (f"Project { project_id } and limit range successfully created" )
307336
308337 def _get_role (self , username , project_id ):
309- # /users/<user_name>/projects/<project>/roles/<role>
310- url = (
311- f"{ self .auth_url } /users/{ username } /projects/{ project_id } "
312- f"/roles/{ self .member_role_name } "
338+ rolebindings = self ._openshift_get_rolebindings (
339+ project_id , self .member_role_name
313340 )
314- r = self .session .get (url )
315- return self .check_response (r )
341+ if not self ._user_in_rolebinding (username , rolebindings ):
342+ raise NotFound (
343+ f"User { username } has no rolebindings in project { project_id } "
344+ )
316345
317346 def _get_project (self , project_id ):
318347 return self ._openshift_get_project (project_id )
@@ -323,9 +352,23 @@ def _delete_user(self, username):
323352 logger .info (f"User { username } successfully deleted" )
324353
325354 def get_users (self , project_id ):
326- url = f"{ self .auth_url } /projects/{ project_id } /users"
327- r = self .session .get (url )
328- return set (self .check_response (r ))
355+ """Get all users with roles in a project"""
356+ users = set ()
357+
358+ # Check all standard OpenShift roles
359+ for role in OPENSHIFT_ROLES :
360+ try :
361+ rolebinding = self ._openshift_get_rolebindings (project_id , role )
362+ if "subjects" in rolebinding :
363+ users .update (
364+ subject ["name" ]
365+ for subject in rolebinding ["subjects" ]
366+ if subject .get ("kind" ) == "User"
367+ )
368+ except kexc .NotFoundError :
369+ continue
370+
371+ return users
329372
330373 def _openshift_get_user (self , username ):
331374 api = self .get_resource_api (API_USER , "User" )
@@ -488,3 +531,51 @@ def _openshift_delete_resourcequota(self, project_id, resourcequota_name):
488531 """In an openshift namespace {project_id) delete a specified resourcequota"""
489532 api = self .get_resource_api (API_CORE , "ResourceQuota" )
490533 return api .delete (namespace = project_id , name = resourcequota_name ).to_dict ()
534+
535+ def _user_in_rolebinding (self , username , rolebinding ):
536+ """Check if a user is in a rolebinding"""
537+ if "subjects" not in rolebinding :
538+ return False
539+
540+ return any (
541+ subject .get ("kind" ) == "User" and subject .get ("name" ) == username
542+ for subject in rolebinding ["subjects" ]
543+ )
544+
545+ def _openshift_get_rolebindings (self , project_name , role ):
546+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
547+ result = clean_openshift_metadata (
548+ api .get (namespace = project_name , name = role ).to_dict ()
549+ )
550+
551+ # Ensure subjects is a list
552+ if not result .get ("subjects" ):
553+ result ["subjects" ] = []
554+
555+ return result
556+
557+ def _openshift_create_rolebindings (self , project_name , username , role ):
558+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
559+ payload = {
560+ "metadata" : {"name" : role , "namespace" : project_name },
561+ "subjects" : [{"name" : username , "kind" : "User" }],
562+ "roleRef" : {"name" : role , "kind" : "ClusterRole" },
563+ }
564+ return clean_openshift_metadata (
565+ api .create (body = payload , namespace = project_name ).to_dict ()
566+ )
567+
568+ def _openshift_update_rolebindings (self , project_name , rolebinding ):
569+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
570+ return clean_openshift_metadata (
571+ api .patch (body = rolebinding , namespace = project_name ).to_dict ()
572+ )
573+
574+ def _openshift_list_rolebindings (self , project_name ):
575+ """List all rolebindings in a project"""
576+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
577+ try :
578+ result = clean_openshift_metadata (api .get (namespace = project_name ).to_dict ())
579+ return result .get ("items" , [])
580+ except kexc .NotFoundError :
581+ return []
0 commit comments