parent
							
								
									277d5392fb
								
							
						
					
					
						commit
						f64ce71afc
					
				| 
						 | 
					@ -4,35 +4,16 @@ namespace BookStack\Auth\Access\Oidc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class OidcIdToken
 | 
					class OidcIdToken
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    /**
 | 
					    protected array $header;
 | 
				
			||||||
     * @var array
 | 
					    protected array $payload;
 | 
				
			||||||
     */
 | 
					    protected string $signature;
 | 
				
			||||||
    protected $header;
 | 
					    protected string $issuer;
 | 
				
			||||||
 | 
					    protected array $tokenParts = [];
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @var array
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected $payload;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @var string
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected $signature;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @var array[]|string[]
 | 
					     * @var array[]|string[]
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    protected $keys;
 | 
					    protected array $keys;
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @var string
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected $issuer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * @var array
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    protected $tokenParts = [];
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function __construct(string $token, string $issuer, array $keys)
 | 
					    public function __construct(string $token, string $issuer, array $keys)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -106,6 +87,14 @@ class OidcIdToken
 | 
				
			||||||
        return $this->payload;
 | 
					        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.
 | 
					     * 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.
 | 
					     * 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\JsonDebugException;
 | 
				
			||||||
use BookStack\Exceptions\StoppedAuthenticationException;
 | 
					use BookStack\Exceptions\StoppedAuthenticationException;
 | 
				
			||||||
use BookStack\Exceptions\UserRegistrationException;
 | 
					use BookStack\Exceptions\UserRegistrationException;
 | 
				
			||||||
 | 
					use BookStack\Facades\Theme;
 | 
				
			||||||
 | 
					use BookStack\Theming\ThemeEvents;
 | 
				
			||||||
use Illuminate\Support\Arr;
 | 
					use Illuminate\Support\Arr;
 | 
				
			||||||
use Illuminate\Support\Facades\Cache;
 | 
					use Illuminate\Support\Facades\Cache;
 | 
				
			||||||
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
 | 
					use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
 | 
				
			||||||
| 
						 | 
					@ -21,24 +23,12 @@ use Psr\Http\Client\ClientInterface as HttpClient;
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class OidcService
 | 
					class OidcService
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected RegistrationService $registrationService;
 | 
					 | 
				
			||||||
    protected LoginService $loginService;
 | 
					 | 
				
			||||||
    protected HttpClient $httpClient;
 | 
					 | 
				
			||||||
    protected GroupSyncService $groupService;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * OpenIdService constructor.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public function __construct(
 | 
					    public function __construct(
 | 
				
			||||||
        RegistrationService $registrationService,
 | 
					        protected RegistrationService $registrationService,
 | 
				
			||||||
        LoginService $loginService,
 | 
					        protected LoginService $loginService,
 | 
				
			||||||
        HttpClient $httpClient,
 | 
					        protected HttpClient $httpClient,
 | 
				
			||||||
        GroupSyncService $groupService
 | 
					        protected GroupSyncService $groupService
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        $this->registrationService = $registrationService;
 | 
					 | 
				
			||||||
        $this->loginService = $loginService;
 | 
					 | 
				
			||||||
        $this->httpClient = $httpClient;
 | 
					 | 
				
			||||||
        $this->groupService = $groupService;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					@ -226,6 +216,16 @@ class OidcService
 | 
				
			||||||
            $settings->keys,
 | 
					            $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']) {
 | 
					        if ($this->config()['dump_user_details']) {
 | 
				
			||||||
            throw new JsonDebugException($idToken->getAllClaims());
 | 
					            throw new JsonDebugException($idToken->getAllClaims());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,19 @@ class ThemeEvents
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    const COMMONMARK_ENVIRONMENT_CONFIGURE = 'commonmark_environment_configure';
 | 
					    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.
 | 
					     * Page include parse event.
 | 
				
			||||||
     * Runs when a page include tag is being parsed, typically when page content is being processed for viewing.
 | 
					     * 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\Actions\ActivityType;
 | 
				
			||||||
use BookStack\Auth\Role;
 | 
					use BookStack\Auth\Role;
 | 
				
			||||||
use BookStack\Auth\User;
 | 
					use BookStack\Auth\User;
 | 
				
			||||||
 | 
					use BookStack\Facades\Theme;
 | 
				
			||||||
 | 
					use BookStack\Theming\ThemeEvents;
 | 
				
			||||||
use GuzzleHttp\Psr7\Request;
 | 
					use GuzzleHttp\Psr7\Request;
 | 
				
			||||||
use GuzzleHttp\Psr7\Response;
 | 
					use GuzzleHttp\Psr7\Response;
 | 
				
			||||||
use Illuminate\Testing\TestResponse;
 | 
					use Illuminate\Testing\TestResponse;
 | 
				
			||||||
| 
						 | 
					@ -397,7 +399,6 @@ class OidcTest extends TestCase
 | 
				
			||||||
        config()->set([
 | 
					        config()->set([
 | 
				
			||||||
            'oidc.external_id_claim' => 'super_awesome_id',
 | 
					            'oidc.external_id_claim' => 'super_awesome_id',
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
        $roleA = Role::factory()->create(['display_name' => 'Wizards']);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $resp = $this->runLogin([
 | 
					        $resp = $this->runLogin([
 | 
				
			||||||
            'email'            => 'benny@example.com',
 | 
					            'email'            => 'benny@example.com',
 | 
				
			||||||
| 
						 | 
					@ -464,6 +465,60 @@ class OidcTest extends TestCase
 | 
				
			||||||
        $this->assertTrue($user->hasRole($roleA->id));
 | 
					        $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()
 | 
					    protected function withAutodiscovery()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        config()->set([
 | 
					        config()->set([
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,8 +23,8 @@ use League\CommonMark\Environment\Environment;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ThemeTest extends TestCase
 | 
					class ThemeTest extends TestCase
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    protected $themeFolderName;
 | 
					    protected string $themeFolderName;
 | 
				
			||||||
    protected $themeFolderPath;
 | 
					    protected string $themeFolderPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function test_translation_text_can_be_overridden_via_theme()
 | 
					    public function test_translation_text_can_be_overridden_via_theme()
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue