Skip to content

Commit

Permalink
chore: Fix more psalm reported issues
Browse files Browse the repository at this point in the history
Mostly missing type annotations and missing template parameters for EventListeners.
Also fixed one typo in `SuccesfullLoginListener` (successful vs "sucessfull").

But there is also one potential issue: `HIBPValidator` was assuming `getBody` is always a string, but it could also be a resource.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
  • Loading branch information
susnux committed Sep 2, 2024
1 parent 031ba33 commit ee8dcc0
Show file tree
Hide file tree
Showing 15 changed files with 63 additions and 45 deletions.
4 changes: 2 additions & 2 deletions lib/Compliance/Expiration.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function entryControl(IUser $user, ?string $password): void {
}
}

protected function isPasswordExpired(IUser $user) {
protected function isPasswordExpired(IUser $user): bool {
$updatedAt = (int)$this->config->getUserValue(
$user->getUID(),
'password_policy',
Expand All @@ -97,7 +97,7 @@ protected function isPasswordExpired(IUser $user) {
return $expiresIn <= time();
}

protected function isLocalUser(IUser $user) {
protected function isLocalUser(IUser $user): bool {
$localBackends = ['Database', 'Guests'];
return in_array($user->getBackendClassName(), $localBackends);
}
Expand Down
6 changes: 5 additions & 1 deletion lib/Compliance/HistoryCompliance.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,17 @@ public function update(IUser $user, string $password): void {
);
}

/**
* @return list<string> List of previously used passwords (hashed)
*/
protected function getHistory(IUser $user): array {
$history = $this->config->getUserValue(
$user->getUID(),
'password_policy',
'passwordHistory',
'[]'
);
/** @var string[]|string */
$history = \json_decode($history, true);
if (!is_array($history)) {
$this->logger->warning(
Expand All @@ -90,6 +94,6 @@ protected function getHistory(IUser $user): array {
}
$history = \array_slice($history, 0, $this->policyConfig->getHistorySize());

return $history;
return \array_values($history);
}
}
13 changes: 7 additions & 6 deletions lib/ComplianceService.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ public function __construct(
) {
}

public function update(IUser $user, string $password) {
public function update(IUser $user, string $password): void {
foreach ($this->getInstance(IUpdatable::class) as $instance) {
$instance->update($user, $password);
}
}

public function audit(IUser $user, string $password) {
public function audit(IUser $user, string $password): void {
foreach ($this->getInstance(IAuditor::class) as $instance) {
$instance->audit($user, $password);
}
Expand All @@ -56,15 +56,14 @@ public function audit(IUser $user, string $password) {
/**
* @throws LoginException
*/
//public function entryControl(IUser $user, string $password, bool $isTokenLogin) {
public function entryControl(string $loginName, ?string $password) {
public function entryControl(string $loginName, ?string $password): void {
$uid = $loginName;
\OCP\Util::emitHook('\OCA\Files_Sharing\API\Server2Server', 'preLoginNameUsedAsUserName', ['uid' => &$uid]);

/** @var IEntryControl $instance */
foreach ($this->getInstance(IEntryControl::class) as $instance) {
try {
$user = $this->userManager->get($uid);
$user = $this->userManager->get((string)$uid);

if ($user === null) {
break;
Expand All @@ -78,7 +77,9 @@ public function entryControl(string $loginName, ?string $password) {
}

/**
* @returns Iterable
* @template T
* @psalm-param class-string<T> $interface
* @return Iterable<T>
*/
protected function getInstance($interface) {
foreach (self::COMPLIANCERS as $compliance) {
Expand Down
4 changes: 2 additions & 2 deletions lib/FailedLoginCompliance.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function __construct(
) {
}

public function onFailedLogin(string $uid) {
public function onFailedLogin(string $uid): void {
$user = $this->userManager->get($uid);

if (!($user instanceof IUser)) {
Expand Down Expand Up @@ -52,7 +52,7 @@ public function onFailedLogin(string $uid) {
$this->setAttempts($uid, $attempts);
}

public function onSucessfullLogin(IUser $user) {
public function onSuccessfulLogin(IUser $user): void {
$this->setAttempts($user->getUID(), 0);
}

Expand Down
46 changes: 15 additions & 31 deletions lib/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,16 @@

class Generator {

/** @var PasswordPolicyConfig */
private $config;
public const PASSWORD_GENERATION_MAX_ROUNDS = 10;

/** @var PasswordValidator */
private $validator;

/** @var ISecureRandom */
private $random;

public function __construct(PasswordPolicyConfig $config,
PasswordValidator $validator,
ISecureRandom $random) {
$this->config = $config;
$this->validator = $validator;
$this->random = $random;
public function __construct(
private PasswordPolicyConfig $config,
private PasswordValidator $validator,
private ISecureRandom $random,
) {
}

/**
* @return string
* @throws HintException
*/
public function generate(): string {
Expand All @@ -41,8 +32,7 @@ public function generate(): string {
$password = '';
$chars = '';

$found = false;
for ($i = 0; $i < 10; $i++) {
for ($i = 0; $i < self::PASSWORD_GENERATION_MAX_ROUNDS; $i++) {
if ($this->config->getEnforceUpperLowerCase()) {
$password .= $this->random->generate(1, ISecureRandom::CHAR_UPPER);
$password .= $this->random->generate(1, ISecureRandom::CHAR_LOWER);
Expand All @@ -67,20 +57,18 @@ public function generate(): string {
}

$password .= $chars = $this->random->generate($length, $chars);

// Shuffle string so the order is random
$password = str_shuffle($password);

if ($password === '') {
// something went wrong
break;
}

try {
$this->validator->validate($password);

if ($password === null || $password === '') {
// something went wrong
break;
}

$found = true;
break;
// Validation succeeded
return $password;
} catch (HintException $e) {
/*
* Invalid so lets go for another round
Expand All @@ -90,10 +78,6 @@ public function generate(): string {
}
}

if ($found === false) {
throw new HintException('Could not generate a valid password');
}

return $password;
throw new HintException('Could not generate a valid password');
}
}
3 changes: 3 additions & 0 deletions lib/Listener/BeforePasswordUpdatedEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\BeforePasswordUpdatedEvent;

/**
* @template-implements IEventListener<BeforePasswordUpdatedEvent>
*/
class BeforePasswordUpdatedEventListener implements IEventListener {
/** @var ComplianceService */
private $complianceUpdater;
Expand Down
3 changes: 3 additions & 0 deletions lib/Listener/BeforeUserLoggedInEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\BeforeUserLoggedInEvent;

/**
* @template-implements IEventListener<BeforeUserLoggedInEvent>
*/
class BeforeUserLoggedInEventListener implements IEventListener {
/** @var ComplianceService */
private $complianceUpdater;
Expand Down
3 changes: 3 additions & 0 deletions lib/Listener/FailedLoginListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;

/**
* @template-implements IEventListener<LoginFailedEvent>
*/
class FailedLoginListener implements IEventListener {
/** @var FailedLoginCompliance */
private $compliance;
Expand Down
3 changes: 3 additions & 0 deletions lib/Listener/GenerateSecurePasswordEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use OCP\EventDispatcher\IEventListener;
use OCP\Security\Events\GenerateSecurePasswordEvent;

/**
* @template-implements IEventListener<GenerateSecurePasswordEvent>
*/
class GenerateSecurePasswordEventListener implements IEventListener {
/** @var Generator */
private $generator;
Expand Down
3 changes: 3 additions & 0 deletions lib/Listener/PasswordUpdatedEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\PasswordUpdatedEvent;

/**
* @template-implements IEventListener<PasswordUpdatedEvent>
*/
class PasswordUpdatedEventListener implements IEventListener {
/** @var ComplianceService */
private $complianceUpdater;
Expand Down
5 changes: 4 additions & 1 deletion lib/Listener/SuccesfullLoginListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use OCP\EventDispatcher\IEventListener;
use OCP\User\Events\UserLoggedInEvent;

/**
* @template-implements IEventListener<UserLoggedInEvent>
*/
class SuccesfullLoginListener implements IEventListener {
/** @var FailedLoginCompliance */
private $compliance;
Expand All @@ -26,6 +29,6 @@ public function handle(Event $event): void {
return;
}

$this->compliance->onSucessfullLogin($event->getUser());
$this->compliance->onSuccessfulLogin($event->getUser());
}
}
3 changes: 3 additions & 0 deletions lib/Listener/ValidatePasswordPolicyEventListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@
use OCP\EventDispatcher\IEventListener;
use OCP\Security\Events\ValidatePasswordPolicyEvent;

/**
* @template-implements IEventListener<ValidatePasswordPolicyEvent>
*/
class ValidatePasswordPolicyEventListener implements IEventListener {
/** @var PasswordValidator */
private $passwordValidator;
Expand Down
2 changes: 1 addition & 1 deletion lib/PasswordPolicyConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public function setEnforceSpecialCharacters(bool $enforceSpecialCharacters): voi
* Do we check against the HaveIBeenPwned passwords
*/
public function getEnforceHaveIBeenPwned(): bool {
$hasInternetConnection = $this->config->getSystemValue('has_internet_connection', true);
$hasInternetConnection = $this->config->getSystemValueBool('has_internet_connection', true);
if (!$hasInternetConnection) {
return false;
}
Expand Down
7 changes: 6 additions & 1 deletion lib/Validator/CommonPasswordsValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,14 @@ public function __construct(PasswordPolicyConfig $config, IL10N $l) {

public function validate(string $password): void {
$enforceNonCommonPassword = $this->config->getEnforceNonCommonPassword();
if (!$enforceNonCommonPassword) {
return;
}

$passwordFile = __DIR__ . '/../../lists/list-'.strlen($password).'.php';
if ($enforceNonCommonPassword && file_exists($passwordFile)) {
if (file_exists($passwordFile)) {
$commonPasswords = require_once $passwordFile;
assert(is_array($commonPasswords));
if (isset($commonPasswords[strtolower($password)])) {
$message = 'Password is among the 1,000,000 most common ones. Please make it unique.';
$message_t = $this->l->t(
Expand Down
3 changes: 3 additions & 0 deletions lib/Validator/HIBPValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public function validate(string $password): void {
}

$result = $response->getBody();
if (is_resource($result)) {
$result = stream_get_contents($result);
}
$result = preg_replace('/^([0-9A-Z]+:0)$/m', '', $result);

if (strpos($result, $needle) !== false) {
Expand Down

0 comments on commit ee8dcc0

Please sign in to comment.