@@ -539,18 +539,25 @@ def update_user_display_name(self, user_id, display_name):
539539
540540 raise Exception (f"{ response .status_code } { response .text } " )
541541
542- def link_user_to_local_idp (self , user_id , email_password = False ):
542+ def link_user_to_local_idp (self , user_id , email_password = False , manage_api_key = None , resource_id = None ):
543543 """
544544 Link a user to the local identity provider (IDP).
545545
546546 This method is idempotent - if the user already has a local identity, no action is taken.
547547 The method creates a local authentication identity for the user, enabling them to log in
548548 with username/password.
549549
550+ For MAS version < 9.1: Uses Core API PUT request
551+ For MAS version >= 9.1: Uses Manage API PATCH request
552+
550553 Args:
551554 user_id (str): The unique identifier of the user to link.
552555 email_password (bool, optional): Whether to enable email/password authentication.
553556 Defaults to False.
557+ manage_api_key (dict, optional): API key record with 'apikey' field for authentication.
558+ Required for MAS version >= 9.1.
559+ resource_id (str, optional): The resource identifier of the user (extracted from href).
560+ Required for MAS version >= 9.1.
554561
555562 Returns:
556563 None: Always returns None (authentication token is not exposed).
@@ -563,40 +570,110 @@ def link_user_to_local_idp(self, user_id, email_password=False):
563570 or returned for security reasons.
564571 """
565572
566- # For the sake of idempotency, check if the user already has a local identity
567- resource_id , user = self .get_user (user_id )
568- if user is None :
569- raise Exception (f"User { user_id } was not found" )
573+ # Check MAS version to determine which API to use
574+ current_version = Version (self .mas_version )
575+ version_9_1 = Version ("9.1" )
570576
571- if "identities" in user and "_local" in user ["identities" ]:
572- self .logger .info (f"User { user_id } already has a local identity" )
573- return None
577+ if current_version >= version_9_1 :
578+ # Version >= 9.1: Use Manage API PATCH request
579+ if manage_api_key is None :
580+ raise Exception ("manage_api_key is required for MAS version >= 9.1" )
581+ if resource_id is None :
582+ raise Exception ("resource_id is required for MAS version >= 9.1" )
574583
575- self .logger .info (f"Linking user { user_id } to local IDP (email_password: { email_password } )" )
576- url = f"{ self .mas_api_url_internal } /v3/users/{ user_id } /idps/local"
577- querystring = {
578- "emailPassword" : email_password
579- }
580- payload = {
581- "idpUserId" : user_id ,
582- }
583- headers = {
584- "Content-Type" : "application/json" ,
585- "x-access-token" : self .superuser_auth_token
586- }
587- response = requests .put (
588- url ,
589- json = payload ,
590- headers = headers ,
591- params = querystring ,
592- verify = self .core_internal_ca_pem_file_path
593- )
594- if response .status_code != 200 :
595- raise Exception (response .text )
584+ # For the sake of idempotency, check if the user already has a local identity
585+ _ , user = self .get_user (user_id )
586+ if user is None :
587+ raise Exception (f"User { user_id } was not found" )
596588
597- # Important: HTTP 200 output will contain generated user token; DO NOT LOG
589+ if "identities" in user and "_local" in user ["identities" ]:
590+ self .logger .info (f"User { user_id } already has a local identity" )
591+ return None
598592
599- return None
593+ self .logger .info (f"Linking user { user_id } to local IDP using Manage API (version { self .mas_version } )" )
594+
595+ url = f"{ self .manage_api_url_internal } /maximo/api/os/masperuser/{ resource_id } "
596+ querystring = {
597+ "lean" : 1 ,
598+ "ccm" : 1
599+ }
600+ headers = {
601+ "Content-Type" : "application/json" ,
602+ "apikey" : manage_api_key ["apikey" ],
603+ "x-method-override" : "PATCH" ,
604+ "patchtype" : "MERGE"
605+ }
606+
607+ payload = {
608+ "maxuser" : {
609+ "userid" : user_id ,
610+ "masuseridp" : [
611+ {
612+ "emailpassword" : email_password ,
613+ "idpid" : "local" ,
614+ "logintype" : "0" ,
615+ "idploginid" : user_id ,
616+ "idptype" : "local" ,
617+ "enabled" : True
618+ }
619+ ]
620+ }
621+ }
622+ self .logger .info (f"Sending PATCH request to { url } with payload: { payload } " )
623+
624+ response = requests .post (
625+ url ,
626+ json = payload ,
627+ headers = headers ,
628+ params = querystring ,
629+ cert = self .manage_internal_client_pem_file_path ,
630+ verify = self .manage_internal_ca_pem_file_path
631+ )
632+ self .logger .info (f"Response status code: { response .status_code } " )
633+ self .logger .info (f"Response text: { response .text } " )
634+
635+ if response .status_code in [200 , 204 ]:
636+ self .logger .info (f"Successfully linked user { user_id } to local IDP" )
637+ return None
638+
639+ raise Exception (f"Failed to link user to local IDP: { response .status_code } { response .text } " )
640+
641+ else :
642+ # Version < 9.1: Use Core API PUT request (existing implementation)
643+ # For the sake of idempotency, check if the user already has a local identity
644+ _ , user = self .get_user (user_id )
645+ if user is None :
646+ raise Exception (f"User { user_id } was not found" )
647+
648+ if "identities" in user and "_local" in user ["identities" ]:
649+ self .logger .info (f"User { user_id } already has a local identity" )
650+ return None
651+
652+ self .logger .info (f"Linking user { user_id } to local IDP using Core API (version { self .mas_version } , email_password: { email_password } )" )
653+ url = f"{ self .mas_api_url_internal } /v3/users/{ user_id } /idps/local"
654+ querystring = {
655+ "emailPassword" : email_password
656+ }
657+ payload = {
658+ "idpUserId" : user_id ,
659+ }
660+ headers = {
661+ "Content-Type" : "application/json" ,
662+ "x-access-token" : self .superuser_auth_token
663+ }
664+ response = requests .put (
665+ url ,
666+ json = payload ,
667+ headers = headers ,
668+ params = querystring ,
669+ verify = self .core_internal_ca_pem_file_path
670+ )
671+ if response .status_code != 200 :
672+ raise Exception (response .text )
673+
674+ # Important: HTTP 200 output will contain generated user token; DO NOT LOG
675+
676+ return None
600677
601678 def get_user_workspaces (self , user_id ):
602679 """
@@ -1690,7 +1767,17 @@ def create_initial_user_for_saas(self, user, user_type, groupreassign=None):
16901767 self .logger .info (f"Resource ID - { resource_id } " )
16911768 self .logger .info (f"User info - { user_info } " )
16921769
1693- self .link_user_to_local_idp (user_id , email_password = True )
1770+ # For version >= 9.1, we always need a Manage API key and resource_id to link user to local IDP
1771+ # For version < 9.1, we may need it later for manage_security_groups
1772+ if Version (self .mas_version ) >= Version ('9.1' ) or (len (manage_security_groups ) > 0 and "manage" in self .mas_workspace_application_ids ):
1773+ maxadmin_manage_api_key = self .create_or_get_manage_api_key_for_user (MASUserUtils .MAXADMIN , temporary = True )
1774+ self .logger .info (f"Maxadmin manage api key - { maxadmin_manage_api_key } " )
1775+
1776+ if Version (self .mas_version ) >= Version ('9.1' ):
1777+ self .link_user_to_local_idp (user_id , email_password = True , manage_api_key = maxadmin_manage_api_key , resource_id = resource_id )
1778+ else :
1779+ self .link_user_to_local_idp (user_id , email_password = True )
1780+
16941781 self .add_user_to_workspace (user_id , is_workspace_admin = is_workspace_admin )
16951782
16961783 if Version (self .mas_version ) < Version ('9.1' ):
@@ -1709,8 +1796,6 @@ def create_initial_user_for_saas(self, user, user_type, groupreassign=None):
17091796 self .check_user_sync (user_id , mas_application_id )
17101797
17111798 if len (manage_security_groups ) > 0 and "manage" in self .mas_workspace_application_ids :
1712- maxadmin_manage_api_key = self .create_or_get_manage_api_key_for_user (MASUserUtils .MAXADMIN , temporary = True )
1713- self .logger .info (f"Maxadmin manage api key - { maxadmin_manage_api_key } " )
17141799 if Version (self .mas_version ) < Version ('9.1' ):
17151800 for manage_security_group in manage_security_groups :
17161801 self .add_user_to_manage_group (user_id , manage_security_group , maxadmin_manage_api_key )
@@ -1719,7 +1804,3 @@ def create_initial_user_for_saas(self, user, user_type, groupreassign=None):
17191804 self .set_user_group_reassignment_auth (user_id , resource_id , groupreassign , maxadmin_manage_api_key )
17201805 else :
17211806 self .logger .warning (f"Cannot set group reassignment auth: resource_id not found for user { user_id } " )
1722-
1723- # # Grant authorization to reassign users to/from ALL security groups (PRIMARY users only)
1724- # if user_type == "PRIMARY":
1725- # self.grant_all_group_reassignment_auth(user_id, maxadmin_manage_api_key)
0 commit comments