Skip to content

Commit 9079ef3

Browse files
committed
isolate azure-specific group logic in 2 methods
Signed-off-by: Julien Veyssier <julien-nc@posteo.net>
1 parent 8b2dd9d commit 9079ef3

1 file changed

Lines changed: 106 additions & 78 deletions

File tree

lib/Service/ProvisioningService.php

Lines changed: 106 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
use OCP\IUser;
3030
use OCP\IUserManager;
3131
use OCP\L10N\IFactory;
32-
use OCP\Security\ICrypto;
3332
use OCP\PreConditionNotMetException;
33+
use OCP\Security\ICrypto;
3434
use OCP\User\Events\UserChangedEvent;
3535
use Psr\Log\LoggerInterface;
3636
use 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

Comments
 (0)