+ setting()->put('registration-confirmation', 'true');
+ config()->set([
+ 'saml2.onelogin.strict' => false,
+ 'saml2.user_to_groups' => true,
+ 'saml2.remove_from_groups' => false,
+ ]);
+
+ $memberRole = Role::factory()->create(['external_auth_id' => 'member']);
+ $adminRole = Role::getSystemRole('admin');
+
+ $acsPost = $this->followingRedirects()->post('/saml2/acs', ['SAMLResponse' => $this->acsPostData]);
+
+ $this->assertEquals('http://localhost/register/confirm', url()->current());
+ $acsPost->assertSee('Please check your email and click the confirmation button to access BookStack.');
+ /** @var User $user */
+ $user = User::query()->where('external_auth_id', '=', 'user')->first();
+
+ $userRoleIds = $user->roles()->pluck('id');
+ $this->assertContains($memberRole->id, $userRoleIds, 'User was assigned to member role');
+ $this->assertContains($adminRole->id, $userRoleIds, 'User was assigned to admin role');
+ $this->assertFalse(boolval($user->email_confirmed), 'User email remains unconfirmed');
+
+ $this->assertNull(auth()->user());
+ $homeGet = $this->get('/');
+ $homeGet->assertRedirect('/login');
+ }
+
+ public function test_login_where_existing_non_saml_user_shows_warning()
+ {
+ $this->post('/saml2/login');
+ config()->set(['saml2.onelogin.strict' => false]);
+
+ // Make the user pre-existing in DB with different auth_id
+ User::query()->forceCreate([
+ 'external_auth_id' => 'old_system_user_id',
+ 'email_confirmed' => false,
+ 'name' => 'Barry Scott',
+ ]);
+
+ $acsPost = $this->followingRedirects()->post('/saml2/acs', ['SAMLResponse' => $this->acsPostData]);
+ $this->assertFalse($this->isAuthenticated());
+ $this->assertDatabaseHas('users', [
+ 'external_auth_id' => 'old_system_user_id',
+ ]);
+
+ $acsPost->assertSee('A user with the email
[email protected] already exists but with different credentials');
+ }
+
+ public function test_login_request_contains_expected_default_authncontext()
+ {
+ $authReq = $this->getAuthnRequest();
+ $this->assertStringContainsString('samlp:RequestedAuthnContext Comparison="exact"', $authReq);
+ $this->assertStringContainsString('<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>', $authReq);
+ }
+
+ public function test_false_idp_authncontext_option_does_not_pass_authncontext_in_saml_request()
+ {
+ config()->set(['saml2.onelogin.security.requestedAuthnContext' => false]);
+ $authReq = $this->getAuthnRequest();
+ $this->assertStringNotContainsString('samlp:RequestedAuthnContext', $authReq);
+ $this->assertStringNotContainsString('<saml:AuthnContextClassRef>', $authReq);
+ }
+
+ public function test_array_idp_authncontext_option_passes_value_as_authncontextclassref_in_request()
+ {
+ config()->set(['saml2.onelogin.security.requestedAuthnContext' => ['urn:federation:authentication:windows', 'urn:federation:authentication:linux']]);
+ $authReq = $this->getAuthnRequest();
+ $this->assertStringContainsString('samlp:RequestedAuthnContext', $authReq);
+ $this->assertStringContainsString('<saml:AuthnContextClassRef>urn:federation:authentication:windows</saml:AuthnContextClassRef>', $authReq);
+ $this->assertStringContainsString('<saml:AuthnContextClassRef>urn:federation:authentication:linux</saml:AuthnContextClassRef>', $authReq);