mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-08-07 23:03:00 +03:00
Added OIDC group sync functionality
Is generally aligned with out SAML2 group sync functionality, but for OIDC based upon feedback in #3004. Neeeded the tangental addition of being able to define custom scopes on the initial auth request as some systems use this to provide additional id token claims such as groups. Includes tests to cover. Tested live using Okta.
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace Tests\Auth;
|
||||
|
||||
use BookStack\Actions\ActivityType;
|
||||
use BookStack\Auth\Role;
|
||||
use BookStack\Auth\User;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
@@ -37,6 +38,10 @@ class OidcTest extends TestCase
|
||||
'oidc.token_endpoint' => 'https://oidc.local/token',
|
||||
'oidc.discover' => false,
|
||||
'oidc.dump_user_details' => false,
|
||||
'oidc.additional_scopes' => '',
|
||||
'oidc.user_to_groups' => false,
|
||||
'oidc.group_attribute' => 'group',
|
||||
'oidc.remove_from_groups' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -159,6 +164,17 @@ class OidcTest extends TestCase
|
||||
$this->assertActivityExists(ActivityType::AUTH_LOGIN, null, "oidc; ({$user->id}) Barry Scott");
|
||||
}
|
||||
|
||||
public function test_login_uses_custom_additional_scopes_if_defined()
|
||||
{
|
||||
config()->set([
|
||||
'oidc.additional_scopes' => 'groups, badgers',
|
||||
]);
|
||||
|
||||
$redirect = $this->post('/oidc/login')->headers->get('location');
|
||||
|
||||
$this->assertStringContainsString('scope=openid%20profile%20email%20groups%20badgers', $redirect);
|
||||
}
|
||||
|
||||
public function test_callback_fails_if_no_state_present_or_matching()
|
||||
{
|
||||
$this->get('/oidc/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=abc124');
|
||||
@@ -344,6 +360,59 @@ class OidcTest extends TestCase
|
||||
$this->assertTrue(auth()->check());
|
||||
}
|
||||
|
||||
public function test_login_group_sync()
|
||||
{
|
||||
config()->set([
|
||||
'oidc.user_to_groups' => true,
|
||||
'oidc.group_attribute' => 'groups',
|
||||
'oidc.remove_from_groups' => false,
|
||||
]);
|
||||
$roleA = Role::factory()->create(['display_name' => 'Wizards']);
|
||||
$roleB = Role::factory()->create(['display_name' => 'ZooFolks', 'external_auth_id' => 'zookeepers']);
|
||||
$roleC = Role::factory()->create(['display_name' => 'Another Role']);
|
||||
|
||||
$resp = $this->runLogin([
|
||||
'email' => 'benny@example.com',
|
||||
'sub' => 'benny1010101',
|
||||
'groups' => ['Wizards', 'Zookeepers']
|
||||
]);
|
||||
$resp->assertRedirect('/');
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::query()->where('email', '=', 'benny@example.com')->first();
|
||||
|
||||
$this->assertTrue($user->hasRole($roleA->id));
|
||||
$this->assertTrue($user->hasRole($roleB->id));
|
||||
$this->assertFalse($user->hasRole($roleC->id));
|
||||
}
|
||||
|
||||
public function test_login_group_sync_with_nested_groups_in_token()
|
||||
{
|
||||
config()->set([
|
||||
'oidc.user_to_groups' => true,
|
||||
'oidc.group_attribute' => 'my.custom.groups.attr',
|
||||
'oidc.remove_from_groups' => false,
|
||||
]);
|
||||
$roleA = Role::factory()->create(['display_name' => 'Wizards']);
|
||||
|
||||
$resp = $this->runLogin([
|
||||
'email' => 'benny@example.com',
|
||||
'sub' => 'benny1010101',
|
||||
'my' => [
|
||||
'custom' => [
|
||||
'groups' => [
|
||||
'attr' => ['Wizards']
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
$resp->assertRedirect('/');
|
||||
|
||||
/** @var User $user */
|
||||
$user = User::query()->where('email', '=', 'benny@example.com')->first();
|
||||
$this->assertTrue($user->hasRole($roleA->id));
|
||||
}
|
||||
|
||||
protected function withAutodiscovery()
|
||||
{
|
||||
config()->set([
|
||||
|
Reference in New Issue
Block a user