commit
859bc245b1
@ -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()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,9 @@ |
|||||||
|
.picker-window { |
||||||
|
display: block; |
||||||
|
padding: 10px; |
||||||
|
margin-bottom: 20px; |
||||||
|
background-color: rgba(0,0,0,.3); |
||||||
|
color: #fff; |
||||||
|
border-radius: 3px; |
||||||
|
cursor: default; |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
jQuery(document).ready(function() { |
||||||
|
$('#app-token-login').click(function (e) { |
||||||
|
e.preventDefault(); |
||||||
|
$(this).addClass('hidden'); |
||||||
|
$('#redirect-link').addClass('hidden'); |
||||||
|
$('#app-token-login-field').removeClass('hidden'); |
||||||
|
}); |
||||||
|
|
||||||
|
$('#submit-app-token-login').click(function(e) { |
||||||
|
e.preventDefault(); |
||||||
|
window.location.href = 'nc://' + encodeURIComponent($('#user').val()) + ':' + encodeURIComponent($('#password').val()) + '@' + encodeURIComponent($('#serverHost').val()); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,3 @@ |
|||||||
|
jQuery(document).ready(function() { |
||||||
|
$('#submit-redirect-form').trigger('click'); |
||||||
|
}); |
@ -0,0 +1,57 @@ |
|||||||
|
<?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/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
script('core', 'login/authpicker'); |
||||||
|
style('core', 'login/authpicker'); |
||||||
|
|
||||||
|
/** @var array $_ */ |
||||||
|
/** @var \OCP\IURLGenerator $urlGenerator */ |
||||||
|
$urlGenerator = $_['urlGenerator']; |
||||||
|
?> |
||||||
|
|
||||||
|
<div class="picker-window"> |
||||||
|
<p class="info"> |
||||||
|
<?php p($l->t('You are about to grant "%s" access to your %s account.', [$_['client'], $_['instanceName']])) ?> |
||||||
|
</p> |
||||||
|
|
||||||
|
<br/> |
||||||
|
|
||||||
|
<p id="redirect-link"> |
||||||
|
<a href="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLogin.redirectPage', ['stateToken' => $_['stateToken']])) ?>">
|
||||||
|
<input type="submit" class="login primary icon-confirm-white" value="<?php p('Grant access') ?>">
|
||||||
|
</a> |
||||||
|
</p> |
||||||
|
|
||||||
|
<fieldset id="app-token-login-field" class="hidden"> |
||||||
|
<p class="grouptop"> |
||||||
|
<input type="text" name="user" id="user" placeholder="<?php p($l->t('Username')) ?>">
|
||||||
|
<label for="user" class="infield"><?php p($l->t('Username')) ?></label>
|
||||||
|
</p> |
||||||
|
<p class="groupbottom"> |
||||||
|
<input type="password" name="password" id="password" placeholder="<?php p($l->t('App token')) ?>">
|
||||||
|
<label for="password" class="infield"><?php p($l->t('Password')) ?></label>
|
||||||
|
</p> |
||||||
|
<input type="hidden" id="serverHost" value="<?php p($_['serverHost']) ?>" />
|
||||||
|
<input id="submit-app-token-login" type="submit" class="login primary icon-confirm-white" value="<?php p('Grant access') ?>">
|
||||||
|
</fieldset> |
||||||
|
</div> |
||||||
|
|
||||||
|
<a id="app-token-login" class="warning" href="#"><?php p($l->t('Alternative login using app token')) ?></a>
|
@ -0,0 +1,37 @@ |
|||||||
|
<?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/>. |
||||||
|
* |
||||||
|
*/ |
||||||
|
script('core', 'login/redirect'); |
||||||
|
style('core', 'login/authpicker'); |
||||||
|
|
||||||
|
/** @var array $_ */ |
||||||
|
/** @var \OCP\IURLGenerator $urlGenerator */ |
||||||
|
$urlGenerator = $_['urlGenerator']; |
||||||
|
?> |
||||||
|
|
||||||
|
<div class="picker-window"> |
||||||
|
<p class="info"><?php p($l->t('Redirecting …')) ?></p>
|
||||||
|
</div> |
||||||
|
|
||||||
|
<form method="POST" action="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLogin.generateAppPassword')) ?>">
|
||||||
|
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" />
|
||||||
|
<input type="hidden" name="stateToken" value="<?php p($_['stateToken']) ?>" />
|
||||||
|
<input id="submit-redirect-form" type="submit" class="hidden "/> |
||||||
|
</form> |
@ -0,0 +1,408 @@ |
|||||||
|
<?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 Tests\Core\Controller; |
||||||
|
|
||||||
|
use OC\Authentication\Exceptions\InvalidTokenException; |
||||||
|
use OC\Authentication\Exceptions\PasswordlessTokenException; |
||||||
|
use OC\Authentication\Token\IProvider; |
||||||
|
use OC\Authentication\Token\IToken; |
||||||
|
use OC\Core\Controller\ClientFlowLoginController; |
||||||
|
use OCP\AppFramework\Http; |
||||||
|
use OCP\AppFramework\Http\TemplateResponse; |
||||||
|
use OCP\Defaults; |
||||||
|
use OCP\IL10N; |
||||||
|
use OCP\IRequest; |
||||||
|
use OCP\ISession; |
||||||
|
use OCP\IURLGenerator; |
||||||
|
use OCP\IUser; |
||||||
|
use OCP\IUserSession; |
||||||
|
use OCP\Security\ISecureRandom; |
||||||
|
use OCP\Session\Exceptions\SessionNotAvailableException; |
||||||
|
use Test\TestCase; |
||||||
|
|
||||||
|
class ClientFlowLoginControllerTest extends TestCase { |
||||||
|
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $request; |
||||||
|
/** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $userSession; |
||||||
|
/** @var IL10N|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $l10n; |
||||||
|
/** @var Defaults|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $defaults; |
||||||
|
/** @var ISession|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $session; |
||||||
|
/** @var IProvider|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $tokenProvider; |
||||||
|
/** @var ISecureRandom|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $random; |
||||||
|
/** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ |
||||||
|
private $urlGenerator; |
||||||
|
/** @var ClientFlowLoginController */ |
||||||
|
private $clientFlowLoginController; |
||||||
|
|
||||||
|
public function setUp() { |
||||||
|
parent::setUp(); |
||||||
|
|
||||||
|
$this->request = $this->createMock(IRequest::class); |
||||||
|
$this->userSession = $this->createMock(IUserSession::class); |
||||||
|
$this->l10n = $this->createMock(IL10N::class); |
||||||
|
$this->l10n |
||||||
|
->expects($this->any()) |
||||||
|
->method('t') |
||||||
|
->will($this->returnCallback(function($text, $parameters = array()) { |
||||||
|
return vsprintf($text, $parameters); |
||||||
|
})); |
||||||
|
$this->defaults = $this->createMock(Defaults::class); |
||||||
|
$this->session = $this->createMock(ISession::class); |
||||||
|
$this->tokenProvider = $this->createMock(IProvider::class); |
||||||
|
$this->random = $this->createMock(ISecureRandom::class); |
||||||
|
$this->urlGenerator = $this->createMock(IURLGenerator::class); |
||||||
|
|
||||||
|
$this->clientFlowLoginController = new ClientFlowLoginController( |
||||||
|
'core', |
||||||
|
$this->request, |
||||||
|
$this->userSession, |
||||||
|
$this->l10n, |
||||||
|
$this->defaults, |
||||||
|
$this->session, |
||||||
|
$this->tokenProvider, |
||||||
|
$this->random, |
||||||
|
$this->urlGenerator |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public function testShowAuthPickerPageNotAuthenticated() { |
||||||
|
$this->userSession |
||||||
|
->expects($this->once()) |
||||||
|
->method('isLoggedIn') |
||||||
|
->willReturn(true); |
||||||
|
|
||||||
|
$expected = new TemplateResponse( |
||||||
|
'core', |
||||||
|
'403', |
||||||
|
[ |
||||||
|
'file' => 'Auth flow can only be started unauthenticated.', |
||||||
|
], |
||||||
|
'guest' |
||||||
|
); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage()); |
||||||
|
} |
||||||
|
|
||||||
|
public function testShowAuthPickerPage() { |
||||||
|
$this->userSession |
||||||
|
->expects($this->once()) |
||||||
|
->method('isLoggedIn') |
||||||
|
->willReturn(false); |
||||||
|
$this->random |
||||||
|
->expects($this->once()) |
||||||
|
->method('generate') |
||||||
|
->with( |
||||||
|
64, |
||||||
|
ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_DIGITS |
||||||
|
) |
||||||
|
->willReturn('StateToken'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('set') |
||||||
|
->with('client.flow.state.token', 'StateToken'); |
||||||
|
$this->request |
||||||
|
->expects($this->exactly(2)) |
||||||
|
->method('getHeader') |
||||||
|
->with('USER_AGENT') |
||||||
|
->willReturn('Mac OS X Sync Client'); |
||||||
|
$this->defaults |
||||||
|
->expects($this->once()) |
||||||
|
->method('getName') |
||||||
|
->willReturn('ExampleCloud'); |
||||||
|
$this->request |
||||||
|
->expects($this->once()) |
||||||
|
->method('getServerHost') |
||||||
|
->willReturn('example.com'); |
||||||
|
|
||||||
|
$expected = new TemplateResponse( |
||||||
|
'core', |
||||||
|
'loginflow/authpicker', |
||||||
|
[ |
||||||
|
'client' => 'Mac OS X Sync Client', |
||||||
|
'instanceName' => 'ExampleCloud', |
||||||
|
'urlGenerator' => $this->urlGenerator, |
||||||
|
'stateToken' => 'StateToken', |
||||||
|
'serverHost' => 'example.com', |
||||||
|
], |
||||||
|
'guest' |
||||||
|
); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->showAuthPickerPage()); |
||||||
|
} |
||||||
|
|
||||||
|
public function testRedirectPageWithInvalidToken() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn('OtherToken'); |
||||||
|
|
||||||
|
$expected = new TemplateResponse( |
||||||
|
'core', |
||||||
|
'403', |
||||||
|
[ |
||||||
|
'file' => 'State token does not match', |
||||||
|
], |
||||||
|
'guest' |
||||||
|
); |
||||||
|
$expected->setStatus(Http::STATUS_FORBIDDEN); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->redirectPage('MyStateToken')); |
||||||
|
} |
||||||
|
|
||||||
|
public function testRedirectPageWithoutToken() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn(null); |
||||||
|
|
||||||
|
$expected = new TemplateResponse( |
||||||
|
'core', |
||||||
|
'403', |
||||||
|
[ |
||||||
|
'file' => 'State token does not match', |
||||||
|
], |
||||||
|
'guest' |
||||||
|
); |
||||||
|
$expected->setStatus(Http::STATUS_FORBIDDEN); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->redirectPage('MyStateToken')); |
||||||
|
} |
||||||
|
|
||||||
|
public function testRedirectPage() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn('MyStateToken'); |
||||||
|
|
||||||
|
$expected = new TemplateResponse( |
||||||
|
'core', |
||||||
|
'loginflow/redirect', |
||||||
|
[ |
||||||
|
'urlGenerator' => $this->urlGenerator, |
||||||
|
'stateToken' => 'MyStateToken', |
||||||
|
], |
||||||
|
'empty' |
||||||
|
); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->redirectPage('MyStateToken')); |
||||||
|
} |
||||||
|
|
||||||
|
public function testGenerateAppPasswordWithInvalidToken() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn('OtherToken'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('remove') |
||||||
|
->with('client.flow.state.token'); |
||||||
|
|
||||||
|
$expected = new TemplateResponse( |
||||||
|
'core', |
||||||
|
'403', |
||||||
|
[ |
||||||
|
'file' => 'State token does not match', |
||||||
|
], |
||||||
|
'guest' |
||||||
|
); |
||||||
|
$expected->setStatus(Http::STATUS_FORBIDDEN); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); |
||||||
|
} |
||||||
|
|
||||||
|
public function testGenerateAppPasswordWithSessionNotAvailableException() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn('MyStateToken'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('remove') |
||||||
|
->with('client.flow.state.token'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('getId') |
||||||
|
->willThrowException(new SessionNotAvailableException()); |
||||||
|
|
||||||
|
$expected = new Http\Response(); |
||||||
|
$expected->setStatus(Http::STATUS_FORBIDDEN); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); |
||||||
|
} |
||||||
|
|
||||||
|
public function testGenerateAppPasswordWithInvalidTokenException() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn('MyStateToken'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('remove') |
||||||
|
->with('client.flow.state.token'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('getId') |
||||||
|
->willReturn('SessionId'); |
||||||
|
$this->tokenProvider |
||||||
|
->expects($this->once()) |
||||||
|
->method('getToken') |
||||||
|
->with('SessionId') |
||||||
|
->willThrowException(new InvalidTokenException()); |
||||||
|
|
||||||
|
$expected = new Http\Response(); |
||||||
|
$expected->setStatus(Http::STATUS_FORBIDDEN); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); |
||||||
|
} |
||||||
|
|
||||||
|
public function testGeneratePasswordWithPassword() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn('MyStateToken'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('remove') |
||||||
|
->with('client.flow.state.token'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('getId') |
||||||
|
->willReturn('SessionId'); |
||||||
|
$myToken = $this->createMock(IToken::class); |
||||||
|
$myToken |
||||||
|
->expects($this->once()) |
||||||
|
->method('getLoginName') |
||||||
|
->willReturn('MyLoginName'); |
||||||
|
$this->tokenProvider |
||||||
|
->expects($this->once()) |
||||||
|
->method('getToken') |
||||||
|
->with('SessionId') |
||||||
|
->willReturn($myToken); |
||||||
|
$this->tokenProvider |
||||||
|
->expects($this->once()) |
||||||
|
->method('getPassword') |
||||||
|
->with($myToken, 'SessionId') |
||||||
|
->willReturn('MyPassword'); |
||||||
|
$this->random |
||||||
|
->expects($this->once()) |
||||||
|
->method('generate') |
||||||
|
->with(72) |
||||||
|
->willReturn('MyGeneratedToken'); |
||||||
|
$user = $this->createMock(IUser::class); |
||||||
|
$user |
||||||
|
->expects($this->once()) |
||||||
|
->method('getUID') |
||||||
|
->willReturn('MyUid'); |
||||||
|
$this->userSession |
||||||
|
->expects($this->once()) |
||||||
|
->method('getUser') |
||||||
|
->willReturn($user); |
||||||
|
$this->tokenProvider |
||||||
|
->expects($this->once()) |
||||||
|
->method('generateToken') |
||||||
|
->with( |
||||||
|
'MyGeneratedToken', |
||||||
|
'MyUid', |
||||||
|
'MyLoginName', |
||||||
|
'MyPassword', |
||||||
|
'unknown', |
||||||
|
IToken::PERMANENT_TOKEN, |
||||||
|
IToken::DO_NOT_REMEMBER |
||||||
|
); |
||||||
|
$this->request |
||||||
|
->expects($this->once()) |
||||||
|
->method('getServerHost') |
||||||
|
->willReturn('example.com'); |
||||||
|
|
||||||
|
$expected = new Http\RedirectResponse('nc://MyLoginName:MyGeneratedToken@example.com'); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); |
||||||
|
} |
||||||
|
|
||||||
|
public function testGeneratePasswordWithoutPassword() { |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('get') |
||||||
|
->with('client.flow.state.token') |
||||||
|
->willReturn('MyStateToken'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('remove') |
||||||
|
->with('client.flow.state.token'); |
||||||
|
$this->session |
||||||
|
->expects($this->once()) |
||||||
|
->method('getId') |
||||||
|
->willReturn('SessionId'); |
||||||
|
$myToken = $this->createMock(IToken::class); |
||||||
|
$myToken |
||||||
|
->expects($this->once()) |
||||||
|
->method('getLoginName') |
||||||
|
->willReturn('MyLoginName'); |
||||||
|
$this->tokenProvider |
||||||
|
->expects($this->once()) |
||||||
|
->method('getToken') |
||||||
|
->with('SessionId') |
||||||
|
->willReturn($myToken); |
||||||
|
$this->tokenProvider |
||||||
|
->expects($this->once()) |
||||||
|
->method('getPassword') |
||||||
|
->with($myToken, 'SessionId') |
||||||
|
->willThrowException(new PasswordlessTokenException()); |
||||||
|
$this->random |
||||||
|
->expects($this->once()) |
||||||
|
->method('generate') |
||||||
|
->with(72) |
||||||
|
->willReturn('MyGeneratedToken'); |
||||||
|
$user = $this->createMock(IUser::class); |
||||||
|
$user |
||||||
|
->expects($this->once()) |
||||||
|
->method('getUID') |
||||||
|
->willReturn('MyUid'); |
||||||
|
$this->userSession |
||||||
|
->expects($this->once()) |
||||||
|
->method('getUser') |
||||||
|
->willReturn($user); |
||||||
|
$this->tokenProvider |
||||||
|
->expects($this->once()) |
||||||
|
->method('generateToken') |
||||||
|
->with( |
||||||
|
'MyGeneratedToken', |
||||||
|
'MyUid', |
||||||
|
'MyLoginName', |
||||||
|
null, |
||||||
|
'unknown', |
||||||
|
IToken::PERMANENT_TOKEN, |
||||||
|
IToken::DO_NOT_REMEMBER |
||||||
|
); |
||||||
|
$this->request |
||||||
|
->expects($this->once()) |
||||||
|
->method('getServerHost') |
||||||
|
->willReturn('example.com'); |
||||||
|
|
||||||
|
$expected = new Http\RedirectResponse('nc://MyLoginName:MyGeneratedToken@example.com'); |
||||||
|
$this->assertEquals($expected, $this->clientFlowLoginController->generateAppPassword('MyStateToken')); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue