2929use OCP \IUser ;
3030use OCP \IUserManager ;
3131use OCP \L10N \IFactory ;
32- use OCP \Security \ICrypto ;
3332use OCP \PreConditionNotMetException ;
33+ use OCP \Security \ICrypto ;
3434use OCP \User \Events \UserChangedEvent ;
3535use Psr \Log \LoggerInterface ;
3636use Throwable ;
@@ -53,7 +53,6 @@ public function __construct(
5353 private IFactory $ l10nFactory ,
5454 private ProviderMapper $ providerMapper ,
5555 private ICrypto $ crypto ,
56-
5756 ) {
5857 }
5958
@@ -596,44 +595,13 @@ public function getSyncGroupsOfToken(int $providerId, object $idTokenPayload): ?
596595 $ token = null ;
597596 $ tenant = null ;
598597 if ($ this ->providerService ->getSetting ($ providerId , ProviderService::SETTING_AZURE_GROUP_NAMES , '0 ' ) === '1 ' ) {
599- $ url = $ this ->providerMapper ->getProvider ($ providerId )->getDiscoveryEndpoint ();
600- $ tenant = explode ('// ' , $ url );
601- $ tenant = count ($ tenant ) === 1 ? $ tenant [0 ] : $ tenant [1 ];
602- $ tenant = explode ('/ ' , $ tenant );
603- if (count ($ tenant ) === 1 ) {
604- $ this ->logger ->error ('Could not figure out the tenant id. (Is the discovery endpoint formatted properly?) Will not sync groups ' );
605- return null ;
606- }
607- $ tenant = $ tenant [1 ];
608-
609- $ client = $ this ->clientService ->newClient ();
610- try {
611- $ response = $ client ->post ("https://login.microsoftonline.com/ $ tenant/oauth2/v2.0/token " , [
612- 'headers ' => [ 'Accept ' => 'application/json ' ],
613- 'form_params ' => [
614- 'client_id ' => $ this ->providerMapper ->getProvider ($ providerId )->getClientId (),
615- 'scope ' => 'https://graph.microsoft.com/.default ' ,
616- 'client_secret ' => $ this ->crypto ->decrypt ($ this ->providerMapper ->getProvider ($ providerId )->getClientSecret ()),
617- 'grant_type ' => 'client_credentials '
618- ],
619- 'http_errors ' => false
620- ]);
621- } catch (\Exception $ e ) {
622- $ this ->logger ->error ($ e ->getMessage ());
598+ $ azureGroupSyncContext = $ this ->getAzureGroupSyncContext ($ providerId );
599+ if ($ azureGroupSyncContext === null ) {
623600 return null ;
624601 }
625602
626- $ res = $ response ->getBody ();
627- if (!is_string ($ res )) {
628- $ this ->logger ->error ('Could not fetch Bearer token for Microsoft Graph. Will not sync groups ' );
629- return null ;
630- }
631- $ res = json_decode ($ res , true );
632- if (empty ($ res )) {
633- $ this ->logger ->error ('Could not fetch Bearer token for Microsoft Graph. Will not sync groups ' );
634- return null ;
635- }
636- $ token = $ res ['access_token ' ];
603+ $ tenant = $ azureGroupSyncContext ['tenant ' ];
604+ $ token = $ azureGroupSyncContext ['token ' ];
637605 }
638606
639607 foreach ($ groups as $ k => $ v ) {
@@ -663,49 +631,10 @@ public function getSyncGroupsOfToken(int $providerId, object $idTokenPayload): ?
663631 }
664632
665633 if ($ this ->providerService ->getSetting ($ providerId , ProviderService::SETTING_AZURE_GROUP_NAMES , '0 ' ) === '1 ' && is_string ($ v )) {
666- $ client = $ this ->clientService ->newClient ();
667- try {
668- $ response = $ client ->get (
669- "https://graph.microsoft.com/v1.0/ $ tenant/groups/ " . $ v ,
670- [ 'headers ' => [ 'Accept ' => 'application/json ' , 'Authorization ' => "Bearer $ token " ], 'http_errors ' => false ]
671- );
672- } catch (\Exception $ e ) {
673- $ this ->logger ->error ($ e ->getMessage ());
674- continue ;
675- }
676- $ res = $ response ->getBody ();
677-
678- if (!is_string ($ res )) {
679- $ this ->logger ->error ('No response from Microsoft Graph while fetching group name. Will not sync the group ' . $ v );
680- continue ;
681- }
682- $ res = json_decode ($ res , true ); // https://learn.microsoft.com/en-us/graph/api/group-get?view=graph-rest-1.0&tabs=http#response-1
683-
684- if (isset ($ res ['error ' ])) {
685- $ errorMessage = !empty ($ res ['error ' ]['message ' ]) && is_string ($ res ['error ' ]['message ' ]) ? $ res ['error ' ]['message ' ] : '' ;
686- $ this ->logger ->error ('Error response from Microsoft Graph while fetching group name. Will not sync the group ' . $ v . '. Graph said: ' . $ errorMessage );
634+ $ group = $ this ->getAzureGroupWithResolvedName ($ providerId , $ tenant , $ token , $ v );
635+ if ($ group === null ) {
687636 continue ;
688637 }
689-
690- if (empty ($ res ['displayName ' ])) {
691- $ this ->logger ->error ('Empty response from Microsoft Graph while fetching group name. Will not sync the group ' . $ v );
692- continue ;
693- }
694- $ group = (object )['gid ' => $ res ['displayName ' ]];
695-
696- if ($ this ->providerService ->getSetting ($ providerId , ProviderService::SETTING_PROVIDER_BASED_ID , '0 ' ) === '1 ' ) {
697- $ providerName = $ this ->providerMapper ->getProvider ($ providerId )->getIdentifier ();
698- $ group ->gid = $ providerName . '- ' . $ group ->gid ;
699- }
700- if (strlen ($ group ->gid ) > 64 ) {
701- $ this ->logger ->warning ('Group id ' . $ group ->gid . ' longer than supported. Group id truncated. ' );
702- $ group ->displayName = $ group ->gid ;
703- $ group ->gid = substr ($ group ->gid , 0 , 64 );
704- if (strlen ($ group ->displayName ) > 255 ) {
705- $ this ->logger ->warning ('Group name ' . $ group ->displayName . ' longer than supported. Group name truncated. ' );
706- $ group ->displayName = substr ($ group ->displayName , 0 , 255 );
707- }
708- }
709638 } else {
710639 $ group ->gid = $ this ->idService ->getId ($ providerId , $ group ->gid );
711640 }
@@ -718,6 +647,105 @@ public function getSyncGroupsOfToken(int $providerId, object $idTokenPayload): ?
718647 return null ;
719648 }
720649
650+ /**
651+ * @return array{tenant: string, token: string}|null
652+ */
653+ private function getAzureGroupSyncContext (int $ providerId ): ?array {
654+ $ provider = $ this ->providerMapper ->getProvider ($ providerId );
655+ $ url = $ provider ->getDiscoveryEndpoint ();
656+ $ tenant = explode ('// ' , $ url );
657+ $ tenant = count ($ tenant ) === 1 ? $ tenant [0 ] : $ tenant [1 ];
658+ $ tenant = explode ('/ ' , $ tenant );
659+ if (count ($ tenant ) === 1 ) {
660+ $ this ->logger ->error ('Could not figure out the tenant id. (Is the discovery endpoint formatted properly?) Will not sync groups ' );
661+ return null ;
662+ }
663+ $ tenant = $ tenant [1 ];
664+
665+ $ client = $ this ->clientService ->newClient ();
666+ try {
667+ $ response = $ client ->post ("https://login.microsoftonline.com/ $ tenant/oauth2/v2.0/token " , [
668+ 'headers ' => [ 'Accept ' => 'application/json ' ],
669+ 'form_params ' => [
670+ 'client_id ' => $ provider ->getClientId (),
671+ 'scope ' => 'https://graph.microsoft.com/.default ' ,
672+ 'client_secret ' => $ this ->crypto ->decrypt ($ provider ->getClientSecret ()),
673+ 'grant_type ' => 'client_credentials '
674+ ],
675+ 'http_errors ' => false
676+ ]);
677+ } catch (\Exception $ e ) {
678+ $ this ->logger ->error ($ e ->getMessage ());
679+ return null ;
680+ }
681+
682+ $ res = $ response ->getBody ();
683+ if (!is_string ($ res )) {
684+ $ this ->logger ->error ('Could not fetch Bearer token for Microsoft Graph. Will not sync groups ' );
685+ return null ;
686+ }
687+
688+ $ res = json_decode ($ res , true );
689+ if (empty ($ res ) || empty ($ res ['access_token ' ]) || !is_string ($ res ['access_token ' ])) {
690+ $ this ->logger ->error ('Could not fetch Bearer token for Microsoft Graph. Will not sync groups ' );
691+ return null ;
692+ }
693+
694+ return [
695+ 'tenant ' => $ tenant ,
696+ 'token ' => $ res ['access_token ' ],
697+ ];
698+ }
699+
700+ private function getAzureGroupWithResolvedName (int $ providerId , string $ tenant , string $ token , string $ groupId ): ?object {
701+ $ client = $ this ->clientService ->newClient ();
702+ try {
703+ $ response = $ client ->get (
704+ "https://graph.microsoft.com/v1.0/ $ tenant/groups/ " . $ groupId ,
705+ [ 'headers ' => [ 'Accept ' => 'application/json ' , 'Authorization ' => "Bearer $ token " ], 'http_errors ' => false ]
706+ );
707+ } catch (\Exception $ e ) {
708+ $ this ->logger ->error ($ e ->getMessage ());
709+ return null ;
710+ }
711+
712+ $ res = $ response ->getBody ();
713+ if (!is_string ($ res )) {
714+ $ this ->logger ->error ('No response from Microsoft Graph while fetching group name. Will not sync the group ' . $ groupId );
715+ return null ;
716+ }
717+
718+ $ res = json_decode ($ res , true ); // https://learn.microsoft.com/en-us/graph/api/group-get?view=graph-rest-1.0&tabs=http#response-1
719+ if (isset ($ res ['error ' ])) {
720+ $ errorMessage = !empty ($ res ['error ' ]['message ' ]) && is_string ($ res ['error ' ]['message ' ]) ? $ res ['error ' ]['message ' ] : '' ;
721+ $ this ->logger ->error ('Error response from Microsoft Graph while fetching group name. Will not sync the group ' . $ groupId . '. Graph said: ' . $ errorMessage );
722+ return null ;
723+ }
724+
725+ if (empty ($ res ['displayName ' ])) {
726+ $ this ->logger ->error ('Empty response from Microsoft Graph while fetching group name. Will not sync the group ' . $ groupId );
727+ return null ;
728+ }
729+
730+ $ group = (object )['gid ' => $ res ['displayName ' ]];
731+ if ($ this ->providerService ->getSetting ($ providerId , ProviderService::SETTING_PROVIDER_BASED_ID , '0 ' ) === '1 ' ) {
732+ $ providerName = $ this ->providerMapper ->getProvider ($ providerId )->getIdentifier ();
733+ $ group ->gid = $ providerName . '- ' . $ group ->gid ;
734+ }
735+
736+ if (strlen ($ group ->gid ) > 64 ) {
737+ $ this ->logger ->warning ('Group id ' . $ group ->gid . ' longer than supported. Group id truncated. ' );
738+ $ group ->displayName = $ group ->gid ;
739+ $ group ->gid = substr ($ group ->gid , 0 , 64 );
740+ if (strlen ($ group ->displayName ) > 255 ) {
741+ $ this ->logger ->warning ('Group name ' . $ group ->displayName . ' longer than supported. Group name truncated. ' );
742+ $ group ->displayName = substr ($ group ->displayName , 0 , 255 );
743+ }
744+ }
745+
746+ return $ group ;
747+ }
748+
721749 public function provisionUserGroups (IUser $ user , int $ providerId , object $ idTokenPayload ): ?array {
722750 $ groupsWhitelistRegex = $ this ->getGroupWhitelistRegex ($ providerId );
723751
0 commit comments