commit
efa545f8f0
33 changed files with 2060 additions and 428 deletions
|
@ -33,7 +33,7 @@ use Exception;
|
|||
use OC\AppFramework\Http\Request;
|
||||
use OCP\IRequest;
|
||||
use OCP\ISession;
|
||||
use OCP\IUserSession;
|
||||
use OC\User\Session;
|
||||
use Sabre\DAV\Auth\Backend\AbstractBasic;
|
||||
use Sabre\DAV\Exception\NotAuthenticated;
|
||||
use Sabre\DAV\Exception\ServiceUnavailable;
|
||||
|
@ -45,7 +45,7 @@ class Auth extends AbstractBasic {
|
|||
|
||||
/** @var ISession */
|
||||
private $session;
|
||||
/** @var IUserSession */
|
||||
/** @var Session */
|
||||
private $userSession;
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
|
@ -54,12 +54,12 @@ class Auth extends AbstractBasic {
|
|||
|
||||
/**
|
||||
* @param ISession $session
|
||||
* @param IUserSession $userSession
|
||||
* @param Session $userSession
|
||||
* @param IRequest $request
|
||||
* @param string $principalPrefix
|
||||
*/
|
||||
public function __construct(ISession $session,
|
||||
IUserSession $userSession,
|
||||
Session $userSession,
|
||||
IRequest $request,
|
||||
$principalPrefix = 'principals/users/') {
|
||||
$this->session = $session;
|
||||
|
@ -104,6 +104,7 @@ class Auth extends AbstractBasic {
|
|||
} else {
|
||||
\OC_Util::setUpFS(); //login hooks may need early access to the filesystem
|
||||
if($this->userSession->login($username, $password)) {
|
||||
$this->userSession->createSessionToken($this->request, $username, $password);
|
||||
\OC_Util::setUpFS($this->userSession->getUser()->getUID());
|
||||
$this->session->set(self::DAV_AUTHENTICATED, $this->userSession->getUser()->getUID());
|
||||
$this->session->close();
|
||||
|
|
|
@ -28,7 +28,7 @@ use OCP\IRequest;
|
|||
use OCP\IUser;
|
||||
use Test\TestCase;
|
||||
use OCP\ISession;
|
||||
use OCP\IUserSession;
|
||||
use OC\User\Session;
|
||||
|
||||
/**
|
||||
* Class Auth
|
||||
|
@ -41,7 +41,7 @@ class Auth extends TestCase {
|
|||
private $session;
|
||||
/** @var \OCA\DAV\Connector\Sabre\Auth */
|
||||
private $auth;
|
||||
/** @var IUserSession */
|
||||
/** @var Session */
|
||||
private $userSession;
|
||||
/** @var IRequest */
|
||||
private $request;
|
||||
|
@ -50,7 +50,7 @@ class Auth extends TestCase {
|
|||
parent::setUp();
|
||||
$this->session = $this->getMockBuilder('\OCP\ISession')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->userSession = $this->getMockBuilder('\OCP\IUserSession')
|
||||
$this->userSession = $this->getMockBuilder('\OC\User\Session')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
$this->request = $this->getMockBuilder('\OCP\IRequest')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
|
@ -170,6 +170,10 @@ class Auth extends TestCase {
|
|||
->method('login')
|
||||
->with('MyTestUser', 'MyTestPassword')
|
||||
->will($this->returnValue(true));
|
||||
$this->userSession
|
||||
->expects($this->once())
|
||||
->method('createSessionToken')
|
||||
->with($this->request, 'MyTestUser', 'MyTestPassword');
|
||||
$this->session
|
||||
->expects($this->once())
|
||||
->method('set')
|
||||
|
@ -559,6 +563,9 @@ class Auth extends TestCase {
|
|||
->method('login')
|
||||
->with('username', 'password')
|
||||
->will($this->returnValue(true));
|
||||
$this->userSession
|
||||
->expects($this->once())
|
||||
->method('createSessionToken');
|
||||
$user = $this->getMockBuilder('\OCP\IUser')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
|
78
build/integration/features/auth.feature
Normal file
78
build/integration/features/auth.feature
Normal file
|
@ -0,0 +1,78 @@
|
|||
Feature: auth
|
||||
|
||||
Background:
|
||||
Given user "user0" exists
|
||||
Given a new client token is used
|
||||
|
||||
|
||||
# FILES APP
|
||||
|
||||
Scenario: access files app anonymously
|
||||
When requesting "/index.php/apps/files" with "GET"
|
||||
Then the HTTP status code should be "401"
|
||||
|
||||
Scenario: access files app with basic auth
|
||||
When requesting "/index.php/apps/files" with "GET" using basic auth
|
||||
Then the HTTP status code should be "200"
|
||||
|
||||
Scenario: access files app with basic token auth
|
||||
When requesting "/index.php/apps/files" with "GET" using basic token auth
|
||||
Then the HTTP status code should be "200"
|
||||
|
||||
Scenario: access files app with a client token
|
||||
When requesting "/index.php/apps/files" with "GET" using a client token
|
||||
Then the HTTP status code should be "200"
|
||||
|
||||
Scenario: access files app with browser session
|
||||
Given a new browser session is started
|
||||
When requesting "/index.php/apps/files" with "GET" using browser session
|
||||
Then the HTTP status code should be "200"
|
||||
|
||||
|
||||
# WebDAV
|
||||
|
||||
Scenario: using WebDAV anonymously
|
||||
When requesting "/remote.php/webdav" with "PROPFIND"
|
||||
Then the HTTP status code should be "401"
|
||||
|
||||
Scenario: using WebDAV with basic auth
|
||||
When requesting "/remote.php/webdav" with "PROPFIND" using basic auth
|
||||
Then the HTTP status code should be "207"
|
||||
|
||||
Scenario: using WebDAV with token auth
|
||||
When requesting "/remote.php/webdav" with "PROPFIND" using basic token auth
|
||||
Then the HTTP status code should be "207"
|
||||
|
||||
# DAV token auth is not possible yet
|
||||
#Scenario: using WebDAV with a client token
|
||||
# When requesting "/remote.php/webdav" with "PROPFIND" using a client token
|
||||
# Then the HTTP status code should be "207"
|
||||
|
||||
Scenario: using WebDAV with browser session
|
||||
Given a new browser session is started
|
||||
When requesting "/remote.php/webdav" with "PROPFIND" using browser session
|
||||
Then the HTTP status code should be "207"
|
||||
|
||||
|
||||
# OCS
|
||||
|
||||
Scenario: using OCS anonymously
|
||||
When requesting "/ocs/v1.php/apps/files_sharing/api/v1/remote_shares" with "GET"
|
||||
Then the OCS status code should be "997"
|
||||
|
||||
Scenario: using OCS with basic auth
|
||||
When requesting "/ocs/v1.php/apps/files_sharing/api/v1/remote_shares" with "GET" using basic auth
|
||||
Then the OCS status code should be "100"
|
||||
|
||||
Scenario: using OCS with token auth
|
||||
When requesting "/ocs/v1.php/apps/files_sharing/api/v1/remote_shares" with "GET" using basic token auth
|
||||
Then the OCS status code should be "100"
|
||||
|
||||
Scenario: using OCS with client token
|
||||
When requesting "/ocs/v1.php/apps/files_sharing/api/v1/remote_shares" with "GET" using a client token
|
||||
Then the OCS status code should be "100"
|
||||
|
||||
Scenario: using OCS with browser session
|
||||
Given a new browser session is started
|
||||
When requesting "/ocs/v1.php/apps/files_sharing/api/v1/remote_shares" with "GET" using browser session
|
||||
Then the OCS status code should be "100"
|
117
build/integration/features/bootstrap/Auth.php
Normal file
117
build/integration/features/bootstrap/Auth.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
trait Auth {
|
||||
|
||||
private $clientToken;
|
||||
|
||||
/** @BeforeScenario */
|
||||
public function tearUpScenario() {
|
||||
$this->client = new Client();
|
||||
$this->responseXml = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @When requesting :url with :method
|
||||
*/
|
||||
public function requestingWith($url, $method) {
|
||||
$this->sendRequest($url, $method);
|
||||
}
|
||||
|
||||
private function sendRequest($url, $method, $authHeader = null, $useCookies = false) {
|
||||
$fullUrl = substr($this->baseUrl, 0, -5) . $url;
|
||||
try {
|
||||
if ($useCookies) {
|
||||
$request = $this->client->createRequest($method, $fullUrl, [
|
||||
'cookies' => $this->cookieJar,
|
||||
]);
|
||||
} else {
|
||||
$request = $this->client->createRequest($method, $fullUrl);
|
||||
}
|
||||
if ($authHeader) {
|
||||
$request->setHeader('Authorization', $authHeader);
|
||||
}
|
||||
$request->setHeader('OCS_APIREQUEST', 'true');
|
||||
$request->setHeader('requesttoken', $this->requestToken);
|
||||
$this->response = $this->client->send($request);
|
||||
} catch (ClientException $ex) {
|
||||
$this->response = $ex->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given a new client token is used
|
||||
*/
|
||||
public function aNewClientTokenIsUsed() {
|
||||
$client = new Client();
|
||||
$resp = $client->post(substr($this->baseUrl, 0, -5) . '/token/generate', [
|
||||
'json' => [
|
||||
'user' => 'user0',
|
||||
'password' => '123456',
|
||||
]
|
||||
]);
|
||||
$this->clientToken = json_decode($resp->getBody()->getContents())->token;
|
||||
}
|
||||
|
||||
/**
|
||||
* @When requesting :url with :method using basic auth
|
||||
*/
|
||||
public function requestingWithBasicAuth($url, $method) {
|
||||
$this->sendRequest($url, $method, 'basic ' . base64_encode('user0:123456'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When requesting :url with :method using basic token auth
|
||||
*/
|
||||
public function requestingWithBasicTokenAuth($url, $method) {
|
||||
$this->sendRequest($url, $method, 'basic ' . base64_encode('user0:' . $this->clientToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* @When requesting :url with :method using a client token
|
||||
*/
|
||||
public function requestingWithUsingAClientToken($url, $method) {
|
||||
$this->sendRequest($url, $method, 'token ' . $this->clientToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @When requesting :url with :method using browser session
|
||||
*/
|
||||
public function requestingWithBrowserSession($url, $method) {
|
||||
$this->sendRequest($url, $method, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Given a new browser session is started
|
||||
*/
|
||||
public function aNewBrowserSessionIsStarted() {
|
||||
$loginUrl = substr($this->baseUrl, 0, -5) . '/login';
|
||||
// Request a new session and extract CSRF token
|
||||
$client = new Client();
|
||||
$response = $client->get(
|
||||
$loginUrl, [
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$this->extracRequestTokenFromResponse($response);
|
||||
|
||||
// Login and extract new token
|
||||
$client = new Client();
|
||||
$response = $client->post(
|
||||
$loginUrl, [
|
||||
'body' => [
|
||||
'user' => 'user0',
|
||||
'password' => '123456',
|
||||
'requesttoken' => $this->requestToken,
|
||||
],
|
||||
'cookies' => $this->cookieJar,
|
||||
]
|
||||
);
|
||||
$this->extracRequestTokenFromResponse($response);
|
||||
}
|
||||
|
||||
}
|
|
@ -6,6 +6,9 @@ use GuzzleHttp\Message\ResponseInterface;
|
|||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
trait BasicStructure {
|
||||
|
||||
use Auth;
|
||||
|
||||
/** @var string */
|
||||
private $currentUser = '';
|
||||
|
||||
|
@ -176,7 +179,7 @@ trait BasicStructure {
|
|||
* @param string $user
|
||||
*/
|
||||
public function loggingInUsingWebAs($user) {
|
||||
$loginUrl = substr($this->baseUrl, 0, -5);
|
||||
$loginUrl = substr($this->baseUrl, 0, -5) . '/login';
|
||||
// Request a new session and extract CSRF token
|
||||
$client = new Client();
|
||||
$response = $client->get(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
/**
|
||||
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
* @author Morris Jobke <hey@morrisjobke.de>
|
||||
* @author Roeland Jago Douma <rullzer@owncloud.com>
|
||||
|
@ -28,12 +29,14 @@ namespace OC\Core;
|
|||
|
||||
use OC\AppFramework\Utility\SimpleContainer;
|
||||
use OC\AppFramework\Utility\TimeFactory;
|
||||
use OC\Core\Controller\LoginController;
|
||||
use \OCP\AppFramework\App;
|
||||
use OC\Core\Controller\LostController;
|
||||
use OC\Core\Controller\UserController;
|
||||
use OC\Core\Controller\AvatarController;
|
||||
use \OCP\Util;
|
||||
use OC\Core\Controller\LoginController;
|
||||
use OC\Core\Controller\LostController;
|
||||
use OC\Core\Controller\TokenController;
|
||||
use OC\Core\Controller\UserController;
|
||||
use OC_Defaults;
|
||||
use OCP\AppFramework\App;
|
||||
use OCP\Util;
|
||||
|
||||
/**
|
||||
* Class Application
|
||||
|
@ -101,6 +104,15 @@ class Application extends App {
|
|||
$c->query('URLGenerator')
|
||||
);
|
||||
});
|
||||
$container->registerService('TokenController', function(SimpleContainer $c) {
|
||||
return new TokenController(
|
||||
$c->query('AppName'),
|
||||
$c->query('Request'),
|
||||
$c->query('UserManager'),
|
||||
$c->query('OC\Authentication\Token\DefaultTokenProvider'),
|
||||
$c->query('SecureRandom')
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Core class wrappers
|
||||
|
@ -132,6 +144,9 @@ class Application extends App {
|
|||
$container->registerService('UserSession', function(SimpleContainer $c) {
|
||||
return $c->query('ServerContainer')->getUserSession();
|
||||
});
|
||||
$container->registerService('Session', function(SimpleContainer $c) {
|
||||
return $c->query('ServerContainer')->getSession();
|
||||
});
|
||||
$container->registerService('Cache', function(SimpleContainer $c) {
|
||||
return $c->query('ServerContainer')->getCache();
|
||||
});
|
||||
|
@ -139,7 +154,7 @@ class Application extends App {
|
|||
return $c->query('ServerContainer')->getUserFolder();
|
||||
});
|
||||
$container->registerService('Defaults', function() {
|
||||
return new \OC_Defaults;
|
||||
return new OC_Defaults;
|
||||
});
|
||||
$container->registerService('Mailer', function(SimpleContainer $c) {
|
||||
return $c->query('ServerContainer')->getMailer();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
|
@ -21,7 +23,10 @@
|
|||
|
||||
namespace OC\Core\Controller;
|
||||
|
||||
use OC\Setup;
|
||||
use OC;
|
||||
use OC\User\Session;
|
||||
use OC_App;
|
||||
use OC_Util;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\RedirectResponse;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
|
@ -31,17 +36,21 @@ use OCP\ISession;
|
|||
use OCP\IURLGenerator;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
|
||||
class LoginController extends Controller {
|
||||
|
||||
/** @var IUserManager */
|
||||
private $userManager;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var ISession */
|
||||
private $session;
|
||||
/** @var IUserSession */
|
||||
|
||||
/** @var Session */
|
||||
private $userSession;
|
||||
|
||||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
|
@ -51,16 +60,11 @@ class LoginController extends Controller {
|
|||
* @param IUserManager $userManager
|
||||
* @param IConfig $config
|
||||
* @param ISession $session
|
||||
* @param IUserSession $userSession
|
||||
* @param Session $userSession
|
||||
* @param IURLGenerator $urlGenerator
|
||||
*/
|
||||
function __construct($appName,
|
||||
IRequest $request,
|
||||
IUserManager $userManager,
|
||||
IConfig $config,
|
||||
ISession $session,
|
||||
IUserSession $userSession,
|
||||
IURLGenerator $urlGenerator) {
|
||||
function __construct($appName, IRequest $request, IUserManager $userManager, IConfig $config, ISession $session,
|
||||
Session $userSession, IURLGenerator $urlGenerator) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->userManager = $userManager;
|
||||
$this->config = $config;
|
||||
|
@ -94,20 +98,18 @@ class LoginController extends Controller {
|
|||
* @param string $redirect_url
|
||||
* @param string $remember_login
|
||||
*
|
||||
* @return TemplateResponse
|
||||
* @return TemplateResponse|RedirectResponse
|
||||
*/
|
||||
public function showLoginForm($user,
|
||||
$redirect_url,
|
||||
$remember_login) {
|
||||
if($this->userSession->isLoggedIn()) {
|
||||
return new RedirectResponse(\OC_Util::getDefaultPageUrl());
|
||||
public function showLoginForm($user, $redirect_url, $remember_login) {
|
||||
if ($this->userSession->isLoggedIn()) {
|
||||
return new RedirectResponse(OC_Util::getDefaultPageUrl());
|
||||
}
|
||||
|
||||
$parameters = array();
|
||||
$loginMessages = $this->session->get('loginMessages');
|
||||
$errors = [];
|
||||
$messages = [];
|
||||
if(is_array($loginMessages)) {
|
||||
if (is_array($loginMessages)) {
|
||||
list($errors, $messages) = $loginMessages;
|
||||
}
|
||||
$this->session->remove('loginMessages');
|
||||
|
@ -137,8 +139,8 @@ class LoginController extends Controller {
|
|||
}
|
||||
}
|
||||
|
||||
$parameters['alt_login'] = \OC_App::getAlternativeLogIns();
|
||||
$parameters['rememberLoginAllowed'] = \OC_Util::rememberLoginAllowed();
|
||||
$parameters['alt_login'] = OC_App::getAlternativeLogIns();
|
||||
$parameters['rememberLoginAllowed'] = OC_Util::rememberLoginAllowed();
|
||||
$parameters['rememberLoginState'] = !empty($remember_login) ? $remember_login : 0;
|
||||
|
||||
if (!is_null($user) && $user !== '') {
|
||||
|
@ -150,11 +152,49 @@ class LoginController extends Controller {
|
|||
}
|
||||
|
||||
return new TemplateResponse(
|
||||
$this->appName,
|
||||
'login',
|
||||
$parameters,
|
||||
'guest'
|
||||
$this->appName, 'login', $parameters, 'guest'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @UseSession
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $redirect_url
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function tryLogin($user, $password, $redirect_url) {
|
||||
// TODO: Add all the insane error handling
|
||||
$loginResult = $this->userManager->checkPassword($user, $password);
|
||||
if ($loginResult === false) {
|
||||
$users = $this->userManager->getByEmail($user);
|
||||
// we only allow login by email if unique
|
||||
if (count($users) === 1) {
|
||||
$user = $users[0]->getUID();
|
||||
$loginResult = $this->userManager->checkPassword($user, $password);
|
||||
}
|
||||
}
|
||||
if ($loginResult === false) {
|
||||
$this->session->set('loginMessages', [
|
||||
[],
|
||||
['invalidpassword']
|
||||
]);
|
||||
// Read current user and append if possible
|
||||
$args = !is_null($user) ? ['user' => $user] : [];
|
||||
return new RedirectResponse($this->urlGenerator->linkToRoute('core.login.showLoginForm', $args));
|
||||
}
|
||||
$this->userSession->createSessionToken($this->request, $loginResult->getUID(), $password);
|
||||
if (!is_null($redirect_url) && $this->userSession->isLoggedIn()) {
|
||||
$location = $this->urlGenerator->getAbsoluteURL(urldecode($redirect_url));
|
||||
// Deny the redirect if the URL contains a @
|
||||
// This prevents unvalidated redirects like ?redirect_url=:user@domain.com
|
||||
if (strpos($location, '@') === false) {
|
||||
return new RedirectResponse($location);
|
||||
}
|
||||
}
|
||||
return new RedirectResponse($this->urlGenerator->linkTo('files', 'index'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
90
core/Controller/TokenController.php
Normal file
90
core/Controller/TokenController.php
Normal file
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Core\Controller;
|
||||
|
||||
use OC\AppFramework\Http;
|
||||
use OC\Authentication\Token\DefaultTokenProvider;
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OC\User\Manager;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\JSONResponse;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use OCP\IRequest;
|
||||
use OCP\Security\ISecureRandom;
|
||||
|
||||
class TokenController extends Controller {
|
||||
|
||||
/** @var Manager */
|
||||
private $userManager;
|
||||
|
||||
/** @var DefaultTokenProvider */
|
||||
private $tokenProvider;
|
||||
|
||||
/** @var ISecureRandom */
|
||||
private $secureRandom;
|
||||
|
||||
/**
|
||||
* @param string $appName
|
||||
* @param IRequest $request
|
||||
* @param Manager $userManager
|
||||
* @param DefaultTokenProvider $tokenProvider
|
||||
* @param ISecureRandom $secureRandom
|
||||
*/
|
||||
public function __construct($appName, IRequest $request, Manager $userManager, DefaultTokenProvider $tokenProvider,
|
||||
ISecureRandom $secureRandom) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->userManager = $userManager;
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
$this->secureRandom = $secureRandom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new access token clients can authenticate with
|
||||
*
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $user
|
||||
* @param string $password
|
||||
* @param string $name the name of the client
|
||||
* @return JSONResponse
|
||||
*/
|
||||
public function generateToken($user, $password, $name = 'unknown client') {
|
||||
if (is_null($user) || is_null($password)) {
|
||||
$response = new Response();
|
||||
$response->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY);
|
||||
return $response;
|
||||
}
|
||||
if ($this->userManager->checkPassword($user, $password) === false) {
|
||||
$response = new Response();
|
||||
$response->setStatus(Http::STATUS_UNAUTHORIZED);
|
||||
return $response;
|
||||
}
|
||||
$token = $this->secureRandom->generate(128);
|
||||
$this->tokenProvider->generateToken($token, $user, $password, $name, IToken::PERMANENT_TOKEN);
|
||||
return [
|
||||
'token' => $token,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
|
@ -42,9 +42,11 @@ $application->registerRoutes($this, [
|
|||
['name' => 'avatar#postCroppedAvatar', 'url' => '/avatar/cropped', 'verb' => 'POST'],
|
||||
['name' => 'avatar#getTmpAvatar', 'url' => '/avatar/tmp', 'verb' => 'GET'],
|
||||
['name' => 'avatar#postAvatar', 'url' => '/avatar/', 'verb' => 'POST'],
|
||||
['name' => 'login#tryLogin', 'url' => '/login', 'verb' => 'POST'],
|
||||
['name' => 'login#showLoginForm', 'url' => '/login', 'verb' => 'GET'],
|
||||
['name' => 'login#logout', 'url' => '/logout', 'verb' => 'GET'],
|
||||
]
|
||||
['name' => 'token#generateToken', 'url' => '/token/generate', 'verb' => 'POST'],
|
||||
],
|
||||
]);
|
||||
|
||||
// Post installation check
|
||||
|
|
|
@ -9,7 +9,7 @@ script('core', [
|
|||
?>
|
||||
|
||||
<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}</style><![endif]-->
|
||||
<form method="post" name="login" action="<?php p(OC::$WEBROOT) ?>/">
|
||||
<form method="post" name="login">
|
||||
<fieldset>
|
||||
<?php if (!empty($_['redirect_url'])) {
|
||||
print_unescaped('<input type="hidden" name="redirect_url" value="' . \OCP\Util::sanitizeHTML($_['redirect_url']) . '">');
|
||||
|
|
|
@ -1031,6 +1031,92 @@
|
|||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
<name>*dbprefix*authtoken</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<!-- Foreign Key users::uid -->
|
||||
<field>
|
||||
<name>uid</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>64</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>password</name>
|
||||
<type>clob</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>4000</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>name</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>100</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>token</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>200</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>type</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>2</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>last_activity</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>authtoken_token_index</name>
|
||||
<unique>true</unique>
|
||||
<field>
|
||||
<name>token</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
<index>
|
||||
<name>authtoken_last_activity_index</name>
|
||||
<field>
|
||||
<name>last_activity</name>
|
||||
<sorting>ascending</sorting>
|
||||
</field>
|
||||
</index>
|
||||
|
||||
</declaration>
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<!--
|
||||
|
|
193
lib/base.php
193
lib/base.php
|
@ -856,7 +856,7 @@ class OC {
|
|||
} else {
|
||||
// For guests: Load only filesystem and logging
|
||||
OC_App::loadApps(array('filesystem', 'logging'));
|
||||
\OC_User::tryBasicAuthLogin();
|
||||
self::handleLogin($request);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -878,17 +878,6 @@ class OC {
|
|||
}
|
||||
}
|
||||
|
||||
// Handle redirect URL for logged in users
|
||||
if (isset($_REQUEST['redirect_url']) && OC_User::isLoggedIn()) {
|
||||
$location = \OC::$server->getURLGenerator()->getAbsoluteURL(urldecode($_REQUEST['redirect_url']));
|
||||
|
||||
// Deny the redirect if the URL contains a @
|
||||
// This prevents unvalidated redirects like ?redirect_url=:user@domain.com
|
||||
if (strpos($location, '@') === false) {
|
||||
header('Location: ' . $location);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Handle WebDAV
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'PROPFIND') {
|
||||
// not allowed any more to prevent people
|
||||
|
@ -904,14 +893,35 @@ class OC {
|
|||
OC_App::loadApps();
|
||||
OC_User::setupBackends();
|
||||
OC_Util::setupFS();
|
||||
// FIXME
|
||||
// Redirect to default application
|
||||
OC_Util::redirectToDefaultPage();
|
||||
} else {
|
||||
// Not handled and not logged in
|
||||
self::handleLogin();
|
||||
header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute('core.login.showLoginForm'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check login: apache auth, auth token, basic auth
|
||||
*
|
||||
* @param OCP\IRequest $request
|
||||
* @return boolean
|
||||
*/
|
||||
private static function handleLogin(OCP\IRequest $request) {
|
||||
$userSession = self::$server->getUserSession();
|
||||
if (OC_User::handleApacheAuth()) {
|
||||
return true;
|
||||
}
|
||||
if ($userSession->tryTokenLogin($request)) {
|
||||
return true;
|
||||
}
|
||||
if ($userSession->tryBasicAuthLogin($request)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function handleAuthHeaders() {
|
||||
//copy http auth headers for apache+php-fcgid work around
|
||||
if (isset($_SERVER['HTTP_XAUTHORIZATION']) && !isset($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
|
@ -932,163 +942,6 @@ class OC {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static function handleLogin() {
|
||||
OC_App::loadApps(array('prelogin'));
|
||||
$error = array();
|
||||
$messages = [];
|
||||
|
||||
try {
|
||||
// auth possible via apache module?
|
||||
if (OC::tryApacheAuth()) {
|
||||
$error[] = 'apacheauthfailed';
|
||||
} // remember was checked after last login
|
||||
elseif (OC::tryRememberLogin()) {
|
||||
$error[] = 'invalidcookie';
|
||||
} // logon via web form
|
||||
elseif (OC::tryFormLogin()) {
|
||||
$error[] = 'invalidpassword';
|
||||
}
|
||||
} catch (\OC\User\LoginException $e) {
|
||||
$messages[] = $e->getMessage();
|
||||
} catch (\Exception $ex) {
|
||||
\OCP\Util::logException('handleLogin', $ex);
|
||||
// do not disclose information. show generic error
|
||||
$error[] = 'internalexception';
|
||||
}
|
||||
|
||||
if(!\OC::$server->getUserSession()->isLoggedIn()) {
|
||||
$loginMessages = array(array_unique($error), $messages);
|
||||
\OC::$server->getSession()->set('loginMessages', $loginMessages);
|
||||
// Read current user and append if possible
|
||||
$args = [];
|
||||
if(isset($_POST['user'])) {
|
||||
$args['user'] = $_POST['user'];
|
||||
}
|
||||
|
||||
$redirectionTarget = \OC::$server->getURLGenerator()->linkToRoute('core.login.showLoginForm', $args);
|
||||
header('Location: ' . $redirectionTarget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove outdated and therefore invalid tokens for a user
|
||||
* @param string $user
|
||||
*/
|
||||
protected static function cleanupLoginTokens($user) {
|
||||
$config = \OC::$server->getConfig();
|
||||
$cutoff = time() - $config->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
|
||||
$tokens = $config->getUserKeys($user, 'login_token');
|
||||
foreach ($tokens as $token) {
|
||||
$time = $config->getUserValue($user, 'login_token', $token);
|
||||
if ($time < $cutoff) {
|
||||
$config->deleteUserValue($user, 'login_token', $token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to login a user via HTTP authentication
|
||||
* @return bool|void
|
||||
*/
|
||||
protected static function tryApacheAuth() {
|
||||
$return = OC_User::handleApacheAuth();
|
||||
|
||||
// if return is true we are logged in -> redirect to the default page
|
||||
if ($return === true) {
|
||||
$_REQUEST['redirect_url'] = \OC::$server->getRequest()->getRequestUri();
|
||||
OC_Util::redirectToDefaultPage();
|
||||
exit;
|
||||
}
|
||||
|
||||
// in case $return is null apache based auth is not enabled
|
||||
return is_null($return) ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to login a user using the remember me cookie.
|
||||
* @return bool Whether the provided cookie was valid
|
||||
*/
|
||||
protected static function tryRememberLogin() {
|
||||
if (!isset($_COOKIE["oc_remember_login"])
|
||||
|| !isset($_COOKIE["oc_token"])
|
||||
|| !isset($_COOKIE["oc_username"])
|
||||
|| !$_COOKIE["oc_remember_login"]
|
||||
|| !OC_Util::rememberLoginAllowed()
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (\OC::$server->getConfig()->getSystemValue('debug', false)) {
|
||||
\OCP\Util::writeLog('core', 'Trying to login from cookie', \OCP\Util::DEBUG);
|
||||
}
|
||||
|
||||
if(OC_User::userExists($_COOKIE['oc_username'])) {
|
||||
self::cleanupLoginTokens($_COOKIE['oc_username']);
|
||||
// verify whether the supplied "remember me" token was valid
|
||||
$granted = OC_User::loginWithCookie(
|
||||
$_COOKIE['oc_username'], $_COOKIE['oc_token']);
|
||||
if($granted === true) {
|
||||
OC_Util::redirectToDefaultPage();
|
||||
// doesn't return
|
||||
}
|
||||
\OCP\Util::writeLog('core', 'Authentication cookie rejected for user ' .
|
||||
$_COOKIE['oc_username'], \OCP\Util::WARN);
|
||||
// if you reach this point you have changed your password
|
||||
// or you are an attacker
|
||||
// we can not delete tokens here because users may reach
|
||||
// this point multiple times after a password change
|
||||
}
|
||||
|
||||
OC_User::unsetMagicInCookie();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to login a user using the form based authentication
|
||||
* @return bool|void
|
||||
*/
|
||||
protected static function tryFormLogin() {
|
||||
if (!isset($_POST["user"]) || !isset($_POST['password'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!(\OC::$server->getRequest()->passesCSRFCheck())) {
|
||||
return false;
|
||||
}
|
||||
OC_App::loadApps();
|
||||
|
||||
//setup extra user backends
|
||||
OC_User::setupBackends();
|
||||
|
||||
if (OC_User::login((string)$_POST["user"], (string)$_POST["password"])) {
|
||||
$userId = OC_User::getUser();
|
||||
|
||||
// setting up the time zone
|
||||
if (isset($_POST['timezone-offset'])) {
|
||||
self::$server->getSession()->set('timezone', (string)$_POST['timezone-offset']);
|
||||
self::$server->getConfig()->setUserValue($userId, 'core', 'timezone', (string)$_POST['timezone']);
|
||||
}
|
||||
|
||||
self::cleanupLoginTokens($userId);
|
||||
if (!empty($_POST["remember_login"])) {
|
||||
$config = self::$server->getConfig();
|
||||
if ($config->getSystemValue('debug', false)) {
|
||||
self::$server->getLogger()->debug('Setting remember login to cookie', array('app' => 'core'));
|
||||
}
|
||||
$token = \OC::$server->getSecureRandom()->generate(32);
|
||||
$config->setUserValue($userId, 'login_token', $token, time());
|
||||
OC_User::setMagicInCookie($userId, $token);
|
||||
} else {
|
||||
OC_User::unsetMagicInCookie();
|
||||
}
|
||||
OC_Util::redirectToDefaultPage();
|
||||
exit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
OC::init();
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Authentication\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
class InvalidTokenException extends Exception {
|
||||
|
||||
}
|
81
lib/private/Authentication/Token/DefaultToken.php
Normal file
81
lib/private/Authentication/Token/DefaultToken.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Authentication\Token;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
|
||||
/**
|
||||
* @method void setId(int $id)
|
||||
* @method void setUid(string $uid);
|
||||
* @method void setPassword(string $password)
|
||||
* @method string getPassword()
|
||||
* @method void setName(string $name)
|
||||
* @method string getName()
|
||||
* @method void setToken(string $token)
|
||||
* @method string getToken()
|
||||
* @method void setType(string $type)
|
||||
* @method int getType()
|
||||
* @method void setLastActivity(int $lastActivity)
|
||||
* @method int getLastActivity()
|
||||
*/
|
||||
class DefaultToken extends Entity implements IToken {
|
||||
|
||||
/**
|
||||
* @var string user UID
|
||||
*/
|
||||
protected $uid;
|
||||
|
||||
/**
|
||||
* @var string encrypted user password
|
||||
*/
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* @var string token name (e.g. browser/OS)
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $token;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $lastActivity;
|
||||
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getUID() {
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
}
|
36
lib/private/Authentication/Token/DefaultTokenCleanupJob.php
Normal file
36
lib/private/Authentication/Token/DefaultTokenCleanupJob.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Authentication\Token;
|
||||
|
||||
use OC;
|
||||
use OC\BackgroundJob\Job;
|
||||
|
||||
class DefaultTokenCleanupJob extends Job {
|
||||
|
||||
protected function run($argument) {
|
||||
/* @var $provider DefaultTokenProvider */
|
||||
$provider = OC::$server->query('OC\Authentication\Token\DefaultTokenProvider');
|
||||
$provider->invalidateOldTokens();
|
||||
}
|
||||
|
||||
}
|
86
lib/private/Authentication/Token/DefaultTokenMapper.php
Normal file
86
lib/private/Authentication/Token/DefaultTokenMapper.php
Normal file
|
@ -0,0 +1,86 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Authentication\Token;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\Mapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
class DefaultTokenMapper extends Mapper {
|
||||
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, 'authtoken');
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate (delete) a given token
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function invalidate($token) {
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('authtoken')
|
||||
->andWhere($qb->expr()->eq('token', $qb->createParameter('token')))
|
||||
->setParameter('token', $token)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $olderThan
|
||||
*/
|
||||
public function invalidateOld($olderThan) {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->delete('authtoken')
|
||||
->where($qb->expr()->lt('last_activity', $qb->createParameter('last_activity')))
|
||||
->andWhere($qb->expr()->eq('type', $qb->createParameter('type')))
|
||||
->setParameter('last_activity', $olderThan, IQueryBuilder::PARAM_INT)
|
||||
->setParameter('type', IToken::TEMPORARY_TOKEN, IQueryBuilder::PARAM_INT)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user UID for the given token
|
||||
*
|
||||
* @param string $token
|
||||
* @throws DoesNotExistException
|
||||
* @return DefaultToken
|
||||
*/
|
||||
public function getToken($token) {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$result = $qb->select('id', 'uid', 'password', 'name', 'type', 'token', 'last_activity')
|
||||
->from('authtoken')
|
||||
->where($qb->expr()->eq('token', $qb->createParameter('token')))
|
||||
->setParameter('token', $token)
|
||||
->execute();
|
||||
|
||||
$data = $result->fetch();
|
||||
if ($data === false) {
|
||||
throw new DoesNotExistException('token does not exist');
|
||||
}
|
||||
return DefaultToken::fromRow($data);
|
||||
}
|
||||
|
||||
}
|
205
lib/private/Authentication/Token/DefaultTokenProvider.php
Normal file
205
lib/private/Authentication/Token/DefaultTokenProvider.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Authentication\Token;
|
||||
|
||||
use Exception;
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IConfig;
|
||||
use OCP\ILogger;
|
||||
use OCP\Security\ICrypto;
|
||||
|
||||
class DefaultTokenProvider implements IProvider {
|
||||
|
||||
/** @var DefaultTokenMapper */
|
||||
private $mapper;
|
||||
|
||||
/** @var ICrypto */
|
||||
private $crypto;
|
||||
|
||||
/** @var IConfig */
|
||||
private $config;
|
||||
|
||||
/** @var ILogger $logger */
|
||||
private $logger;
|
||||
|
||||
/** @var ITimeFactory $time */
|
||||
private $time;
|
||||
|
||||
/**
|
||||
* @param DefaultTokenMapper $mapper
|
||||
* @param ICrypto $crypto
|
||||
* @param IConfig $config
|
||||
* @param ILogger $logger
|
||||
* @param ITimeFactory $time
|
||||
*/
|
||||
public function __construct(DefaultTokenMapper $mapper, ICrypto $crypto, IConfig $config, ILogger $logger, ITimeFactory $time) {
|
||||
$this->mapper = $mapper;
|
||||
$this->crypto = $crypto;
|
||||
$this->config = $config;
|
||||
$this->logger = $logger;
|
||||
$this->time = $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and persist a new token
|
||||
*
|
||||
* @param string $token
|
||||
* @param string $uid
|
||||
* @param string $password
|
||||
* @param string $name
|
||||
* @param int $type token type
|
||||
* @return DefaultToken
|
||||
*/
|
||||
public function generateToken($token, $uid, $password, $name, $type = IToken::TEMPORARY_TOKEN) {
|
||||
$dbToken = new DefaultToken();
|
||||
$dbToken->setUid($uid);
|
||||
$dbToken->setPassword($this->encryptPassword($password, $token));
|
||||
$dbToken->setName($name);
|
||||
$dbToken->setToken($this->hashToken($token));
|
||||
$dbToken->setType($type);
|
||||
$dbToken->setLastActivity($this->time->getTime());
|
||||
|
||||
$this->mapper->insert($dbToken);
|
||||
|
||||
return $dbToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update token activity timestamp
|
||||
*
|
||||
* @throws InvalidTokenException
|
||||
* @param IToken $token
|
||||
*/
|
||||
public function updateToken(IToken $token) {
|
||||
if (!($token instanceof DefaultToken)) {
|
||||
throw new InvalidTokenException();
|
||||
}
|
||||
/** @var DefaultToken $token */
|
||||
$token->setLastActivity($this->time->getTime());
|
||||
|
||||
$this->mapper->update($token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @throws InvalidTokenException
|
||||
* @return DefaultToken
|
||||
*/
|
||||
public function getToken($token) {
|
||||
try {
|
||||
return $this->mapper->getToken($this->hashToken($token));
|
||||
} catch (DoesNotExistException $ex) {
|
||||
throw new InvalidTokenException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DefaultToken $savedToken
|
||||
* @param string $token session token
|
||||
* @return string
|
||||
*/
|
||||
public function getPassword(DefaultToken $savedToken, $token) {
|
||||
return $this->decryptPassword($savedToken->getPassword(), $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate (delete) the given session token
|
||||
*
|
||||
* @param string $token
|
||||
*/
|
||||
public function invalidateToken($token) {
|
||||
$this->mapper->invalidate($this->hashToken($token));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate (delete) old session tokens
|
||||
*/
|
||||
public function invalidateOldTokens() {
|
||||
$olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24);
|
||||
$this->logger->info('Invalidating tokens older than ' . date('c', $olderThan));
|
||||
$this->mapper->invalidateOld($olderThan);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @throws InvalidTokenException
|
||||
* @return DefaultToken user UID
|
||||
*/
|
||||
public function validateToken($token) {
|
||||
$this->logger->debug('validating default token <' . $token . '>');
|
||||
try {
|
||||
$dbToken = $this->mapper->getToken($this->hashToken($token));
|
||||
$this->logger->debug('valid token for ' . $dbToken->getUID());
|
||||
return $dbToken;
|
||||
} catch (DoesNotExistException $ex) {
|
||||
$this->logger->warning('invalid token');
|
||||
throw new InvalidTokenException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return string
|
||||
*/
|
||||
private function hashToken($token) {
|
||||
$secret = $this->config->getSystemValue('secret');
|
||||
return hash('sha512', $token . $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt the given password
|
||||
*
|
||||
* The token is used as key
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $token
|
||||
* @return string encrypted password
|
||||
*/
|
||||
private function encryptPassword($password, $token) {
|
||||
$secret = $this->config->getSystemValue('secret');
|
||||
return $this->crypto->encrypt($password, $token . $secret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the given password
|
||||
*
|
||||
* The token is used as key
|
||||
*
|
||||
* @param string $password
|
||||
* @param string $token
|
||||
* @return string the decrypted key
|
||||
*/
|
||||
private function decryptPassword($password, $token) {
|
||||
$secret = $this->config->getSystemValue('secret');
|
||||
try {
|
||||
return $this->crypto->decrypt($password, $token . $secret);
|
||||
} catch (Exception $ex) {
|
||||
// Delete the invalid token
|
||||
$this->invalidateToken($token);
|
||||
throw new InvalidTokenException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
42
lib/private/Authentication/Token/IProvider.php
Normal file
42
lib/private/Authentication/Token/IProvider.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Authentication\Token;
|
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
|
||||
interface IProvider {
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @throws InvalidTokenException
|
||||
* @return IToken
|
||||
*/
|
||||
public function validateToken($token);
|
||||
|
||||
/**
|
||||
* Update token activity timestamp
|
||||
*
|
||||
* @param IToken $token
|
||||
*/
|
||||
public function updateToken(IToken $token);
|
||||
}
|
46
lib/private/Authentication/Token/IToken.php
Normal file
46
lib/private/Authentication/Token/IToken.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Authentication\Token;
|
||||
|
||||
/**
|
||||
* @since 9.1.0
|
||||
*/
|
||||
interface IToken {
|
||||
|
||||
const TEMPORARY_TOKEN = 0;
|
||||
const PERMANENT_TOKEN = 1;
|
||||
|
||||
/**
|
||||
* Get the token ID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* Get the user UID
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUID();
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
||||
* @author Bernhard Reiter <ockham@raz.or.at>
|
||||
* @author Björn Schießle <schiessle@owncloud.com>
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
* @author Christopher Schäpers <kondou@ts.unde.re>
|
||||
* @author Joas Schilling <nickvergessen@owncloud.com>
|
||||
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
|
||||
|
@ -208,12 +209,35 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
});
|
||||
return $groupManager;
|
||||
});
|
||||
$this->registerService('OC\Authentication\Token\DefaultTokenMapper', function (Server $c) {
|
||||
$dbConnection = $c->getDatabaseConnection();
|
||||
return new Authentication\Token\DefaultTokenMapper($dbConnection);
|
||||
});
|
||||
$this->registerService('OC\Authentication\Token\DefaultTokenProvider', function (Server $c) {
|
||||
$mapper = $c->query('OC\Authentication\Token\DefaultTokenMapper');
|
||||
$crypto = $c->getCrypto();
|
||||
$config = $c->getConfig();
|
||||
$logger = $c->getLogger();
|
||||
$timeFactory = new TimeFactory();
|
||||
return new \OC\Authentication\Token\DefaultTokenProvider($mapper, $crypto, $config, $logger, $timeFactory);
|
||||
});
|
||||
$this->registerService('UserSession', function (Server $c) {
|
||||
$manager = $c->getUserManager();
|
||||
|
||||
$session = new \OC\Session\Memory('');
|
||||
$timeFactory = new TimeFactory();
|
||||
// Token providers might require a working database. This code
|
||||
// might however be called when ownCloud is not yet setup.
|
||||
if (\OC::$server->getSystemConfig()->getValue('installed', false)) {
|
||||
$defaultTokenProvider = $c->query('OC\Authentication\Token\DefaultTokenProvider');
|
||||
$tokenProviders = [
|
||||
$defaultTokenProvider,
|
||||
];
|
||||
} else {
|
||||
$defaultTokenProvider = null;
|
||||
$tokenProviders = [];
|
||||
}
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = new \OC\User\Session($manager, $session, $timeFactory, $defaultTokenProvider, $tokenProviders);
|
||||
$userSession->listen('\OC\User', 'preCreateUser', function ($uid, $password) {
|
||||
\OC_Hook::emit('OC_User', 'pre_createUser', array('run' => true, 'uid' => $uid, 'password' => $password));
|
||||
});
|
||||
|
|
|
@ -364,7 +364,14 @@ class Setup {
|
|||
|
||||
$group =\OC::$server->getGroupManager()->createGroup('admin');
|
||||
$group->addUser($user);
|
||||
\OC_User::login($username, $password);
|
||||
|
||||
// Create a session token for the newly created user
|
||||
// The token provider requires a working db, so it's not injected on setup
|
||||
/* @var $userSession User\Session */
|
||||
$userSession = \OC::$server->getUserSession();
|
||||
$defaultTokenProvider = \OC::$server->query('OC\Authentication\Token\DefaultTokenProvider');
|
||||
$userSession->setTokenProvider($defaultTokenProvider);
|
||||
$userSession->createSessionToken($request, $username, $password);
|
||||
|
||||
//guess what this does
|
||||
Installer::installShippedApps();
|
||||
|
@ -382,6 +389,8 @@ class Setup {
|
|||
$config->setSystemValue('logtimezone', date_default_timezone_get());
|
||||
}
|
||||
|
||||
self::installBackgroundJobs();
|
||||
|
||||
//and we are done
|
||||
$config->setSystemValue('installed', true);
|
||||
}
|
||||
|
@ -389,6 +398,10 @@ class Setup {
|
|||
return $error;
|
||||
}
|
||||
|
||||
public static function installBackgroundJobs() {
|
||||
\OC::$server->getJobList()->add('\OC\Authentication\Token\DefaultTokenCleanupJob');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Absolute path to htaccess
|
||||
*/
|
||||
|
|
|
@ -216,6 +216,8 @@ class Updater extends BasicEmitter {
|
|||
try {
|
||||
Setup::updateHtaccess();
|
||||
Setup::protectDataDirectory();
|
||||
// TODO: replace with the new repair step mechanism https://github.com/owncloud/core/pull/24378
|
||||
Setup::installBackgroundJobs();
|
||||
} catch (\Exception $e) {
|
||||
throw new \Exception($e->getMessage());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Arthur Schiwon <blizzz@owncloud.com>
|
||||
* @author Bernhard Posselt <dev@bernhard-posselt.com>
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
* @author Joas Schilling <nickvergessen@owncloud.com>
|
||||
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
|
||||
* @author Lukas Reschke <lukas@owncloud.com>
|
||||
|
@ -31,10 +33,22 @@
|
|||
|
||||
namespace OC\User;
|
||||
|
||||
use OC;
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Token\DefaultTokenProvider;
|
||||
use OC\Authentication\Token\IProvider;
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OC\Hooks\Emitter;
|
||||
use OC_User;
|
||||
use OC_Util;
|
||||
use OCA\DAV\Connector\Sabre\Auth;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IRequest;
|
||||
use OCP\ISession;
|
||||
use OCP\IUser;
|
||||
use OCP\IUserManager;
|
||||
use OCP\IUserSession;
|
||||
use OCP\Session\Exceptions\SessionNotAvailableException;
|
||||
|
||||
/**
|
||||
* Class Session
|
||||
|
@ -55,22 +69,57 @@ use OCP\IUserSession;
|
|||
* @package OC\User
|
||||
*/
|
||||
class Session implements IUserSession, Emitter {
|
||||
/** @var \OC\User\Manager $manager */
|
||||
/*
|
||||
* @var Manager $manager
|
||||
*/
|
||||
|
||||
private $manager;
|
||||
|
||||
/** @var \OC\Session\Session $session */
|
||||
/*
|
||||
* @var ISession $session
|
||||
*/
|
||||
private $session;
|
||||
|
||||
/** @var \OC\User\User $activeUser */
|
||||
/*
|
||||
* @var ITimeFactory
|
||||
*/
|
||||
private $timeFacory;
|
||||
|
||||
/**
|
||||
* @var DefaultTokenProvider
|
||||
*/
|
||||
private $tokenProvider;
|
||||
|
||||
/**
|
||||
* @var IProvider[]
|
||||
*/
|
||||
private $tokenProviders;
|
||||
|
||||
/**
|
||||
* @var User $activeUser
|
||||
*/
|
||||
protected $activeUser;
|
||||
|
||||
/**
|
||||
* @param IUserManager $manager
|
||||
* @param ISession $session
|
||||
* @param ITimeFactory $timeFacory
|
||||
* @param IProvider $tokenProvider
|
||||
* @param IProvider[] $tokenProviders
|
||||
*/
|
||||
public function __construct(IUserManager $manager, ISession $session) {
|
||||
public function __construct(IUserManager $manager, ISession $session, ITimeFactory $timeFacory, $tokenProvider, array $tokenProviders = []) {
|
||||
$this->manager = $manager;
|
||||
$this->session = $session;
|
||||
$this->timeFacory = $timeFacory;
|
||||
$this->tokenProvider = $tokenProvider;
|
||||
$this->tokenProviders = $tokenProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DefaultTokenProvider $provider
|
||||
*/
|
||||
public function setTokenProvider(DefaultTokenProvider $provider) {
|
||||
$this->tokenProvider = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,7 +143,7 @@ class Session implements IUserSession, Emitter {
|
|||
/**
|
||||
* get the manager object
|
||||
*
|
||||
* @return \OC\User\Manager
|
||||
* @return Manager
|
||||
*/
|
||||
public function getManager() {
|
||||
return $this->manager;
|
||||
|
@ -125,7 +174,7 @@ class Session implements IUserSession, Emitter {
|
|||
/**
|
||||
* set the currently active user
|
||||
*
|
||||
* @param \OC\User\User|null $user
|
||||
* @param User|null $user
|
||||
*/
|
||||
public function setUser($user) {
|
||||
if (is_null($user)) {
|
||||
|
@ -139,25 +188,65 @@ class Session implements IUserSession, Emitter {
|
|||
/**
|
||||
* get the current active user
|
||||
*
|
||||
* @return \OCP\IUser|null Current user, otherwise null
|
||||
* @return IUser|null Current user, otherwise null
|
||||
*/
|
||||
public function getUser() {
|
||||
// FIXME: This is a quick'n dirty work-around for the incognito mode as
|
||||
// described at https://github.com/owncloud/core/pull/12912#issuecomment-67391155
|
||||
if (\OC_User::isIncognitoMode()) {
|
||||
if (OC_User::isIncognitoMode()) {
|
||||
return null;
|
||||
}
|
||||
if ($this->activeUser) {
|
||||
return $this->activeUser;
|
||||
} else {
|
||||
if (is_null($this->activeUser)) {
|
||||
$uid = $this->session->get('user_id');
|
||||
if ($uid !== null) {
|
||||
$this->activeUser = $this->manager->get($uid);
|
||||
return $this->activeUser;
|
||||
} else {
|
||||
if (is_null($uid)) {
|
||||
return null;
|
||||
}
|
||||
$this->activeUser = $this->manager->get($uid);
|
||||
if (is_null($this->activeUser)) {
|
||||
return null;
|
||||
}
|
||||
$this->validateSession($this->activeUser);
|
||||
}
|
||||
return $this->activeUser;
|
||||
}
|
||||
|
||||
protected function validateSession(IUser $user) {
|
||||
try {
|
||||
$sessionId = $this->session->getId();
|
||||
} catch (SessionNotAvailableException $ex) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$token = $this->tokenProvider->getToken($sessionId);
|
||||
} catch (InvalidTokenException $ex) {
|
||||
// Session was invalidated
|
||||
$this->logout();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether login credentials are still valid
|
||||
// This check is performed each 5 minutes
|
||||
$lastCheck = $this->session->get('last_login_check') ? : 0;
|
||||
$now = $this->timeFacory->getTime();
|
||||
if ($lastCheck < ($now - 60 * 5)) {
|
||||
try {
|
||||
$pwd = $this->tokenProvider->getPassword($token, $sessionId);
|
||||
} catch (InvalidTokenException $ex) {
|
||||
// An invalid token password was used -> log user out
|
||||
$this->logout();
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->manager->checkPassword($user->getUID(), $pwd) === false) {
|
||||
// Password has changed -> log user out
|
||||
$this->logout();
|
||||
return;
|
||||
}
|
||||
$this->session->set('last_login_check', $now);
|
||||
}
|
||||
|
||||
// Session is valid, so the token can be refreshed
|
||||
$this->updateToken($this->tokenProvider, $token);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -218,6 +307,11 @@ class Session implements IUserSession, Emitter {
|
|||
$this->session->regenerateId();
|
||||
$this->manager->emit('\OC\User', 'preLogin', array($uid, $password));
|
||||
$user = $this->manager->checkPassword($uid, $password);
|
||||
if ($user === false) {
|
||||
if ($this->validateToken($password)) {
|
||||
$user = $this->getUser();
|
||||
}
|
||||
}
|
||||
if ($user !== false) {
|
||||
if (!is_null($user)) {
|
||||
if ($user->isEnabled()) {
|
||||
|
@ -225,6 +319,7 @@ class Session implements IUserSession, Emitter {
|
|||
$this->setLoginName($uid);
|
||||
$this->manager->emit('\OC\User', 'postLogin', array($user, $password));
|
||||
if ($this->isLoggedIn()) {
|
||||
$this->prepareUserLogin();
|
||||
return true;
|
||||
} else {
|
||||
// injecting l10n does not work - there is a circular dependency between session and \OCP\L10N\IFactory
|
||||
|
@ -241,6 +336,142 @@ class Session implements IUserSession, Emitter {
|
|||
return false;
|
||||
}
|
||||
|
||||
protected function prepareUserLogin() {
|
||||
// TODO: mock/inject/use non-static
|
||||
// Refresh the token
|
||||
\OC::$server->getCsrfTokenManager()->refreshToken();
|
||||
//we need to pass the user name, which may differ from login name
|
||||
$user = $this->getUser()->getUID();
|
||||
OC_Util::setupFS($user);
|
||||
//trigger creation of user home and /files folder
|
||||
\OC::$server->getUserFolder($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to login the user with HTTP Basic Authentication
|
||||
*
|
||||
* @param IRequest $request
|
||||
* @return boolean if the login was successful
|
||||
*/
|
||||
public function tryBasicAuthLogin(IRequest $request) {
|
||||
if (!empty($request->server['PHP_AUTH_USER']) && !empty($request->server['PHP_AUTH_PW'])) {
|
||||
$result = $this->login($request->server['PHP_AUTH_USER'], $request->server['PHP_AUTH_PW']);
|
||||
if ($result === true) {
|
||||
/**
|
||||
* Add DAV authenticated. This should in an ideal world not be
|
||||
* necessary but the iOS App reads cookies from anywhere instead
|
||||
* only the DAV endpoint.
|
||||
* This makes sure that the cookies will be valid for the whole scope
|
||||
* @see https://github.com/owncloud/core/issues/22893
|
||||
*/
|
||||
$this->session->set(
|
||||
Auth::DAV_AUTHENTICATED, $this->getUser()->getUID()
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function loginWithToken($uid) {
|
||||
// TODO: $this->manager->emit('\OC\User', 'preTokenLogin', array($uid));
|
||||
$user = $this->manager->get($uid);
|
||||
if (is_null($user)) {
|
||||
// user does not exist
|
||||
return false;
|
||||
}
|
||||
|
||||
//login
|
||||
$this->setUser($user);
|
||||
// TODO: $this->manager->emit('\OC\User', 'postTokenLogin', array($user));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new session token for the given user credentials
|
||||
*
|
||||
* @param IRequest $request
|
||||
* @param string $uid user UID
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
*/
|
||||
public function createSessionToken(IRequest $request, $uid, $password) {
|
||||
if (is_null($this->manager->get($uid))) {
|
||||
// User does not exist
|
||||
return false;
|
||||
}
|
||||
$name = isset($request->server['HTTP_USER_AGENT']) ? $request->server['HTTP_USER_AGENT'] : 'unknown browser';
|
||||
$loggedIn = $this->login($uid, $password);
|
||||
if ($loggedIn) {
|
||||
try {
|
||||
$sessionId = $this->session->getId();
|
||||
$this->tokenProvider->generateToken($sessionId, $uid, $password, $name);
|
||||
} catch (SessionNotAvailableException $ex) {
|
||||
|
||||
}
|
||||
}
|
||||
return $loggedIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return boolean
|
||||
*/
|
||||
private function validateToken($token) {
|
||||
foreach ($this->tokenProviders as $provider) {
|
||||
try {
|
||||
$token = $provider->validateToken($token);
|
||||
if (!is_null($token)) {
|
||||
$result = $this->loginWithToken($token->getUID());
|
||||
if ($result) {
|
||||
// Login success
|
||||
$this->updateToken($provider, $token);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (InvalidTokenException $ex) {
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IProvider $provider
|
||||
* @param IToken $token
|
||||
*/
|
||||
private function updateToken(IProvider $provider, IToken $token) {
|
||||
// To save unnecessary DB queries, this is only done once a minute
|
||||
$lastTokenUpdate = $this->session->get('last_token_update') ? : 0;
|
||||
$now = $this->timeFacory->getTime();
|
||||
if ($lastTokenUpdate < ($now - 60)) {
|
||||
$provider->updateToken($token);
|
||||
$this->session->set('last_token_update', $now);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to login the user with auth token header
|
||||
*
|
||||
* @todo check remember me cookie
|
||||
* @return boolean
|
||||
*/
|
||||
public function tryTokenLogin(IRequest $request) {
|
||||
$authHeader = $request->getHeader('Authorization');
|
||||
if (strpos($authHeader, 'token ') === false) {
|
||||
// No auth header, let's try session id
|
||||
try {
|
||||
$sessionId = $this->session->getId();
|
||||
return $this->validateToken($sessionId);
|
||||
} catch (SessionNotAvailableException $ex) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$token = substr($authHeader, 6);
|
||||
return $this->validateToken($token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* perform login using the magic cookie (remember login)
|
||||
*
|
||||
|
@ -258,15 +489,15 @@ class Session implements IUserSession, Emitter {
|
|||
}
|
||||
|
||||
// get stored tokens
|
||||
$tokens = \OC::$server->getConfig()->getUserKeys($uid, 'login_token');
|
||||
$tokens = OC::$server->getConfig()->getUserKeys($uid, 'login_token');
|
||||
// test cookies token against stored tokens
|
||||
if (!in_array($currentToken, $tokens, true)) {
|
||||
return false;
|
||||
}
|
||||
// replace successfully used token with a new one
|
||||
\OC::$server->getConfig()->deleteUserValue($uid, 'login_token', $currentToken);
|
||||
$newToken = \OC::$server->getSecureRandom()->generate(32);
|
||||
\OC::$server->getConfig()->setUserValue($uid, 'login_token', $newToken, time());
|
||||
OC::$server->getConfig()->deleteUserValue($uid, 'login_token', $currentToken);
|
||||
$newToken = OC::$server->getSecureRandom()->generate(32);
|
||||
OC::$server->getConfig()->setUserValue($uid, 'login_token', $newToken, time());
|
||||
$this->setMagicInCookie($user->getUID(), $newToken);
|
||||
|
||||
//login
|
||||
|
@ -280,6 +511,14 @@ class Session implements IUserSession, Emitter {
|
|||
*/
|
||||
public function logout() {
|
||||
$this->manager->emit('\OC\User', 'logout');
|
||||
$user = $this->getUser();
|
||||
if (!is_null($user)) {
|
||||
try {
|
||||
$this->tokenProvider->invalidateToken($this->session->getId());
|
||||
} catch (SessionNotAvailableException $ex) {
|
||||
|
||||
}
|
||||
}
|
||||
$this->setUser(null);
|
||||
$this->setLoginName(null);
|
||||
$this->unsetMagicInCookie();
|
||||
|
@ -293,11 +532,11 @@ class Session implements IUserSession, Emitter {
|
|||
* @param string $token
|
||||
*/
|
||||
public function setMagicInCookie($username, $token) {
|
||||
$secureCookie = \OC::$server->getRequest()->getServerProtocol() === 'https';
|
||||
$expires = time() + \OC::$server->getConfig()->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
|
||||
setcookie("oc_username", $username, $expires, \OC::$WEBROOT, '', $secureCookie, true);
|
||||
setcookie("oc_token", $token, $expires, \OC::$WEBROOT, '', $secureCookie, true);
|
||||
setcookie("oc_remember_login", "1", $expires, \OC::$WEBROOT, '', $secureCookie, true);
|
||||
$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
|
||||
$expires = time() + OC::$server->getConfig()->getSystemValue('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
|
||||
setcookie('oc_username', $username, $expires, OC::$WEBROOT, '', $secureCookie, true);
|
||||
setcookie('oc_token', $token, $expires, OC::$WEBROOT, '', $secureCookie, true);
|
||||
setcookie('oc_remember_login', '1', $expires, OC::$WEBROOT, '', $secureCookie, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -305,18 +544,19 @@ class Session implements IUserSession, Emitter {
|
|||
*/
|
||||
public function unsetMagicInCookie() {
|
||||
//TODO: DI for cookies and IRequest
|
||||
$secureCookie = \OC::$server->getRequest()->getServerProtocol() === 'https';
|
||||
$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
|
||||
|
||||
unset($_COOKIE["oc_username"]); //TODO: DI
|
||||
unset($_COOKIE["oc_token"]);
|
||||
unset($_COOKIE["oc_remember_login"]);
|
||||
setcookie('oc_username', '', time() - 3600, \OC::$WEBROOT, '',$secureCookie, true);
|
||||
setcookie('oc_token', '', time() - 3600, \OC::$WEBROOT, '', $secureCookie, true);
|
||||
setcookie('oc_remember_login', '', time() - 3600, \OC::$WEBROOT, '', $secureCookie, true);
|
||||
unset($_COOKIE['oc_username']); //TODO: DI
|
||||
unset($_COOKIE['oc_token']);
|
||||
unset($_COOKIE['oc_remember_login']);
|
||||
setcookie('oc_username', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true);
|
||||
setcookie('oc_token', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true);
|
||||
setcookie('oc_remember_login', '', time() - 3600, OC::$WEBROOT, '', $secureCookie, true);
|
||||
// old cookies might be stored under /webroot/ instead of /webroot
|
||||
// and Firefox doesn't like it!
|
||||
setcookie('oc_username', '', time() - 3600, \OC::$WEBROOT . '/', '', $secureCookie, true);
|
||||
setcookie('oc_token', '', time() - 3600, \OC::$WEBROOT . '/', '', $secureCookie, true);
|
||||
setcookie('oc_remember_login', '', time() - 3600, \OC::$WEBROOT . '/', '', $secureCookie, true);
|
||||
setcookie('oc_username', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
|
||||
setcookie('oc_token', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
|
||||
setcookie('oc_remember_login', '', time() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -337,7 +337,7 @@ class OC_API {
|
|||
}
|
||||
|
||||
// reuse existing login
|
||||
$loggedIn = OC_User::isLoggedIn();
|
||||
$loggedIn = \OC::$server->getUserSession()->isLoggedIn();
|
||||
if ($loggedIn === true) {
|
||||
$ocsApiRequest = isset($_SERVER['HTTP_OCS_APIREQUEST']) ? $_SERVER['HTTP_OCS_APIREQUEST'] === 'true' : false;
|
||||
if ($ocsApiRequest) {
|
||||
|
@ -353,35 +353,25 @@ class OC_API {
|
|||
|
||||
// basic auth - because OC_User::login will create a new session we shall only try to login
|
||||
// if user and pass are set
|
||||
if(isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) ) {
|
||||
$authUser = $_SERVER['PHP_AUTH_USER'];
|
||||
$authPw = $_SERVER['PHP_AUTH_PW'];
|
||||
try {
|
||||
$return = OC_User::login($authUser, $authPw);
|
||||
} catch (\OC\User\LoginException $e) {
|
||||
return false;
|
||||
$userSession = \OC::$server->getUserSession();
|
||||
$request = \OC::$server->getRequest();
|
||||
try {
|
||||
$loginSuccess = $userSession->tryTokenLogin($request);
|
||||
if (!$loginSuccess) {
|
||||
$loginSuccess = $userSession->tryBasicAuthLogin($request);
|
||||
}
|
||||
if ($return === true) {
|
||||
self::$logoutRequired = true;
|
||||
} catch (\OC\User\LoginException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize the user's filesystem
|
||||
\OC_Util::setUpFS(\OC_User::getUser());
|
||||
self::$isLoggedIn = true;
|
||||
if ($loginSuccess === true) {
|
||||
self::$logoutRequired = true;
|
||||
|
||||
/**
|
||||
* Add DAV authenticated. This should in an ideal world not be
|
||||
* necessary but the iOS App reads cookies from anywhere instead
|
||||
* only the DAV endpoint.
|
||||
* This makes sure that the cookies will be valid for the whole scope
|
||||
* @see https://github.com/owncloud/core/issues/22893
|
||||
*/
|
||||
\OC::$server->getSession()->set(
|
||||
\OCA\DAV\Connector\Sabre\Auth::DAV_AUTHENTICATED,
|
||||
\OC::$server->getUserSession()->getUser()->getUID()
|
||||
);
|
||||
// initialize the user's filesystem
|
||||
\OC_Util::setUpFS(\OC_User::getUser());
|
||||
self::$isLoggedIn = true;
|
||||
|
||||
return \OC_User::getUser();
|
||||
}
|
||||
return \OC_User::getUser();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* @author Bart Visscher <bartv@thisnet.nl>
|
||||
* @author Bartek Przybylski <bart.p.pl@gmail.com>
|
||||
* @author Björn Schießle <schiessle@owncloud.com>
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
* @author Florian Preinstorfer <nblock@archlinux.us>
|
||||
* @author Georg Ehrke <georg@owncloud.com>
|
||||
* @author Jakob Sack <mail@jakobsack.de>
|
||||
|
@ -67,7 +68,7 @@ class OC_User {
|
|||
|
||||
private static $_setupedBackends = array();
|
||||
|
||||
// bool, stores if a user want to access a resource anonymously, e.g if he opens a public link
|
||||
// bool, stores if a user want to access a resource anonymously, e.g if they open a public link
|
||||
private static $incognitoMode = false;
|
||||
|
||||
/**
|
||||
|
@ -148,37 +149,7 @@ class OC_User {
|
|||
}
|
||||
|
||||
/**
|
||||
* Try to login a user
|
||||
*
|
||||
* @param string $loginName The login name of the user to log in
|
||||
* @param string $password The password of the user
|
||||
* @return boolean|null
|
||||
*
|
||||
* Log in a user and regenerate a new session - if the password is ok
|
||||
*/
|
||||
public static function login($loginName, $password) {
|
||||
|
||||
$result = self::getUserSession()->login($loginName, $password);
|
||||
if (!$result) {
|
||||
$users = \OC::$server->getUserManager()->getByEmail($loginName);
|
||||
// we only allow login by email if unique
|
||||
if (count($users) === 1) {
|
||||
$result = self::getUserSession()->login($users[0]->getUID(), $password);
|
||||
}
|
||||
}
|
||||
if ($result) {
|
||||
// Refresh the token
|
||||
\OC::$server->getCsrfTokenManager()->refreshToken();
|
||||
//we need to pass the user name, which may differ from login name
|
||||
$user = self::getUserSession()->getUser()->getUID();
|
||||
OC_Util::setupFS($user);
|
||||
//trigger creation of user home and /files folder
|
||||
\OC::$server->getUserFolder($user);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to login a user using the magic cookie (remember login)
|
||||
*
|
||||
* @deprecated use \OCP\IUserSession::loginWithCookie()
|
||||
|
@ -283,28 +254,6 @@ class OC_User {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to login the user with HTTP Basic Authentication
|
||||
*/
|
||||
public static function tryBasicAuthLogin() {
|
||||
if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
|
||||
$result = \OC_User::login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
|
||||
if($result === true) {
|
||||
/**
|
||||
* Add DAV authenticated. This should in an ideal world not be
|
||||
* necessary but the iOS App reads cookies from anywhere instead
|
||||
* only the DAV endpoint.
|
||||
* This makes sure that the cookies will be valid for the whole scope
|
||||
* @see https://github.com/owncloud/core/issues/22893
|
||||
*/
|
||||
\OC::$server->getSession()->set(
|
||||
\OCA\DAV\Connector\Sabre\Auth::DAV_AUTHENTICATED,
|
||||
\OC::$server->getUserSession()->getUser()->getUID()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is logged in, considers also the HTTP basic credentials
|
||||
*
|
||||
|
|
|
@ -957,8 +957,7 @@ class OC_Util {
|
|||
public static function checkLoggedIn() {
|
||||
// Check if we are a user
|
||||
if (!OC_User::isLoggedIn()) {
|
||||
header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute(
|
||||
'core.login.showLoginForm',
|
||||
header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php',
|
||||
[
|
||||
'redirect_url' => \OC::$server->getRequest()->getRequestUri()
|
||||
]
|
||||
|
|
|
@ -53,7 +53,9 @@ class LoginControllerTest extends TestCase {
|
|||
$this->userManager = $this->getMock('\\OCP\\IUserManager');
|
||||
$this->config = $this->getMock('\\OCP\\IConfig');
|
||||
$this->session = $this->getMock('\\OCP\\ISession');
|
||||
$this->userSession = $this->getMock('\\OCP\\IUserSession');
|
||||
$this->userSession = $this->getMockBuilder('\\OC\\User\\Session')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->urlGenerator = $this->getMock('\\OCP\\IURLGenerator');
|
||||
|
||||
$this->loginController = new LoginController(
|
||||
|
@ -264,4 +266,74 @@ class LoginControllerTest extends TestCase {
|
|||
);
|
||||
$this->assertEquals($expectedResponse, $this->loginController->showLoginForm('0', '', ''));
|
||||
}
|
||||
|
||||
public function testLoginWithInvalidCredentials() {
|
||||
$user = $this->getMock('\OCP\IUser');
|
||||
$password = 'secret';
|
||||
$loginPageUrl = 'some url';
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('checkPassword')
|
||||
->will($this->returnValue(false));
|
||||
$this->urlGenerator->expects($this->once())
|
||||
->method('linkToRoute')
|
||||
->with('core.login.showLoginForm')
|
||||
->will($this->returnValue($loginPageUrl));
|
||||
|
||||
$this->userSession->expects($this->never())
|
||||
->method('createSessionToken');
|
||||
|
||||
$expected = new \OCP\AppFramework\Http\RedirectResponse($loginPageUrl);
|
||||
$this->assertEquals($expected, $this->loginController->tryLogin($user, $password, ''));
|
||||
}
|
||||
|
||||
public function testLoginWithValidCredentials() {
|
||||
$user = $this->getMock('\OCP\IUser');
|
||||
$password = 'secret';
|
||||
$indexPageUrl = 'some url';
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('checkPassword')
|
||||
->will($this->returnValue($user));
|
||||
$this->userSession->expects($this->once())
|
||||
->method('createSessionToken')
|
||||
->with($this->request, $user->getUID(), $password);
|
||||
$this->urlGenerator->expects($this->once())
|
||||
->method('linkTo')
|
||||
->with('files', 'index')
|
||||
->will($this->returnValue($indexPageUrl));
|
||||
|
||||
$expected = new \OCP\AppFramework\Http\RedirectResponse($indexPageUrl);
|
||||
$this->assertEquals($expected, $this->loginController->tryLogin($user, $password, null));
|
||||
}
|
||||
|
||||
public function testLoginWithValidCredentialsAndRedirectUrl() {
|
||||
$user = $this->getMock('\OCP\IUser');
|
||||
$user->expects($this->any())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('jane'));
|
||||
$password = 'secret';
|
||||
$originalUrl = 'another%20url';
|
||||
$redirectUrl = 'http://localhost/another url';
|
||||
|
||||
$this->userManager->expects($this->once())
|
||||
->method('checkPassword')
|
||||
->with('jane', $password)
|
||||
->will($this->returnValue($user));
|
||||
$this->userSession->expects($this->once())
|
||||
->method('createSessionToken')
|
||||
->with($this->request, $user->getUID(), $password);
|
||||
$this->userSession->expects($this->once())
|
||||
->method('isLoggedIn')
|
||||
->with()
|
||||
->will($this->returnValue(true));
|
||||
$this->urlGenerator->expects($this->once())
|
||||
->method('getAbsoluteURL')
|
||||
->with(urldecode($originalUrl))
|
||||
->will($this->returnValue($redirectUrl));
|
||||
|
||||
$expected = new \OCP\AppFramework\Http\RedirectResponse(urldecode($redirectUrl));
|
||||
$this->assertEquals($expected, $this->loginController->tryLogin($user->getUID(), $password, $originalUrl));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
94
tests/core/controller/TokenControllerTest.php
Normal file
94
tests/core/controller/TokenControllerTest.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Core\Controller;
|
||||
|
||||
use OC\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Response;
|
||||
use Test\TestCase;
|
||||
|
||||
class TokenControllerTest extends TestCase {
|
||||
|
||||
/** \OC\Core\Controller\TokenController */
|
||||
private $tokenController;
|
||||
private $request;
|
||||
private $userManager;
|
||||
private $tokenProvider;
|
||||
private $secureRandom;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->request = $this->getMock('\OCP\IRequest');
|
||||
$this->userManager = $this->getMockBuilder('\OC\User\Manager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->tokenProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->secureRandom = $this->getMock('\OCP\Security\ISecureRandom');
|
||||
|
||||
$this->tokenController = new TokenController('core', $this->request, $this->userManager, $this->tokenProvider,
|
||||
$this->secureRandom);
|
||||
}
|
||||
|
||||
public function testWithoutCredentials() {
|
||||
$expected = new Response();
|
||||
$expected->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY);
|
||||
|
||||
$actual = $this->tokenController->generateToken(null, null);
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testWithInvalidCredentials() {
|
||||
$this->userManager->expects($this->once())
|
||||
->method('checkPassword')
|
||||
->with('john', 'passme')
|
||||
->will($this->returnValue(false));
|
||||
$expected = new Response();
|
||||
$expected->setStatus(Http::STATUS_UNAUTHORIZED);
|
||||
|
||||
$actual = $this->tokenController->generateToken('john', 'passme');
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testWithValidCredentials() {
|
||||
$this->userManager->expects($this->once())
|
||||
->method('checkPassword')
|
||||
->with('john', '123456')
|
||||
->will($this->returnValue(true));
|
||||
$this->secureRandom->expects($this->once())
|
||||
->method('generate')
|
||||
->with(128)
|
||||
->will($this->returnValue('verysecurerandomtoken'));
|
||||
$expected = [
|
||||
'token' => 'verysecurerandomtoken'
|
||||
];
|
||||
|
||||
$actual = $this->tokenController->generateToken('john', '123456');
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\Authentication\Token;
|
||||
|
||||
use OC\Authentication\Token\DefaultTokenCleanupJob;
|
||||
use Test\TestCase;
|
||||
|
||||
class DefaultTokenCleanupJobTest extends TestCase {
|
||||
|
||||
/** @var DefaultTokenCleanupJob */
|
||||
private $job;
|
||||
private $tokenProvider;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->tokenProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->overwriteService('\OC\Authentication\Token\DefaultTokenProvider', $this->tokenProvider);
|
||||
$this->job = new DefaultTokenCleanupJob();
|
||||
}
|
||||
|
||||
protected function tearDown() {
|
||||
parent::tearDown();
|
||||
|
||||
$this->restoreService('\OC\Authentication\Token\DefaultTokenProvider');
|
||||
}
|
||||
|
||||
public function testRun() {
|
||||
$this->tokenProvider->expects($this->once())
|
||||
->method('invalidateOldTokens')
|
||||
->with();
|
||||
$this->invokePrivate($this->job, 'run', [null]);
|
||||
}
|
||||
|
||||
}
|
144
tests/lib/authentication/token/defaulttokenmappertest.php
Normal file
144
tests/lib/authentication/token/defaulttokenmappertest.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\Authentication\Token;
|
||||
|
||||
use OC;
|
||||
use OC\Authentication\Token\DefaultToken;
|
||||
use OC\Authentication\Token\DefaultTokenMapper;
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* Class DefaultTokenMapperTest
|
||||
*
|
||||
* @group DB
|
||||
* @package Test\Authentication
|
||||
*/
|
||||
class DefaultTokenMapperTest extends TestCase {
|
||||
|
||||
/** @var DefaultTokenMapper */
|
||||
private $mapper;
|
||||
private $dbConnection;
|
||||
private $time;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->dbConnection = OC::$server->getDatabaseConnection();
|
||||
$this->time = time();
|
||||
$this->resetDatabase();
|
||||
|
||||
$this->mapper = new DefaultTokenMapper($this->dbConnection);
|
||||
}
|
||||
|
||||
private function resetDatabase() {
|
||||
$qb = $this->dbConnection->getQueryBuilder();
|
||||
$qb->delete('authtoken')->execute();
|
||||
$qb->insert('authtoken')->values([
|
||||
'uid' => $qb->createNamedParameter('user1'),
|
||||
'password' => $qb->createNamedParameter('a75c7116460c082912d8f6860a850904|3nz5qbG1nNSLLi6V|c55365a0e54cfdfac4a175bcf11a7612aea74492277bba6e5d96a24497fa9272488787cb2f3ad34d8b9b8060934fce02f008d371df3ff3848f4aa61944851ff0'),
|
||||
'name' => $qb->createNamedParameter('Firefox on Linux'),
|
||||
'token' => $qb->createNamedParameter('9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206'),
|
||||
'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
|
||||
'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
|
||||
])->execute();
|
||||
$qb->insert('authtoken')->values([
|
||||
'uid' => $qb->createNamedParameter('user2'),
|
||||
'password' => $qb->createNamedParameter('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f'),
|
||||
'name' => $qb->createNamedParameter('Firefox on Android'),
|
||||
'token' => $qb->createNamedParameter('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b'),
|
||||
'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
|
||||
'last_activity' => $qb->createNamedParameter($this->time - 60 * 60 * 24 * 3, IQueryBuilder::PARAM_INT), // Three days ago
|
||||
])->execute();
|
||||
$qb->insert('authtoken')->values([
|
||||
'uid' => $qb->createNamedParameter('user1'),
|
||||
'password' => $qb->createNamedParameter('063de945d6f6b26862d9b6f40652f2d5|DZ/z520tfdXPtd0T|395f6b89be8d9d605e409e20b9d9abe477fde1be38a3223f9e508f979bf906e50d9eaa4dca983ca4fb22a241eb696c3f98654e7775f78c4caf13108f98642b53'),
|
||||
'name' => $qb->createNamedParameter('Iceweasel on Linux'),
|
||||
'token' => $qb->createNamedParameter('47af8697ba590fb82579b5f1b3b6e8066773a62100abbe0db09a289a62f5d980dc300fa3d98b01d7228468d1ab05c1aa14c8d14bd5b6eee9cdf1ac14864680c3'),
|
||||
'type' => $qb->createNamedParameter(IToken::TEMPORARY_TOKEN),
|
||||
'last_activity' => $qb->createNamedParameter($this->time - 120, IQueryBuilder::PARAM_INT), // Two minutes ago
|
||||
])->execute();
|
||||
}
|
||||
|
||||
private function getNumberOfTokens() {
|
||||
$qb = $this->dbConnection->getQueryBuilder();
|
||||
$result = $qb->select($qb->createFunction('count(*) as `count`'))
|
||||
->from('authtoken')
|
||||
->execute()
|
||||
->fetch();
|
||||
return (int) $result['count'];
|
||||
}
|
||||
|
||||
public function testInvalidate() {
|
||||
$token = '9c5a2e661482b65597408a6bb6c4a3d1af36337381872ac56e445a06cdb7fea2b1039db707545c11027a4966919918b19d875a8b774840b18c6cbb7ae56fe206';
|
||||
|
||||
$this->mapper->invalidate($token);
|
||||
|
||||
$this->assertSame(2, $this->getNumberOfTokens());
|
||||
}
|
||||
|
||||
public function testInvalidateInvalid() {
|
||||
$token = 'youwontfindthisoneinthedatabase';
|
||||
|
||||
$this->mapper->invalidate($token);
|
||||
|
||||
$this->assertSame(3, $this->getNumberOfTokens());
|
||||
}
|
||||
|
||||
public function testInvalidateOld() {
|
||||
$olderThan = $this->time - 60 * 60; // One hour
|
||||
|
||||
$this->mapper->invalidateOld($olderThan);
|
||||
|
||||
$this->assertSame(2, $this->getNumberOfTokens());
|
||||
}
|
||||
|
||||
public function testGetToken() {
|
||||
$token = '1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b';
|
||||
$token = new DefaultToken();
|
||||
$token->setUid('user2');
|
||||
$token->setPassword('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f');
|
||||
$token->setName('Firefox on Android');
|
||||
$token->setToken('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b');
|
||||
$token->setType(IToken::TEMPORARY_TOKEN);
|
||||
$token->setLastActivity($this->time - 60 * 60 * 24 * 3);
|
||||
|
||||
$dbToken = $this->mapper->getToken($token->getToken());
|
||||
|
||||
$token->setId($dbToken->getId()); // We don't know the ID
|
||||
$token->resetUpdatedFields();
|
||||
|
||||
$this->assertEquals($token, $dbToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCP\AppFramework\Db\DoesNotExistException
|
||||
*/
|
||||
public function testGetInvalidToken() {
|
||||
$token = 'thisisaninvalidtokenthatisnotinthedatabase';
|
||||
|
||||
$this->mapper->getToken($token);
|
||||
}
|
||||
|
||||
}
|
199
tests/lib/authentication/token/defaulttokenprovidertest.php
Normal file
199
tests/lib/authentication/token/defaulttokenprovidertest.php
Normal file
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @author Christoph Wurst <christoph@owncloud.com>
|
||||
*
|
||||
* @copyright Copyright (c) 2016, ownCloud, Inc.
|
||||
* @license AGPL-3.0
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Test\Authentication\Token;
|
||||
|
||||
use OC\Authentication\Token\DefaultToken;
|
||||
use OC\Authentication\Token\DefaultTokenProvider;
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use Test\TestCase;
|
||||
|
||||
class DefaultTokenProviderTest extends TestCase {
|
||||
|
||||
/** @var DefaultTokenProvider */
|
||||
private $tokenProvider;
|
||||
private $mapper;
|
||||
private $crypto;
|
||||
private $config;
|
||||
private $logger;
|
||||
private $timeFactory;
|
||||
private $time;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->mapper = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenMapper')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$this->crypto = $this->getMock('\OCP\Security\ICrypto');
|
||||
$this->config = $this->getMock('\OCP\IConfig');
|
||||
$this->logger = $this->getMock('\OCP\ILogger');
|
||||
$this->timeFactory = $this->getMock('\OCP\AppFramework\Utility\ITimeFactory');
|
||||
$this->time = 1313131;
|
||||
$this->timeFactory->expects($this->any())
|
||||
->method('getTime')
|
||||
->will($this->returnValue($this->time));
|
||||
|
||||
$this->tokenProvider = new DefaultTokenProvider($this->mapper, $this->crypto, $this->config, $this->logger,
|
||||
$this->timeFactory);
|
||||
}
|
||||
|
||||
public function testGenerateToken() {
|
||||
$token = 'token';
|
||||
$uid = 'user';
|
||||
$password = 'passme';
|
||||
$name = 'Some browser';
|
||||
$type = IToken::PERMANENT_TOKEN;
|
||||
|
||||
$toInsert = new DefaultToken();
|
||||
$toInsert->setUid($uid);
|
||||
$toInsert->setPassword('encryptedpassword');
|
||||
$toInsert->setName($name);
|
||||
$toInsert->setToken(hash('sha512', $token . '1f4h9s'));
|
||||
$toInsert->setType($type);
|
||||
$toInsert->setLastActivity($this->time);
|
||||
|
||||
$this->config->expects($this->any())
|
||||
->method('getSystemValue')
|
||||
->with('secret')
|
||||
->will($this->returnValue('1f4h9s'));
|
||||
$this->crypto->expects($this->once())
|
||||
->method('encrypt')
|
||||
->with($password, $token . '1f4h9s')
|
||||
->will($this->returnValue('encryptedpassword'));
|
||||
$this->mapper->expects($this->once())
|
||||
->method('insert')
|
||||
->with($this->equalTo($toInsert));
|
||||
|
||||
$actual = $this->tokenProvider->generateToken($token, $uid, $password, $name, $type);
|
||||
|
||||
$this->assertEquals($toInsert, $actual);
|
||||
}
|
||||
|
||||
public function testUpdateToken() {
|
||||
$tk = new DefaultToken();
|
||||
$this->mapper->expects($this->once())
|
||||
->method('update')
|
||||
->with($tk);
|
||||
|
||||
$this->tokenProvider->updateToken($tk);
|
||||
|
||||
$this->assertEquals($this->time, $tk->getLastActivity());
|
||||
}
|
||||
|
||||
public function testGetPassword() {
|
||||
$token = 'token1234';
|
||||
$tk = new DefaultToken();
|
||||
$tk->setPassword('someencryptedvalue');
|
||||
$this->config->expects($this->once())
|
||||
->method('getSystemValue')
|
||||
->with('secret')
|
||||
->will($this->returnValue('1f4h9s'));
|
||||
$this->crypto->expects($this->once())
|
||||
->method('decrypt')
|
||||
->with('someencryptedvalue', $token . '1f4h9s')
|
||||
->will($this->returnValue('passme'));
|
||||
|
||||
$actual = $this->tokenProvider->getPassword($tk, $token);
|
||||
|
||||
$this->assertEquals('passme', $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OC\Authentication\Exceptions\InvalidTokenException
|
||||
*/
|
||||
public function testGetPasswordDeletesInvalidToken() {
|
||||
$token = 'token1234';
|
||||
$tk = new DefaultToken();
|
||||
$tk->setPassword('someencryptedvalue');
|
||||
/* @var $tokenProvider DefaultTokenProvider */
|
||||
$tokenProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider')
|
||||
->setMethods([
|
||||
'invalidateToken'
|
||||
])
|
||||
->setConstructorArgs([$this->mapper, $this->crypto, $this->config, $this->logger,
|
||||
$this->timeFactory])
|
||||
->getMock();
|
||||
$this->config->expects($this->once())
|
||||
->method('getSystemValue')
|
||||
->with('secret')
|
||||
->will($this->returnValue('1f4h9s'));
|
||||
$this->crypto->expects($this->once())
|
||||
->method('decrypt')
|
||||
->with('someencryptedvalue', $token . '1f4h9s')
|
||||
->will($this->throwException(new \Exception('some crypto error occurred')));
|
||||
$tokenProvider->expects($this->once())
|
||||
->method('invalidateToken')
|
||||
->with($token);
|
||||
|
||||
$tokenProvider->getPassword($tk, $token);
|
||||
}
|
||||
|
||||
public function testInvalidateToken() {
|
||||
$this->mapper->expects($this->once())
|
||||
->method('invalidate')
|
||||
->with(hash('sha512', 'token7'));
|
||||
|
||||
$this->tokenProvider->invalidateToken('token7');
|
||||
}
|
||||
|
||||
public function testInvalidateOldTokens() {
|
||||
$defaultSessionLifetime = 60 * 60 * 24;
|
||||
$this->config->expects($this->once())
|
||||
->method('getSystemValue')
|
||||
->with('session_lifetime', $defaultSessionLifetime)
|
||||
->will($this->returnValue(150));
|
||||
$this->mapper->expects($this->once())
|
||||
->method('invalidateOld')
|
||||
->with($this->time - 150);
|
||||
|
||||
$this->tokenProvider->invalidateOldTokens();
|
||||
}
|
||||
|
||||
public function testValidateToken() {
|
||||
$token = 'sometoken';
|
||||
$dbToken = new DefaultToken();
|
||||
$this->mapper->expects($this->once())
|
||||
->method('getToken')
|
||||
->with(hash('sha512', $token))
|
||||
->will($this->returnValue($dbToken));
|
||||
|
||||
$actual = $this->tokenProvider->validateToken($token);
|
||||
|
||||
$this->assertEquals($dbToken, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OC\Authentication\Exceptions\InvalidTokenException
|
||||
*/
|
||||
public function testValidateInvalidToken() {
|
||||
$token = 'sometoken';
|
||||
$this->mapper->expects($this->once())
|
||||
->method('getToken')
|
||||
->with(hash('sha512', $token))
|
||||
->will($this->throwException(new DoesNotExistException('')));
|
||||
|
||||
$this->tokenProvider->validateToken($token);
|
||||
}
|
||||
|
||||
}
|
|
@ -11,107 +11,138 @@ namespace Test\User;
|
|||
|
||||
use OC\Session\Memory;
|
||||
use OC\User\User;
|
||||
use OCP\ISession;
|
||||
use OCP\IUserManager;
|
||||
use OCP\UserInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
/**
|
||||
* @group DB
|
||||
* @package Test\User
|
||||
*/
|
||||
class Session extends TestCase {
|
||||
class Session extends \Test\TestCase {
|
||||
|
||||
/** @var \OCP\AppFramework\Utility\ITimeFactory */
|
||||
private $timeFactory;
|
||||
|
||||
/** @var \OC\Authentication\Token\DefaultTokenProvider */
|
||||
protected $defaultProvider;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->timeFactory = $this->getMock('\OCP\AppFramework\Utility\ITimeFactory');
|
||||
$this->timeFactory->expects($this->any())
|
||||
->method('getTime')
|
||||
->will($this->returnValue(10000));
|
||||
$this->defaultProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
}
|
||||
|
||||
public function testGetUser() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$token = new \OC\Authentication\Token\DefaultToken();
|
||||
|
||||
$expectedUser = new User('foo', null);
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->once())
|
||||
$session->expects($this->at(0))
|
||||
->method('get')
|
||||
->with('user_id')
|
||||
->will($this->returnValue('foo'));
|
||||
->will($this->returnValue($expectedUser->getUID()));
|
||||
$sessionId = 'abcdef12345';
|
||||
|
||||
/** @var UserInterface | \PHPUnit_Framework_MockObject_MockObject $backend */
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
$backend->expects($this->once())
|
||||
->method('userExists')
|
||||
->with('foo')
|
||||
$manager = $this->getMockBuilder('\OC\User\Manager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
$session->expects($this->once())
|
||||
->method('getId')
|
||||
->will($this->returnValue($sessionId));
|
||||
$this->defaultProvider->expects($this->once())
|
||||
->method('getToken')
|
||||
->will($this->returnValue($token));
|
||||
$session->expects($this->at(2))
|
||||
->method('get')
|
||||
->with('last_login_check')
|
||||
->will($this->returnValue(null)); // No check has been run yet
|
||||
$this->defaultProvider->expects($this->once())
|
||||
->method('getPassword')
|
||||
->with($token, $sessionId)
|
||||
->will($this->returnValue('password123'));
|
||||
$manager->expects($this->once())
|
||||
->method('checkPassword')
|
||||
->with($expectedUser->getUID(), 'password123')
|
||||
->will($this->returnValue(true));
|
||||
$session->expects($this->at(3))
|
||||
->method('set')
|
||||
->with('last_login_check', 10000);
|
||||
|
||||
$manager = new \OC\User\Manager();
|
||||
$manager->registerBackend($backend);
|
||||
$session->expects($this->at(4))
|
||||
->method('get')
|
||||
->with('last_token_update')
|
||||
->will($this->returnValue(null)); // No check run so far
|
||||
$this->defaultProvider->expects($this->once())
|
||||
->method('updateToken')
|
||||
->with($token);
|
||||
$session->expects($this->at(5))
|
||||
->method('set')
|
||||
->with('last_token_update', $this->equalTo(10000));
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$manager->expects($this->any())
|
||||
->method('get')
|
||||
->with($expectedUser->getUID())
|
||||
->will($this->returnValue($expectedUser));
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]);
|
||||
$user = $userSession->getUser();
|
||||
$this->assertEquals('foo', $user->getUID());
|
||||
$this->assertSame($expectedUser, $user);
|
||||
}
|
||||
|
||||
public function testIsLoggedIn() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->once())
|
||||
->method('get')
|
||||
->with('user_id')
|
||||
->will($this->returnValue('foo'));
|
||||
|
||||
/** @var UserInterface | \PHPUnit_Framework_MockObject_MockObject $backend */
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
$backend->expects($this->once())
|
||||
->method('userExists')
|
||||
->with('foo')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$manager = new \OC\User\Manager();
|
||||
$manager->registerBackend($backend);
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$isLoggedIn = $userSession->isLoggedIn();
|
||||
$this->assertTrue($isLoggedIn);
|
||||
public function isLoggedInData() {
|
||||
return [
|
||||
[true],
|
||||
[false],
|
||||
];
|
||||
}
|
||||
|
||||
public function testNotLoggedIn() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
/**
|
||||
* @dataProvider isLoggedInData
|
||||
*/
|
||||
public function testIsLoggedIn($isLoggedIn) {
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->once())
|
||||
->method('get')
|
||||
->with('user_id')
|
||||
->will($this->returnValue(null));
|
||||
|
||||
/** @var UserInterface | \PHPUnit_Framework_MockObject_MockObject $backend */
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
$backend->expects($this->never())
|
||||
->method('userExists');
|
||||
$manager = $this->getMockBuilder('\OC\User\Manager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$manager = new \OC\User\Manager();
|
||||
$manager->registerBackend($backend);
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$isLoggedIn = $userSession->isLoggedIn();
|
||||
$this->assertFalse($isLoggedIn);
|
||||
$userSession = $this->getMockBuilder('\OC\User\Session')
|
||||
->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]])
|
||||
->setMethods([
|
||||
'getUser'
|
||||
])
|
||||
->getMock();
|
||||
$user = new User('sepp', null);
|
||||
$userSession->expects($this->once())
|
||||
->method('getUser')
|
||||
->will($this->returnValue($isLoggedIn ? $user : null));
|
||||
$this->assertEquals($isLoggedIn, $userSession->isLoggedIn());
|
||||
}
|
||||
|
||||
public function testSetUser() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->once())
|
||||
->method('set')
|
||||
->with('user_id', 'foo');
|
||||
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMock('\OC\User\Manager');
|
||||
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
|
||||
/** @var User | \PHPUnit_Framework_MockObject_MockObject $user */
|
||||
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
||||
$user->expects($this->once())
|
||||
->method('getUID')
|
||||
->will($this->returnValue('foo'));
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]);
|
||||
$userSession->setUser($user);
|
||||
}
|
||||
|
||||
public function testLoginValidPasswordEnabled() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->once())
|
||||
->method('regenerateId');
|
||||
|
@ -127,8 +158,7 @@ class Session extends TestCase {
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
},
|
||||
'foo'));
|
||||
}, 'foo'));
|
||||
|
||||
$managerMethods = get_class_methods('\OC\User\Manager');
|
||||
//keep following methods intact in order to ensure hooks are
|
||||
|
@ -140,13 +170,12 @@ class Session extends TestCase {
|
|||
unset($managerMethods[$i]);
|
||||
}
|
||||
}
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
||||
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
|
||||
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
||||
$user->expects($this->exactly(2))
|
||||
$user->expects($this->any())
|
||||
->method('isEnabled')
|
||||
->will($this->returnValue(true));
|
||||
$user->expects($this->any())
|
||||
|
@ -160,22 +189,27 @@ class Session extends TestCase {
|
|||
->with('foo', 'bar')
|
||||
->will($this->returnValue($user));
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = $this->getMockBuilder('\OC\User\Session')
|
||||
->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]])
|
||||
->setMethods([
|
||||
'prepareUserLogin'
|
||||
])
|
||||
->getMock();
|
||||
$userSession->expects($this->once())
|
||||
->method('prepareUserLogin');
|
||||
$userSession->login('foo', 'bar');
|
||||
$this->assertEquals($user, $userSession->getUser());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OC\User\LoginException
|
||||
* @expectedExceptionMessage User disabled
|
||||
*/
|
||||
public function testLoginValidPasswordDisabled() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->never())
|
||||
->method('set');
|
||||
$session->expects($this->once())
|
||||
->method('regenerateId');
|
||||
->method('regenerateId');
|
||||
|
||||
$managerMethods = get_class_methods('\OC\User\Manager');
|
||||
//keep following methods intact in order to ensure hooks are
|
||||
|
@ -187,13 +221,12 @@ class Session extends TestCase {
|
|||
unset($managerMethods[$i]);
|
||||
}
|
||||
}
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
||||
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
|
||||
$user = $this->getMock('\OC\User\User', array(), array('foo', $backend));
|
||||
$user->expects($this->once())
|
||||
$user->expects($this->any())
|
||||
->method('isEnabled')
|
||||
->will($this->returnValue(false));
|
||||
$user->expects($this->never())
|
||||
|
@ -204,17 +237,16 @@ class Session extends TestCase {
|
|||
->with('foo', 'bar')
|
||||
->will($this->returnValue($user));
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]);
|
||||
$userSession->login('foo', 'bar');
|
||||
}
|
||||
|
||||
public function testLoginInvalidPassword() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->never())
|
||||
->method('set');
|
||||
$session->expects($this->once())
|
||||
->method('regenerateId');
|
||||
->method('regenerateId');
|
||||
|
||||
$managerMethods = get_class_methods('\OC\User\Manager');
|
||||
//keep following methods intact in order to ensure hooks are
|
||||
|
@ -226,7 +258,6 @@ class Session extends TestCase {
|
|||
unset($managerMethods[$i]);
|
||||
}
|
||||
}
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
||||
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
|
@ -242,32 +273,31 @@ class Session extends TestCase {
|
|||
->with('foo', 'bar')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]);
|
||||
$userSession->login('foo', 'bar');
|
||||
}
|
||||
|
||||
public function testLoginNonExisting() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->never())
|
||||
->method('set');
|
||||
$session->expects($this->once())
|
||||
->method('regenerateId');
|
||||
->method('regenerateId');
|
||||
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMock('\OC\User\Manager');
|
||||
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
|
||||
$manager->expects($this->once())
|
||||
->method('checkPassword')
|
||||
->with('foo', 'bar')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]);
|
||||
$userSession->login('foo', 'bar');
|
||||
}
|
||||
|
||||
public function testRememberLoginValidToken() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->exactly(1))
|
||||
->method('set')
|
||||
|
@ -278,10 +308,9 @@ class Session extends TestCase {
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
},
|
||||
'foo'));
|
||||
}, 'foo'));
|
||||
$session->expects($this->once())
|
||||
->method('regenerateId');
|
||||
->method('regenerateId');
|
||||
|
||||
$managerMethods = get_class_methods('\OC\User\Manager');
|
||||
//keep following methods intact in order to ensure hooks are
|
||||
|
@ -314,13 +343,12 @@ class Session extends TestCase {
|
|||
$token = 'goodToken';
|
||||
\OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time());
|
||||
|
||||
/** @var \OC\User\Session $userSession */
|
||||
$userSession = $this->getMock(
|
||||
'\OC\User\Session',
|
||||
//override, otherwise tests will fail because of setcookie()
|
||||
array('setMagicInCookie'),
|
||||
//there are passed as parameters to the constructor
|
||||
array($manager, $session));
|
||||
array($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]));
|
||||
|
||||
$granted = $userSession->loginWithCookie('foo', $token);
|
||||
|
||||
|
@ -328,12 +356,11 @@ class Session extends TestCase {
|
|||
}
|
||||
|
||||
public function testRememberLoginInvalidToken() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->never())
|
||||
->method('set');
|
||||
$session->expects($this->once())
|
||||
->method('regenerateId');
|
||||
->method('regenerateId');
|
||||
|
||||
$managerMethods = get_class_methods('\OC\User\Manager');
|
||||
//keep following methods intact in order to ensure hooks are
|
||||
|
@ -345,7 +372,6 @@ class Session extends TestCase {
|
|||
unset($managerMethods[$i]);
|
||||
}
|
||||
}
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
||||
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
|
@ -367,19 +393,18 @@ class Session extends TestCase {
|
|||
$token = 'goodToken';
|
||||
\OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time());
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]);
|
||||
$granted = $userSession->loginWithCookie('foo', 'badToken');
|
||||
|
||||
$this->assertSame($granted, false);
|
||||
}
|
||||
|
||||
public function testRememberLoginInvalidUser() {
|
||||
/** @var ISession | \PHPUnit_Framework_MockObject_MockObject $session */
|
||||
$session = $this->getMock('\OC\Session\Memory', array(), array(''));
|
||||
$session->expects($this->never())
|
||||
->method('set');
|
||||
$session->expects($this->once())
|
||||
->method('regenerateId');
|
||||
->method('regenerateId');
|
||||
|
||||
$managerMethods = get_class_methods('\OC\User\Manager');
|
||||
//keep following methods intact in order to ensure hooks are
|
||||
|
@ -391,7 +416,6 @@ class Session extends TestCase {
|
|||
unset($managerMethods[$i]);
|
||||
}
|
||||
}
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMock('\OC\User\Manager', $managerMethods, array());
|
||||
|
||||
$backend = $this->getMock('\Test\Util\User\Dummy');
|
||||
|
@ -412,7 +436,7 @@ class Session extends TestCase {
|
|||
$token = 'goodToken';
|
||||
\OC::$server->getConfig()->setUserValue('foo', 'login_token', $token, time());
|
||||
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = new \OC\User\Session($manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]);
|
||||
$granted = $userSession->loginWithCookie('foo', $token);
|
||||
|
||||
$this->assertSame($granted, false);
|
||||
|
@ -424,7 +448,6 @@ class Session extends TestCase {
|
|||
'bar' => new User('bar', null)
|
||||
);
|
||||
|
||||
/** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $manager */
|
||||
$manager = $this->getMockBuilder('\OC\User\Manager')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
@ -432,12 +455,20 @@ class Session extends TestCase {
|
|||
$manager->expects($this->any())
|
||||
->method('get')
|
||||
->will($this->returnCallback(function ($uid) use ($users) {
|
||||
return $users[$uid];
|
||||
}));
|
||||
return $users[$uid];
|
||||
}));
|
||||
|
||||
$session = new Memory('');
|
||||
$session->set('user_id', 'foo');
|
||||
$userSession = new \OC\User\Session($manager, $session);
|
||||
$userSession = $this->getMockBuilder('\OC\User\Session')
|
||||
->setConstructorArgs([$manager, $session, $this->timeFactory, $this->defaultProvider, [$this->defaultProvider]])
|
||||
->setMethods([
|
||||
'validateSession'
|
||||
])
|
||||
->getMock();
|
||||
$userSession->expects($this->any())
|
||||
->method('validateSession');
|
||||
|
||||
$this->assertEquals($users['foo'], $userSession->getUser());
|
||||
|
||||
$session2 = new Memory('');
|
||||
|
@ -445,4 +476,5 @@ class Session extends TestCase {
|
|||
$userSession->setSession($session2);
|
||||
$this->assertEquals($users['bar'], $userSession->getUser());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
|
||||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
$OC_Version = array(9, 1, 0, 0);
|
||||
$OC_Version = array(9, 1, 0, 1);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '9.1.0 pre alpha';
|
||||
|
|
Loading…
Reference in a new issue