diff options
Diffstat (limited to 'core/Controller/ClientFlowLoginV2Controller.php')
-rw-r--r-- | core/Controller/ClientFlowLoginV2Controller.php | 118 |
1 files changed, 79 insertions, 39 deletions
diff --git a/core/Controller/ClientFlowLoginV2Controller.php b/core/Controller/ClientFlowLoginV2Controller.php index 3c57b190593..19c1f9ce251 100644 --- a/core/Controller/ClientFlowLoginV2Controller.php +++ b/core/Controller/ClientFlowLoginV2Controller.php @@ -27,16 +27,20 @@ declare(strict_types=1); */ namespace OC\Core\Controller; -use OC\Authentication\Exceptions\InvalidTokenException; use OC\Core\Db\LoginFlowV2; use OC\Core\Exception\LoginFlowV2NotFoundException; use OC\Core\Service\LoginFlowV2Service; +use OCA\Core\ResponseDefinitions; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Attribute\FrontpageRoute; +use OCP\AppFramework\Http\Attribute\OpenAPI; +use OCP\AppFramework\Http\Attribute\UseSession; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\StandaloneTemplateResponse; +use OCP\Authentication\Exceptions\InvalidTokenException; use OCP\Defaults; use OCP\IL10N; use OCP\IRequest; @@ -46,44 +50,42 @@ use OCP\IUser; use OCP\IUserSession; use OCP\Security\ISecureRandom; +/** + * @psalm-import-type CoreLoginFlowV2Credentials from ResponseDefinitions + * @psalm-import-type CoreLoginFlowV2 from ResponseDefinitions + */ class ClientFlowLoginV2Controller extends Controller { public const TOKEN_NAME = 'client.flow.v2.login.token'; public const STATE_NAME = 'client.flow.v2.state.token'; - private LoginFlowV2Service $loginFlowV2Service; - private IURLGenerator $urlGenerator; - private IUserSession $userSession; - private ISession $session; - private ISecureRandom $random; - private Defaults $defaults; - private ?string $userId; - private IL10N $l10n; - - public function __construct(string $appName, - IRequest $request, - LoginFlowV2Service $loginFlowV2Service, - IURLGenerator $urlGenerator, - ISession $session, - IUserSession $userSession, - ISecureRandom $random, - Defaults $defaults, - ?string $userId, - IL10N $l10n) { + public function __construct( + string $appName, + IRequest $request, + private LoginFlowV2Service $loginFlowV2Service, + private IURLGenerator $urlGenerator, + private ISession $session, + private IUserSession $userSession, + private ISecureRandom $random, + private Defaults $defaults, + private ?string $userId, + private IL10N $l10n, + ) { parent::__construct($appName, $request); - $this->loginFlowV2Service = $loginFlowV2Service; - $this->urlGenerator = $urlGenerator; - $this->session = $session; - $this->userSession = $userSession; - $this->random = $random; - $this->defaults = $defaults; - $this->userId = $userId; - $this->l10n = $l10n; } /** * @NoCSRFRequired * @PublicPage + * + * Poll the login flow credentials + * + * @param string $token Token of the flow + * @return JSONResponse<Http::STATUS_OK, CoreLoginFlowV2Credentials, array{}>|JSONResponse<Http::STATUS_NOT_FOUND, array<empty>, array{}> + * + * 200: Login flow credentials returned + * 404: Login flow not found or completed */ + #[FrontpageRoute(verb: 'POST', url: '/login/v2/poll')] public function poll(string $token): JSONResponse { try { $creds = $this->loginFlowV2Service->poll($token); @@ -91,14 +93,16 @@ class ClientFlowLoginV2Controller extends Controller { return new JSONResponse([], Http::STATUS_NOT_FOUND); } - return new JSONResponse($creds); + return new JSONResponse($creds->jsonSerialize()); } /** * @NoCSRFRequired * @PublicPage - * @UseSession */ + #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] + #[UseSession] + #[FrontpageRoute(verb: 'GET', url: '/login/v2/flow/{token}')] public function landing(string $token, $user = ''): Response { if (!$this->loginFlowV2Service->startLoginFlow($token)) { return $this->loginTokenForbiddenResponse(); @@ -114,8 +118,10 @@ class ClientFlowLoginV2Controller extends Controller { /** * @NoCSRFRequired * @PublicPage - * @UseSession */ + #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] + #[UseSession] + #[FrontpageRoute(verb: 'GET', url: '/login/v2/flow')] public function showAuthPickerPage($user = ''): StandaloneTemplateResponse { try { $flow = $this->getFlowByLoginToken(); @@ -145,11 +151,16 @@ class ClientFlowLoginV2Controller extends Controller { /** * @NoAdminRequired - * @UseSession * @NoCSRFRequired * @NoSameSiteCookieRequired */ - public function grantPage(string $stateToken): StandaloneTemplateResponse { + #[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)] + #[UseSession] + #[FrontpageRoute(verb: 'GET', url: '/login/v2/grant')] + public function grantPage(?string $stateToken): StandaloneTemplateResponse { + if ($stateToken === null) { + return $this->stateTokenMissingResponse(); + } if (!$this->isValidStateToken($stateToken)) { return $this->stateTokenForbiddenResponse(); } @@ -181,7 +192,12 @@ class ClientFlowLoginV2Controller extends Controller { /** * @PublicPage */ - public function apptokenRedirect(string $stateToken, string $user, string $password) { + #[FrontpageRoute(verb: 'POST', url: '/login/v2/apptoken')] + public function apptokenRedirect(?string $stateToken, string $user, string $password) { + if ($stateToken === null) { + return $this->stateTokenMissingResponse(); + } + if (!$this->isValidStateToken($stateToken)) { return $this->stateTokenForbiddenResponse(); } @@ -216,15 +232,19 @@ class ClientFlowLoginV2Controller extends Controller { return $response; } - $result = $this->loginFlowV2Service->flowDoneWithAppPassword($loginToken, $this->getServerPath(), $this->userId, $password); + $result = $this->loginFlowV2Service->flowDoneWithAppPassword($loginToken, $this->getServerPath(), $token->getLoginName(), $password); return $this->handleFlowDone($result); } /** * @NoAdminRequired - * @UseSession */ - public function generateAppPassword(string $stateToken): Response { + #[UseSession] + #[FrontpageRoute(verb: 'POST', url: '/login/v2/grant')] + public function generateAppPassword(?string $stateToken): Response { + if ($stateToken === null) { + return $this->stateTokenMissingResponse(); + } if (!$this->isValidStateToken($stateToken)) { return $this->stateTokenForbiddenResponse(); } @@ -271,7 +291,14 @@ class ClientFlowLoginV2Controller extends Controller { /** * @NoCSRFRequired * @PublicPage + * + * Init a login flow + * + * @return JSONResponse<Http::STATUS_OK, CoreLoginFlowV2, array{}> + * + * 200: Login flow init returned */ + #[FrontpageRoute(verb: 'POST', url: '/login/v2')] public function init(): JSONResponse { // Get client user agent $userAgent = $this->request->getHeader('USER_AGENT'); @@ -297,6 +324,19 @@ class ClientFlowLoginV2Controller extends Controller { return hash_equals($currentToken, $stateToken); } + private function stateTokenMissingResponse(): StandaloneTemplateResponse { + $response = new StandaloneTemplateResponse( + $this->appName, + '403', + [ + 'message' => $this->l10n->t('State token missing'), + ], + 'guest' + ); + $response->setStatus(Http::STATUS_FORBIDDEN); + return $response; + } + private function stateTokenForbiddenResponse(): StandaloneTemplateResponse { $response = new StandaloneTemplateResponse( $this->appName, @@ -339,9 +379,9 @@ class ClientFlowLoginV2Controller extends Controller { private function getServerPath(): string { $serverPostfix = ''; - if (strpos($this->request->getRequestUri(), '/index.php') !== false) { + if (str_contains($this->request->getRequestUri(), '/index.php')) { $serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/index.php')); - } elseif (strpos($this->request->getRequestUri(), '/login/v2') !== false) { + } elseif (str_contains($this->request->getRequestUri(), '/login/v2')) { $serverPostfix = substr($this->request->getRequestUri(), 0, strpos($this->request->getRequestUri(), '/login/v2')); } |