X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/83082c32efa35720f68c78b7ac5306150a5b1b61..refs/pull/3918/head:/app/Auth/Access/Oidc/OidcService.php diff --git a/app/Auth/Access/Oidc/OidcService.php b/app/Auth/Access/Oidc/OidcService.php index eeacdb732..a9323d423 100644 --- a/app/Auth/Access/Oidc/OidcService.php +++ b/app/Auth/Access/Oidc/OidcService.php @@ -2,20 +2,18 @@ namespace BookStack\Auth\Access\Oidc; -use function auth; +use BookStack\Auth\Access\GroupSyncService; use BookStack\Auth\Access\LoginService; use BookStack\Auth\Access\RegistrationService; use BookStack\Auth\User; use BookStack\Exceptions\JsonDebugException; use BookStack\Exceptions\StoppedAuthenticationException; use BookStack\Exceptions\UserRegistrationException; -use function config; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Cache; use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use Psr\Http\Client\ClientInterface as HttpClient; -use function trans; -use function url; /** * Class OpenIdConnectService @@ -26,15 +24,21 @@ class OidcService protected RegistrationService $registrationService; protected LoginService $loginService; protected HttpClient $httpClient; + protected GroupSyncService $groupService; /** * OpenIdService constructor. */ - public function __construct(RegistrationService $registrationService, LoginService $loginService, HttpClient $httpClient) - { + public function __construct( + RegistrationService $registrationService, + LoginService $loginService, + HttpClient $httpClient, + GroupSyncService $groupService + ) { $this->registrationService = $registrationService; $this->loginService = $loginService; $this->httpClient = $httpClient; + $this->groupService = $groupService; } /** @@ -48,7 +52,6 @@ class OidcService { $settings = $this->getProviderSettings(); $provider = $this->getProvider($settings); - return [ 'url' => $provider->getAuthorizationUrl(), 'state' => $provider->getState(), @@ -117,10 +120,31 @@ class OidcService */ protected function getProvider(OidcProviderSettings $settings): OidcOAuthProvider { - return new OidcOAuthProvider($settings->arrayForProvider(), [ + $provider = new OidcOAuthProvider($settings->arrayForProvider(), [ 'httpClient' => $this->httpClient, 'optionProvider' => new HttpBasicAuthOptionProvider(), ]); + + foreach ($this->getAdditionalScopes() as $scope) { + $provider->addScope($scope); + } + + return $provider; + } + + /** + * Get any user-defined addition/custom scopes to apply to the authentication request. + * + * @return string[] + */ + protected function getAdditionalScopes(): array + { + $scopeConfig = $this->config()['additional_scopes'] ?: ''; + + $scopeArr = explode(',', $scopeConfig); + $scopeArr = array_map(fn (string $scope) => trim($scope), $scopeArr); + + return array_filter($scopeArr); } /** @@ -145,10 +169,32 @@ class OidcService return implode(' ', $displayName); } + /** + * Extract the assigned groups from the id token. + * + * @return string[] + */ + protected function getUserGroups(OidcIdToken $token): array + { + $groupsAttr = $this->config()['groups_claim']; + if (empty($groupsAttr)) { + return []; + } + + $groupsList = Arr::get($token->getAllClaims(), $groupsAttr); + if (!is_array($groupsList)) { + return []; + } + + return array_values(array_filter($groupsList, function ($val) { + return is_string($val); + })); + } + /** * Extract the details of a user from an ID token. * - * @return array{name: string, email: string, external_id: string} + * @return array{name: string, email: string, external_id: string, groups: string[]} */ protected function getUserDetails(OidcIdToken $token): array { @@ -158,6 +204,7 @@ class OidcService 'external_id' => $id, 'email' => $token->getClaim('email'), 'name' => $this->getUserDisplayName($token, $id), + 'groups' => $this->getUserGroups($token), ]; } @@ -209,6 +256,12 @@ class OidcService throw new OidcException($exception->getMessage()); } + if ($this->shouldSyncGroups()) { + $groups = $userDetails['groups']; + $detachExisting = $this->config()['remove_from_groups']; + $this->groupService->syncUserWithFoundGroups($user, $groups, $detachExisting); + } + $this->loginService->login($user, 'oidc'); return $user; @@ -221,4 +274,12 @@ class OidcService { return config('oidc'); } + + /** + * Check if groups should be synced. + */ + protected function shouldSyncGroups(): bool + { + return $this->config()['user_to_groups'] !== false; + } }