mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-01 01:36:00 -04:00
parent
277d5392fb
commit
f64ce71afc
@ -4,35 +4,16 @@ namespace BookStack\Auth\Access\Oidc;
|
||||
|
||||
class OidcIdToken
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $header;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $payload;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $signature;
|
||||
protected array $header;
|
||||
protected array $payload;
|
||||
protected string $signature;
|
||||
protected string $issuer;
|
||||
protected array $tokenParts = [];
|
||||
|
||||
/**
|
||||
* @var array[]|string[]
|
||||
*/
|
||||
protected $keys;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $issuer;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $tokenParts = [];
|
||||
protected array $keys;
|
||||
|
||||
public function __construct(string $token, string $issuer, array $keys)
|
||||
{
|
||||
@ -106,6 +87,14 @@ class OidcIdToken
|
||||
return $this->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the existing claim data of this token with that provided.
|
||||
*/
|
||||
public function replaceClaims(array $claims): void
|
||||
{
|
||||
$this->payload = $claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the structure of the given token and ensure we have the required pieces.
|
||||
* As per https://datatracker.ietf.org/doc/html/rfc7519#section-7.2.
|
||||
|
@ -9,6 +9,8 @@ use BookStack\Auth\User;
|
||||
use BookStack\Exceptions\JsonDebugException;
|
||||
use BookStack\Exceptions\StoppedAuthenticationException;
|
||||
use BookStack\Exceptions\UserRegistrationException;
|
||||
use BookStack\Facades\Theme;
|
||||
use BookStack\Theming\ThemeEvents;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
|
||||
@ -21,24 +23,12 @@ use Psr\Http\Client\ClientInterface as HttpClient;
|
||||
*/
|
||||
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,
|
||||
GroupSyncService $groupService
|
||||
protected RegistrationService $registrationService,
|
||||
protected LoginService $loginService,
|
||||
protected HttpClient $httpClient,
|
||||
protected GroupSyncService $groupService
|
||||
) {
|
||||
$this->registrationService = $registrationService;
|
||||
$this->loginService = $loginService;
|
||||
$this->httpClient = $httpClient;
|
||||
$this->groupService = $groupService;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,6 +216,16 @@ class OidcService
|
||||
$settings->keys,
|
||||
);
|
||||
|
||||
$returnClaims = Theme::dispatch(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $idToken->getAllClaims(), [
|
||||
'access_token' => $accessToken->getToken(),
|
||||
'expires_in' => $accessToken->getExpires(),
|
||||
'refresh_token' => $accessToken->getRefreshToken(),
|
||||
]);
|
||||
|
||||
if (!is_null($returnClaims)) {
|
||||
$idToken->replaceClaims($returnClaims);
|
||||
}
|
||||
|
||||
if ($this->config()['dump_user_details']) {
|
||||
throw new JsonDebugException($idToken->getAllClaims());
|
||||
}
|
||||
|
@ -70,6 +70,19 @@ class ThemeEvents
|
||||
*/
|
||||
const COMMONMARK_ENVIRONMENT_CONFIGURE = 'commonmark_environment_configure';
|
||||
|
||||
/**
|
||||
* OIDC ID token pre-validate event.
|
||||
* Runs just before BookStack validates the user ID token data upon login.
|
||||
* Provides the existing found set of claims for the user as a key-value array,
|
||||
* along with an array of the proceeding access token data provided by the identity platform.
|
||||
* If the listener returns a non-null value, that will replace the existing ID token claim data.
|
||||
*
|
||||
* @param array $idTokenData
|
||||
* @param array $accessTokenData
|
||||
* @returns array|null
|
||||
*/
|
||||
const OIDC_ID_TOKEN_PRE_VALIDATE = 'oidc_id_token_pre_validate';
|
||||
|
||||
/**
|
||||
* Page include parse event.
|
||||
* Runs when a page include tag is being parsed, typically when page content is being processed for viewing.
|
||||
|
@ -5,6 +5,8 @@ namespace Tests\Auth;
|
||||
use BookStack\Actions\ActivityType;
|
||||
use BookStack\Auth\Role;
|
||||
use BookStack\Auth\User;
|
||||
use BookStack\Facades\Theme;
|
||||
use BookStack\Theming\ThemeEvents;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
@ -397,7 +399,6 @@ class OidcTest extends TestCase
|
||||
config()->set([
|
||||
'oidc.external_id_claim' => 'super_awesome_id',
|
||||
]);
|
||||
$roleA = Role::factory()->create(['display_name' => 'Wizards']);
|
||||
|
||||
$resp = $this->runLogin([
|
||||
'email' => 'benny@example.com',
|
||||
@ -464,6 +465,60 @@ class OidcTest extends TestCase
|
||||
$this->assertTrue($user->hasRole($roleA->id));
|
||||
}
|
||||
|
||||
public function test_oidc_id_token_pre_validate_theme_event_without_return()
|
||||
{
|
||||
$args = [];
|
||||
$callback = function (...$eventArgs) use (&$args) {
|
||||
$args = $eventArgs;
|
||||
};
|
||||
Theme::listen(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $callback);
|
||||
|
||||
$resp = $this->runLogin([
|
||||
'email' => 'benny@example.com',
|
||||
'sub' => 'benny1010101',
|
||||
'name' => 'Benny',
|
||||
]);
|
||||
$resp->assertRedirect('/');
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'external_auth_id' => 'benny1010101',
|
||||
]);
|
||||
|
||||
$this->assertArrayHasKey('iss', $args[0]);
|
||||
$this->assertArrayHasKey('sub', $args[0]);
|
||||
$this->assertEquals('Benny', $args[0]['name']);
|
||||
$this->assertEquals('benny1010101', $args[0]['sub']);
|
||||
|
||||
$this->assertArrayHasKey('access_token', $args[1]);
|
||||
$this->assertArrayHasKey('expires_in', $args[1]);
|
||||
$this->assertArrayHasKey('refresh_token', $args[1]);
|
||||
}
|
||||
|
||||
public function test_oidc_id_token_pre_validate_theme_event_with_return()
|
||||
{
|
||||
$callback = function (...$eventArgs) {
|
||||
return array_merge($eventArgs[0], [
|
||||
'email' => 'lenny@example.com',
|
||||
'sub' => 'lenny1010101',
|
||||
'name' => 'Lenny',
|
||||
]);
|
||||
};
|
||||
Theme::listen(ThemeEvents::OIDC_ID_TOKEN_PRE_VALIDATE, $callback);
|
||||
|
||||
$resp = $this->runLogin([
|
||||
'email' => 'benny@example.com',
|
||||
'sub' => 'benny1010101',
|
||||
'name' => 'Benny',
|
||||
]);
|
||||
$resp->assertRedirect('/');
|
||||
|
||||
$this->assertDatabaseHas('users', [
|
||||
'email' => 'lenny@example.com',
|
||||
'external_auth_id' => 'lenny1010101',
|
||||
'name' => 'Lenny',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function withAutodiscovery()
|
||||
{
|
||||
config()->set([
|
||||
|
@ -23,8 +23,8 @@ use League\CommonMark\Environment\Environment;
|
||||
|
||||
class ThemeTest extends TestCase
|
||||
{
|
||||
protected $themeFolderName;
|
||||
protected $themeFolderPath;
|
||||
protected string $themeFolderName;
|
||||
protected string $themeFolderPath;
|
||||
|
||||
public function test_translation_text_can_be_overridden_via_theme()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user