Browse Source

Handle one time passwords

This adds an option to disable storing passwords in the database. This
might be desirable when using single use token as passwords or very
large passwords.

Signed-off-by: Carl Schwan <carl@carlschwan.eu>
tags/v25.0.0beta1
Carl Schwan 1 year ago
parent
commit
cdf3b60555

+ 15
- 0
config/config.sample.php View File

@@ -308,6 +308,21 @@ $CONFIG = [
*/
'auth.webauthn.enabled' => true,

/**
* Whether encrypted password should be stored in the database
*
* The passwords are only decrypted using the login token stored uniquely in the
* clients and allow to connect to external storages, autoconfigure mail account in
* the mail app and periodically check if the password it still valid.
*
* This might be desirable to disable this functionality when using one time
* passwords or when having a password policy enforcing long passwords (> 300
* characters).
*
* By default the passwords are stored encrypted in the database.
*/
'auth.storeCryptedPassword' => true,

/**
* By default the login form is always available. There are cases (SSO) where an
* admin wants to avoid users entering their credentials to the system if the SSO

+ 1
- 1
lib/private/Authentication/Token/PublicKeyTokenProvider.php View File

@@ -368,7 +368,7 @@ class PublicKeyTokenProvider implements IProvider {
$dbToken->setPublicKey($publicKey);
$dbToken->setPrivateKey($this->encrypt($privateKey, $token));

if (!is_null($password)) {
if (!is_null($password) && $this->config->getSystemValueBool('auth.storeCryptedPassword', true)) {
$dbToken->setPassword($this->encryptPassword($password, $publicKey));
}


+ 56
- 4
tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php View File

@@ -25,6 +25,7 @@ namespace Test\Authentication\Token;

use OC\Authentication\Exceptions\ExpiredTokenException;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Authentication\Exceptions\PasswordlessTokenException;
use OC\Authentication\Token\IToken;
use OC\Authentication\Token\PublicKeyToken;
use OC\Authentication\Token\PublicKeyTokenMapper;
@@ -83,6 +84,10 @@ class PublicKeyTokenProviderTest extends TestCase {
$name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;

$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, true],
]);
$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

$this->assertInstanceOf(PublicKeyToken::class, $actual);
@@ -93,6 +98,29 @@ class PublicKeyTokenProviderTest extends TestCase {
$this->assertSame($password, $this->tokenProvider->getPassword($actual, $token));
}

public function testGenerateTokenNoPassword() {
$token = 'token';
$uid = 'user';
$user = 'User';
$password = 'passme';
$name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;
$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, false],
]);
$this->expectException(PasswordlessTokenException::class);

$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

$this->assertInstanceOf(PublicKeyToken::class, $actual);
$this->assertSame($uid, $actual->getUID());
$this->assertSame($user, $actual->getLoginName());
$this->assertSame($name, $actual->getName());
$this->assertSame(IToken::DO_NOT_REMEMBER, $actual->getRemember());
$this->tokenProvider->getPassword($actual, $token);
}

public function testGenerateTokenInvalidName() {
$token = 'token';
$uid = 'user';
@@ -103,6 +131,10 @@ class PublicKeyTokenProviderTest extends TestCase {
. 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12'
. 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;
$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, true],
]);

$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

@@ -157,6 +189,10 @@ class PublicKeyTokenProviderTest extends TestCase {
$password = 'passme';
$name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;
$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, true],
]);

$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

@@ -185,6 +221,10 @@ class PublicKeyTokenProviderTest extends TestCase {
$name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;

$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, true],
]);
$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

$this->tokenProvider->getPassword($actual, 'wrongtoken');
@@ -197,6 +237,10 @@ class PublicKeyTokenProviderTest extends TestCase {
$password = 'passme';
$name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;
$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, true],
]);

$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

@@ -301,7 +345,7 @@ class PublicKeyTokenProviderTest extends TestCase {
$this->tokenProvider->renewSessionToken('oldId', 'newId');
}

public function testRenewSessionTokenWithPassword() {
public function testRenewSessionTokenWithPassword(): void {
$token = 'oldId';
$uid = 'user';
$user = 'User';
@@ -309,6 +353,10 @@ class PublicKeyTokenProviderTest extends TestCase {
$name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;

$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, true],
]);
$oldToken = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

$this->mapper
@@ -319,7 +367,7 @@ class PublicKeyTokenProviderTest extends TestCase {
$this->mapper
->expects($this->once())
->method('insert')
->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name) {
->with($this->callback(function (PublicKeyToken $token) use ($user, $uid, $name): bool {
return $token->getUID() === $uid &&
$token->getLoginName() === $user &&
$token->getName() === $name &&
@@ -331,14 +379,14 @@ class PublicKeyTokenProviderTest extends TestCase {
$this->mapper
->expects($this->once())
->method('delete')
->with($this->callback(function ($token) use ($oldToken) {
->with($this->callback(function ($token) use ($oldToken): bool {
return $token === $oldToken;
}));

$this->tokenProvider->renewSessionToken('oldId', 'newId');
}

public function testGetToken() {
public function testGetToken(): void {
$token = new PublicKeyToken();

$this->config->method('getSystemValue')
@@ -441,6 +489,10 @@ class PublicKeyTokenProviderTest extends TestCase {
$name = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.12) Gecko/20101026 Firefox/3.6.12';
$type = IToken::PERMANENT_TOKEN;

$this->config->method('getSystemValueBool')
->willReturnMap([
['auth.storeCryptedPassword', true, true],
]);
$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);

$new = $this->tokenProvider->rotate($actual, 'oldtoken', 'newtoken');

Loading…
Cancel
Save