Skip to content
Snippets Groups Projects
authorize.php 5.26 KiB
Newer Older
<?php
require_once __DIR__ . '/oauth2_controller.php';

use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
use Studip\OAuth2\Bridge\UserEntity;
use Studip\OAuth2\Exceptions\InvalidAuthTokenException;
use Studip\OAuth2\Models\Scope;

class Api_Oauth2_AuthorizeController extends OAuth2Controller
{
    public function before_filter(&$action, &$args)
    {
        parent::before_filter($action, $args);

        if ('index' !== $action) {
            throw new Trails\Exception(404);
        }

        $action = $this->determineAction();
    }

    private function determineAction(): string
    {
        $method = $this->getMethod();

        if (Request::submitted('auth_token')) {
            $GLOBALS['auth']->login_if('nobody' === $GLOBALS['user']->id);
            CSRFProtection::verifyUnsafeRequest();

            switch ($method) {
                case 'POST':
                    return 'approved';

                case 'DELETE':
                    return 'denied';
            }
        }

        return 'authorize';
    }

    public function authorize_action(): void
    {
        $psrRequest = $this->getPsrRequest();
        $authRequest = $this->server->validateAuthorizationRequest($psrRequest);

        $scopes = $authRequest->getScopes();
        $client = $authRequest->getClient();

        $authToken = randomString(32);
        $this->freezeSessionVars($authRequest, $authToken);

        // show login form if not logged in
        $authPlugin = Config::get()->getValue('API_OAUTH_AUTH_PLUGIN');
        if ('nobody' === $GLOBALS['user']->id && 'Standard' !== $authPlugin && !Request::option('sso')) {
            $queryParams = $psrRequest->getQueryParams();
            $queryParams['sso'] = strtolower($authPlugin);
            $this->redirect($this->url_for('api/oauth2/authorize', $queryParams));

            return;
        } else {
            $GLOBALS['auth']->login_if('nobody' === $GLOBALS['user']->id);
        }

        $this->client = $client;
        $this->user = $GLOBALS['user'];
        $this->scopes = $this->scopesFor($scopes);
        $this->authToken = $authToken;
        $this->state = $authRequest->getState();

        PageLayout::disableHeader();
        $this->render_template(
            'api/oauth2/authorize.php',
            $GLOBALS['template_factory']->open('layouts/base.php')
        );
    }

    public function approved_action(): void
    {
        [$authRequest, $authToken] = $this->thawSessionVars();
        $this->assertValidAuthToken($authToken);

        $authRequest->setUser(new UserEntity($GLOBALS['user']->id));
        $authRequest->setAuthorizationApproved(true);

        $response = $this->server->completeAuthorizationRequest($authRequest, $this->getPsrResponse());

        $this->renderPsrResponse($response);
    }

    public function denied_action(): void
    {
        [$authRequest, $authToken] = $this->thawSessionVars();
        $this->assertValidAuthToken($authToken);

        $authRequest->setUser(new UserEntity($GLOBALS['user']->id));
        $authRequest->setAuthorizationApproved(false);

        $clientUris = $authRequest->getClient()->getRedirectUri();

        $uri = $authRequest->getRedirectUri();
        if (!in_array($uri, $clientUris)) {
            $uri = current($clientUris);
        }

        $uri = URLHelper::getURL($uri, [
            'error' => 'access_denied',
            'state' => Request::get('state'),
        ], true);
        $this->redirect($uri);
    }

    private function getMethod(): string
    {
        $method = Request::method();
        if ('POST' === $method && Request::submitted('_method')) {
            $_method = strtoupper(Request::get('_method'));
            if (in_array($_method, ['DELETE', 'PATCH', 'PUT'])) {
                $method = $_method;
            }
        }

        return $method;
    }

    /**
     * Make sure the auth token matches the one in the session.
     *
     * @throws InvalidAuthTokenException
     */
    private function assertValidAuthToken(string $authToken): void
    {
        if (Request::submitted('auth_token') && $authToken !== Request::get('auth_token')) {
            throw InvalidAuthTokenException::different();
        }
    }

    private function freezeSessionVars(AuthorizationRequest $authRequest, string $authToken): void
    {
        $_SESSION['oauth2'] = [
            'authRequest' => serialize($authRequest),
            'authToken'   => $authToken,
        ];
    }

    private function thawSessionVars(): array
    {
        $authRequest = null;
        $authToken = null;
        if (
            isset($_SESSION['oauth2']) &&
            is_array($_SESSION['oauth2']) &&
            isset($_SESSION['oauth2']['authRequest']) &&
            isset($_SESSION['oauth2']['authToken'])
        ) {
            $authRequest = unserialize($_SESSION['oauth2']['authRequest']);
            $authToken = $_SESSION['oauth2']['authToken'];
        }

        return [$authRequest, $authToken];
    }

    private function scopesFor(array $scopeEntities): array
    {
        $scopes = Scope::scopes();
        $scopeModels = [];
        foreach ($scopeEntities as $scopeEntity) {
            if (isset($scopes[$scopeEntity->getIdentifier()])) {
                $scopeModels[] = $scopes[$scopeEntity->getIdentifier()];
            }
        }

        return $scopeModels;
    }
}