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