3535}
3636
3737
38+ OPENSHIFT_ROLES = ["admin" , "edit" , "view" ]
39+
40+
3841def clean_openshift_metadata (obj ):
3942 if "metadata" in obj :
4043 for attr in IGNORED_ATTRIBUTES :
@@ -253,25 +256,51 @@ def create_federated_user(self, unique_id):
253256 logger .info (f"User { unique_id } successfully created" )
254257
255258 def assign_role_on_user (self , username , project_id ):
256- # /users/<user_name>/projects/<project>/roles/<role>
257- url = (
258- f"{ self .auth_url } /users/{ username } /projects/{ project_id } "
259- f"/roles/{ self .member_role_name } "
260- )
259+ """Assign a role to a user in a project using direct OpenShift API calls"""
261260 try :
262- r = self .session .put (url )
263- self .check_response (r )
264- except Conflict :
261+ # Try to get existing rolebindings with same name
262+ # as the role name in project's namespace
263+ rolebinding = self ._openshift_get_rolebindings (
264+ project_id , self .member_role_name
265+ )
266+
267+ if not self ._user_in_rolebinding (username , rolebinding ):
268+ # Add user to existing rolebinding
269+ if "subjects" not in rolebinding :
270+ rolebinding ["subjects" ] = []
271+ rolebinding ["subjects" ].append ({"kind" : "User" , "name" : username })
272+ self ._openshift_update_rolebindings (project_id , rolebinding )
273+
274+ except kexc .NotFoundError :
275+ # Create new rolebinding if it doesn't exist
276+ self ._openshift_create_rolebindings (
277+ project_id , username , self .member_role_name
278+ )
279+ except kexc .ConflictError :
280+ # Role already exists, ignore
265281 pass
266282
267283 def remove_role_from_user (self , username , project_id ):
268- # /users/<user_name>/projects/<project>/roles/<role>
269- url = (
270- f"{ self .auth_url } /users/{ username } /projects/{ project_id } "
271- f"/roles/{ self .member_role_name } "
272- )
273- r = self .session .delete (url )
274- self .check_response (r )
284+ """Remove a role from a user in a project using direct OpenShift API calls"""
285+ try :
286+ rolebinding = self ._openshift_get_rolebindings (
287+ project_id , self .member_role_name
288+ )
289+
290+ if "subjects" in rolebinding :
291+ rolebinding ["subjects" ] = [
292+ subject
293+ for subject in rolebinding ["subjects" ]
294+ if not (
295+ subject .get ("kind" ) == "User"
296+ and subject .get ("name" ) == username
297+ )
298+ ]
299+ self ._openshift_update_rolebindings (project_id , rolebinding )
300+
301+ except kexc .NotFoundError :
302+ # Rolebinding doesn't exist, nothing to remove
303+ pass
275304
276305 def _create_project (self , project_name , project_id ):
277306 url = f"{ self .auth_url } /projects/{ project_id } "
@@ -290,13 +319,13 @@ def _create_project(self, project_name, project_id):
290319 self .check_response (r )
291320
292321 def _get_role (self , username , project_id ):
293- # /users/<user_name>/projects/<project>/roles/<role>
294- url = (
295- f"{ self .auth_url } /users/{ username } /projects/{ project_id } "
296- f"/roles/{ self .member_role_name } "
322+ rolebindings = self ._openshift_get_rolebindings (
323+ project_id , self .member_role_name
297324 )
298- r = self .session .get (url )
299- return self .check_response (r )
325+ if not self ._user_in_rolebinding (username , rolebindings ):
326+ raise NotFound (
327+ f"User { username } has no rolebindings in project { project_id } "
328+ )
300329
301330 def _get_project (self , project_id ):
302331 url = f"{ self .auth_url } /projects/{ project_id } "
@@ -308,10 +337,24 @@ def _delete_user(self, username):
308337 self ._openshift_delete_identity (username )
309338 logger .info (f"User { username } successfully deleted" )
310339
311- def get_users (self , project_id ): # TODO (Quan) This should also replaced
312- url = f"{ self .auth_url } /projects/{ project_id } /users"
313- r = self .session .get (url )
314- return set (self .check_response (r ))
340+ def get_users (self , project_id ):
341+ """Get all users with roles in a project"""
342+ users = set ()
343+
344+ # Check all standard OpenShift roles
345+ for role in OPENSHIFT_ROLES :
346+ try :
347+ rolebinding = self ._openshift_get_rolebindings (project_id , role )
348+ if "subjects" in rolebinding :
349+ users .update (
350+ subject ["name" ]
351+ for subject in rolebinding ["subjects" ]
352+ if subject .get ("kind" ) == "User"
353+ )
354+ except kexc .NotFoundError :
355+ continue
356+
357+ return users
315358
316359 def _openshift_get_user (self , username ):
317360 api = self .get_resource_api (API_USER , "User" )
@@ -446,3 +489,51 @@ def _openshift_delete_resourcequota(self, project_id, resourcequota_name):
446489 """In an openshift namespace {project_id) delete a specified resourcequota"""
447490 api = self .get_resource_api (API_CORE , "ResourceQuota" )
448491 return api .delete (namespace = project_id , name = resourcequota_name ).to_dict ()
492+
493+ def _user_in_rolebinding (self , username , rolebinding ):
494+ """Check if a user is in a rolebinding"""
495+ if "subjects" not in rolebinding :
496+ return False
497+
498+ return any (
499+ subject .get ("kind" ) == "User" and subject .get ("name" ) == username
500+ for subject in rolebinding ["subjects" ]
501+ )
502+
503+ def _openshift_get_rolebindings (self , project_name , role ):
504+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
505+ result = clean_openshift_metadata (
506+ api .get (namespace = project_name , name = role ).to_dict ()
507+ )
508+
509+ # Ensure subjects is a list
510+ if not result .get ("subjects" ):
511+ result ["subjects" ] = []
512+
513+ return result
514+
515+ def _openshift_create_rolebindings (self , project_name , username , role ):
516+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
517+ payload = {
518+ "metadata" : {"name" : role , "namespace" : project_name },
519+ "subjects" : [{"name" : username , "kind" : "User" }],
520+ "roleRef" : {"name" : role , "kind" : "ClusterRole" },
521+ }
522+ return clean_openshift_metadata (
523+ api .create (body = payload , namespace = project_name ).to_dict ()
524+ )
525+
526+ def _openshift_update_rolebindings (self , project_name , rolebinding ):
527+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
528+ return clean_openshift_metadata (
529+ api .patch (body = rolebinding , namespace = project_name ).to_dict ()
530+ )
531+
532+ def _openshift_list_rolebindings (self , project_name ):
533+ """List all rolebindings in a project"""
534+ api = self .get_resource_api (API_RBAC , "RoleBinding" )
535+ try :
536+ result = clean_openshift_metadata (api .get (namespace = project_name ).to_dict ())
537+ return result .get ("items" , [])
538+ except kexc .NotFoundError :
539+ return []
0 commit comments