Merge pull request #24189 from owncloud/pluggable-auth

Pluggable auth
This commit is contained in:
Thomas Müller 2016-05-11 13:52:45 +02:00
commit efa545f8f0
33 changed files with 2060 additions and 428 deletions

View file

@ -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();

View file

@ -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();

View 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"

View 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);
}
}

View file

@ -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(

View file

@ -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();

View file

@ -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'));
}
}

View 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,
];
}
}

View file

@ -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

View file

@ -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']) . '">');

View file

@ -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>
<!--

View file

@ -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();

View file

@ -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 {
}

View 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;
}
}

View 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();
}
}

View 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);
}
}

View 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();
}
}
}

View 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);
}

View 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();
}

View file

@ -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('');
$userSession = new \OC\User\Session($manager, $session);
$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, $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));
});

View file

@ -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
*/

View file

@ -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());
}

View file

@ -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);
}
}

View file

@ -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;
}
if ($loginSuccess === true) {
self::$logoutRequired = true;
// initialize the user's filesystem
\OC_Util::setUpFS(\OC_User::getUser());
self::$isLoggedIn = true;
// initialize the user's filesystem
\OC_Util::setUpFS(\OC_User::getUser());
self::$isLoggedIn = 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()
);
return \OC_User::getUser();
}
return \OC_User::getUser();
}
return false;

View file

@ -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
*

View file

@ -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()
]

View file

@ -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));
}
}

View 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);
}
}

View file

@ -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]);
}
}

View 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);
}
}

View 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);
}
}

View file

@ -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());
}
}

View file

@ -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';