From: Dan Brown Date: Thu, 21 Oct 2021 13:04:23 +0000 (+0100) Subject: Merge branch 'master' of https://github.com/theodor-franke/BookStack into theodor... X-Git-Tag: v21.10~1^2~7 X-Git-Url: http://source.bookstackapp.com/bookstack/commitdiff_plain/2e9ac21b383cad6f120a05130cda9d32e54695a5?ds=inline;hp=-c Merge branch 'master' of https://github.com/theodor-franke/BookStack into theodor-franke-master --- 2e9ac21b383cad6f120a05130cda9d32e54695a5 diff --combined .env.example.complete index 418875165,58e4e4754..a29afaafd --- a/.env.example.complete +++ b/.env.example.complete @@@ -42,14 -42,6 +42,14 @@@ APP_TIMEZONE=UT # overrides can be made. Defaults to disabled. APP_THEME=false +# Trusted Proxies +# Used to indicate trust of systems that proxy to the application so +# certain header values (Such as "X-Forwarded-For") can be used from the +# incoming proxy request to provide origin detail. +# Set to an IP address, or multiple comma seperated IP addresses. +# Can alternatively be set to "*" to trust all proxy addresses. +APP_PROXIES=null + # Database details # Host can contain a port (localhost:3306) or a separate DB_PORT option can be used. DB_HOST=localhost @@@ -232,6 -224,11 +232,11 @@@ SAML2_ONELOGIN_OVERRIDES=nul SAML2_DUMP_USER_DETAILS=false SAML2_AUTOLOAD_METADATA=false SAML2_IDP_AUTHNCONTEXT=true + SAML2_SP_CERTIFICATE=null + SAML2_SP_PRIVATEKEY=null + SAML2_SP_NAME_ID_Format=null + SAML2_SP_NAME_ID_SP_NAME_QUALIFIER=null + SAML2_RETRIEVE_PARAMETERS_FROM_SERVER=false # SAML group sync configuration # Refer to https://www.bookstackapp.com/docs/admin/saml2-auth/ @@@ -239,18 -236,6 +244,18 @@@ SAML2_USER_TO_GROUPS=fals SAML2_GROUP_ATTRIBUTE=group SAML2_REMOVE_FROM_GROUPS=false +# OpenID Connect authentication configuration +OIDC_NAME=SSO +OIDC_DISPLAY_NAME_CLAIMS=name +OIDC_CLIENT_ID=null +OIDC_CLIENT_SECRET=null +OIDC_ISSUER=null +OIDC_ISSUER_DISCOVER=false +OIDC_PUBLIC_KEY=null +OIDC_AUTH_ENDPOINT=null +OIDC_TOKEN_ENDPOINT=null +OIDC_DUMP_USER_DETAILS=false + # Disable default third-party services such as Gravatar and Draw.IO # Service-specific options will override this option DISABLE_EXTERNAL_SERVICES=false @@@ -301,12 -286,6 +306,12 @@@ ALLOW_CONTENT_SCRIPTS=fals # Contents of the robots.txt file can be overridden, making this option obsolete. ALLOW_ROBOTS=null +# Allow server-side fetches to be performed to potentially unknown +# and user-provided locations. Primarily used in exports when loading +# in externally referenced assets. +# Can be 'true' or 'false'. +ALLOW_UNTRUSTED_SERVER_FETCHING=false + # A list of hosts that BookStack can be iframed within. # Space separated if multiple. BookStack host domain is auto-inferred. # For Example: ALLOWED_IFRAME_HOSTS="https://example.com https://a.example.com" diff --combined app/Auth/Access/Saml2Service.php index 9c208832a,339701d27..58f999709 --- a/app/Auth/Access/Saml2Service.php +++ b/app/Auth/Access/Saml2Service.php @@@ -8,6 -8,7 +8,6 @@@ use BookStack\Exceptions\SamlException use BookStack\Exceptions\StoppedAuthenticationException; use BookStack\Exceptions\UserRegistrationException; use Exception; -use Illuminate\Support\Str; use OneLogin\Saml2\Auth; use OneLogin\Saml2\Error; use OneLogin\Saml2\IdPMetadataParser; @@@ -17,25 -18,20 +17,25 @@@ use OneLogin\Saml2\ValidationError * Class Saml2Service * Handles any app-specific SAML tasks. */ -class Saml2Service extends ExternalAuthService +class Saml2Service { protected $config; protected $registrationService; protected $loginService; + protected $groupSyncService; /** * Saml2Service constructor. */ - public function __construct(RegistrationService $registrationService, LoginService $loginService) - { + public function __construct( + RegistrationService $registrationService, + LoginService $loginService, + GroupSyncService $groupSyncService + ) { $this->config = config('saml2'); $this->registrationService = $registrationService; $this->loginService = $loginService; + $this->groupSyncService = $groupSyncService; } /** @@@ -65,7 -61,11 +65,11 @@@ $returnRoute = url('/'); try { - $url = $toolKit->logout($returnRoute, [], null, null, true); + $email = auth()->user()['email']; + $nameIdFormat = env('SAML2_SP_NAME_ID_Format', null); + $nameIdSPNameQualifier = env('SAML2_SP_NAME_ID_SP_NAME_QUALIFIER', null); + + $url = $toolKit->logout($returnRoute, [], $email, null, true, $nameIdFormat, null, $nameIdSPNameQualifier); $id = $toolKit->getLastRequestID(); } catch (Error $error) { if ($error->getCode() !== Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED) { @@@ -91,11 -91,8 +95,11 @@@ * @throws JsonDebugException * @throws UserRegistrationException */ - public function processAcsResponse(?string $requestId): ?User + public function processAcsResponse(string $requestId, string $samlResponse): ?User { + // The SAML2 toolkit expects the response to be within the $_POST superglobal + // so we need to manually put it back there at this point. + $_POST['SAMLResponse'] = $samlResponse; $toolkit = $this->getToolkit(); $toolkit->processResponse($requestId); $errors = $toolkit->getErrors(); @@@ -124,7 -121,9 +128,9 @@@ public function processSlsResponse(?string $requestId): ?string { $toolkit = $this->getToolkit(); - $redirect = $toolkit->processSLO(true, $requestId, false, null, true); + $retrieveParametersFromServer = env('SAML2_RETRIEVE_PARAMETERS_FROM_SERVER', false); + + $redirect = $toolkit->processSLO(true, $requestId, $retrieveParametersFromServer, null, true); $errors = $toolkit->getErrors(); @@@ -265,8 -264,6 +271,8 @@@ /** * Extract the details of a user from a SAML response. + * + * @return array{external_id: string, name: string, email: string, saml_id: string} */ protected function getUserDetails(string $samlID, $samlAttributes): array { @@@ -331,6 -328,31 +337,6 @@@ return $defaultValue; } - /** - * Get the user from the database for the specified details. - * - * @throws UserRegistrationException - */ - protected function getOrRegisterUser(array $userDetails): ?User - { - $user = User::query() - ->where('external_auth_id', '=', $userDetails['external_id']) - ->first(); - - if (is_null($user)) { - $userData = [ - 'name' => $userDetails['name'], - 'email' => $userDetails['email'], - 'password' => Str::random(32), - 'external_auth_id' => $userDetails['external_id'], - ]; - - $user = $this->registrationService->registerUser($userData, null, false); - } - - return $user; - } - /** * Process the SAML response for a user. Login the user when * they exist, optionally registering them automatically. @@@ -361,19 -383,14 +367,19 @@@ throw new SamlException(trans('errors.saml_already_logged_in'), '/login'); } - $user = $this->getOrRegisterUser($userDetails); + $user = $this->registrationService->findOrRegister( + $userDetails['name'], + $userDetails['email'], + $userDetails['external_id'] + ); + if ($user === null) { throw new SamlException(trans('errors.saml_user_not_registered', ['name' => $userDetails['external_id']]), '/login'); } if ($this->shouldSyncGroups()) { $groups = $this->getUserGroups($samlAttributes); - $this->syncWithGroups($user, $groups); + $this->groupSyncService->syncUserWithFoundGroups($user, $groups, $this->config['remove_from_groups']); } $this->loginService->login($user, 'saml2');