Browse Source

Merge pull request #33110 from nextcloud/feat/handle-onetime-password-large

Handle one time password and very large passwords
pull/33180/head
blizzz 4 years ago
committed by GitHub
parent
commit
74ebb72622
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      apps/settings/lib/Controller/ChangePasswordController.php
  2. 1
      apps/settings/src/components/UserList/UserRow.vue
  3. 1
      apps/settings/templates/settings/personal/security/password.php
  4. 15
      config/config.sample.php
  5. 7
      lib/private/Authentication/Token/PublicKeyTokenProvider.php
  6. 79
      tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php

11
apps/settings/lib/Controller/ChangePasswordController.php

@ -107,7 +107,7 @@ class ChangePasswordController extends Controller {
}
try {
if ($newpassword === null || $user->setPassword($newpassword) === false) {
if ($newpassword === null || strlen($newpassword) > 469 || $user->setPassword($newpassword) === false) {
return new JSONResponse([
'status' => 'error',
'data' => [
@ -158,6 +158,15 @@ class ChangePasswordController extends Controller {
]);
}
if (strlen($password) > 469) {
return new JSONResponse([
'status' => 'error',
'data' => [
'message' => $this->l->t('Unable to change password. Password too long.'),
],
]);
}
$currentUser = $this->userSession->getUser();
$targetUser = $this->userManager->get($username);
if ($currentUser === null || $targetUser === null ||

1
apps/settings/src/components/UserList/UserRow.vue

@ -107,6 +107,7 @@
ref="password"
:disabled="loading.password || loading.all"
:minlength="minPasswordLength"
maxlength="469"
:placeholder="t('settings', 'Add new password')"
autocapitalize="off"
autocomplete="new-password"

1
apps/settings/templates/settings/personal/security/password.php

@ -46,6 +46,7 @@ if ($_['passwordChangeSupported']) {
<div class="personal-show-container">
<label for="pass2" class="hidden-visually"><?php p($l->t('New password'));?>: </label>
<input type="password" id="pass2" name="newpassword"
maxlength="469"
placeholder="<?php p($l->t('New password')); ?>"
data-typetoggle="#personal-show"
autocomplete="new-password" autocapitalize="none" autocorrect="off" />

15
config/config.sample.php

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

7
lib/private/Authentication/Token/PublicKeyTokenProvider.php

@ -346,7 +346,7 @@ class PublicKeyTokenProvider implements IProvider {
$config = array_merge([
'digest_alg' => 'sha512',
'private_key_bits' => 2048,
'private_key_bits' => $password !== null && strlen($password) > 250 ? 4096 : 2048,
], $this->config->getSystemValue('openssl', []));
// Generate new key
@ -368,7 +368,10 @@ 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)) {
if (strlen($password) > 469) {
throw new \RuntimeException('Trying to save a password with more than 469 characters is not supported. If you want to use big passwords, disable the auth.storeCryptedPassword option in config.php');
}
$dbToken->setPassword($this->encryptPassword($password, $publicKey));
}

79
tests/lib/Authentication/Token/PublicKeyTokenProviderTest.php

@ -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,48 @@ 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 testGenerateTokenLongPassword() {
$token = 'token';
$uid = 'user';
$user = 'User';
$password = '';
for ($i = 0; $i < 500; $i++) {
$password .= 'e';
}
$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],
]);
$this->expectException(\RuntimeException::class);
$actual = $this->tokenProvider->generateToken($token, $uid, $user, $password, $name, $type, IToken::DO_NOT_REMEMBER);
}
public function testGenerateTokenInvalidName() {
$token = 'token';
$uid = 'user';
@ -103,6 +150,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 +208,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 +240,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 +256,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 +364,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 +372,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 +386,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 +398,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 +508,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