diff options
author | Jan-Christoph Borchardt <hey@jancborchardt.net> | 2017-04-26 00:11:55 +0200 |
---|---|---|
committer | Jan-Christoph Borchardt <hey@jancborchardt.net> | 2017-04-26 00:50:38 +0200 |
commit | 241e397326545ee3ecad1a6a50dbe7839faa5c21 (patch) | |
tree | 6d33b4e4cc22bb1bf4753d83f44e1bb8422082e3 /core/Controller | |
parent | 0f0b04b7d9b4fa8c3c74218c222194f0f2f9e8b7 (diff) | |
parent | 255c7df3bdbaccf00ba8e9fb00e750ffb9a50356 (diff) | |
download | nextcloud-server-241e397326545ee3ecad1a6a50dbe7839faa5c21.tar.gz nextcloud-server-241e397326545ee3ecad1a6a50dbe7839faa5c21.zip |
Merge branch 'master' into contactsmenu
Signed-off-by: Jan-Christoph Borchardt <hey@jancborchardt.net>
Diffstat (limited to 'core/Controller')
-rw-r--r-- | core/Controller/ClientFlowLoginController.php | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/core/Controller/ClientFlowLoginController.php b/core/Controller/ClientFlowLoginController.php new file mode 100644 index 00000000000..ca9c092321a --- /dev/null +++ b/core/Controller/ClientFlowLoginController.php @@ -0,0 +1,238 @@ +<?php +/** + * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Core\Controller; + +use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Exceptions\PasswordlessTokenException; +use OC\Authentication\Token\IProvider; +use OC\Authentication\Token\IToken; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\Defaults; +use OCP\IL10N; +use OCP\IRequest; +use OCP\ISession; +use OCP\IURLGenerator; +use OCP\IUserSession; +use OCP\Security\ISecureRandom; +use OCP\Session\Exceptions\SessionNotAvailableException; + +class ClientFlowLoginController extends Controller { + /** @var IUserSession */ + private $userSession; + /** @var IL10N */ + private $l10n; + /** @var Defaults */ + private $defaults; + /** @var ISession */ + private $session; + /** @var IProvider */ + private $tokenProvider; + /** @var ISecureRandom */ + private $random; + /** @var IURLGenerator */ + private $urlGenerator; + + const stateName = 'client.flow.state.token'; + + /** + * @param string $appName + * @param IRequest $request + * @param IUserSession $userSession + * @param IL10N $l10n + * @param Defaults $defaults + * @param ISession $session + * @param IProvider $tokenProvider + * @param ISecureRandom $random + * @param IURLGenerator $urlGenerator + */ + public function __construct($appName, + IRequest $request, + IUserSession $userSession, + IL10N $l10n, + Defaults $defaults, + ISession $session, + IProvider $tokenProvider, + ISecureRandom $random, + IURLGenerator $urlGenerator) { + parent::__construct($appName, $request); + $this->userSession = $userSession; + $this->l10n = $l10n; + $this->defaults = $defaults; + $this->session = $session; + $this->tokenProvider = $tokenProvider; + $this->random = $random; + $this->urlGenerator = $urlGenerator; + } + + /** + * @return string + */ + private function getClientName() { + return $this->request->getHeader('USER_AGENT') !== null ? $this->request->getHeader('USER_AGENT') : 'unknown'; + } + + /** + * @param string $stateToken + * @return bool + */ + private function isValidToken($stateToken) { + $currentToken = $this->session->get(self::stateName); + if(!is_string($stateToken) || !is_string($currentToken)) { + return false; + } + return hash_equals($currentToken, $stateToken); + } + + /** + * @return TemplateResponse + */ + private function stateTokenForbiddenResponse() { + $response = new TemplateResponse( + $this->appName, + '403', + [ + 'file' => $this->l10n->t('State token does not match'), + ], + 'guest' + ); + $response->setStatus(Http::STATUS_FORBIDDEN); + return $response; + } + + /** + * @PublicPage + * @NoCSRFRequired + * @UseSession + * + * @return TemplateResponse + */ + public function showAuthPickerPage() { + if($this->userSession->isLoggedIn()) { + return new TemplateResponse( + $this->appName, + '403', + [ + 'file' => $this->l10n->t('Auth flow can only be started unauthenticated.'), + ], + 'guest' + ); + } + + $stateToken = $this->random->generate( + 64, + ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS + ); + $this->session->set(self::stateName, $stateToken); + + return new TemplateResponse( + $this->appName, + 'loginflow/authpicker', + [ + 'client' => $this->getClientName(), + 'instanceName' => $this->defaults->getName(), + 'urlGenerator' => $this->urlGenerator, + 'stateToken' => $stateToken, + 'serverHost' => $this->request->getServerHost(), + ], + 'guest' + ); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + * @UseSession + * + * @param string $stateToken + * @return TemplateResponse + */ + public function redirectPage($stateToken = '') { + if(!$this->isValidToken($stateToken)) { + return $this->stateTokenForbiddenResponse(); + } + + return new TemplateResponse( + $this->appName, + 'loginflow/redirect', + [ + 'urlGenerator' => $this->urlGenerator, + 'stateToken' => $stateToken, + ], + 'empty' + ); + } + + /** + * @NoAdminRequired + * @UseSession + * + * @param string $stateToken + * @return Http\RedirectResponse|Response + */ + public function generateAppPassword($stateToken) { + if(!$this->isValidToken($stateToken)) { + $this->session->remove(self::stateName); + return $this->stateTokenForbiddenResponse(); + } + + $this->session->remove(self::stateName); + + try { + $sessionId = $this->session->getId(); + } catch (SessionNotAvailableException $ex) { + $response = new Response(); + $response->setStatus(Http::STATUS_FORBIDDEN); + return $response; + } + + try { + $sessionToken = $this->tokenProvider->getToken($sessionId); + $loginName = $sessionToken->getLoginName(); + try { + $password = $this->tokenProvider->getPassword($sessionToken, $sessionId); + } catch (PasswordlessTokenException $ex) { + $password = null; + } + } catch (InvalidTokenException $ex) { + $response = new Response(); + $response->setStatus(Http::STATUS_FORBIDDEN); + return $response; + } + + $token = $this->random->generate(72); + $this->tokenProvider->generateToken( + $token, + $this->userSession->getUser()->getUID(), + $loginName, + $password, + $this->getClientName(), + IToken::PERMANENT_TOKEN, + IToken::DO_NOT_REMEMBER + ); + + return new Http\RedirectResponse('nc://' . urlencode($loginName) . ':' . urlencode($token) . '@' . $this->request->getServerHost()); + } + +} |