Skip to content

Commit

Permalink
Merge branch 'hotfix/v2.1.37'
Browse files Browse the repository at this point in the history
  • Loading branch information
ambroisemaupate committed Aug 3, 2023
2 parents d3149f1 + be2e19a commit c423435
Show file tree
Hide file tree
Showing 24 changed files with 542 additions and 144 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## [v2.1.37](https://github.com/roadiz/core-bundle-dev-app/compare/v2.1.36...v2.1.37) (2023-08-03)


### Features

* New Doctrine migration
* **Security:** Added UserChecker to check users enabled, expired, locked or credentialExpired. Removed useless User' boolean expired and credentialsExpired fields. ([42d4d11](https://github.com/roadiz/core-bundle-dev-app/commit/42d4d1133916ea1101872665cd3c13d2ea18175f))
- Make sure to register Roadiz `UserChecker` in your `security.yaml` file: https://symfony.com/doc/current/security/user_checkers.html#enabling-the-custom-user-checker

```yaml
# config/packages/security.yaml

# ...
security:
firewalls:
main:
pattern: ^/
user_checker: RZ\Roadiz\CoreBundle\Security\UserChecker
```
## [v2.1.36](https://github.com/roadiz/core-bundle-dev-app/compare/v2.1.35...v2.1.36) (2023-08-03)
Expand Down
2 changes: 2 additions & 0 deletions config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ security:
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
jwt: ~
user_checker: RZ\Roadiz\CoreBundle\Security\UserChecker
# disables session creation for assets and healthcheck controllers
assets:
pattern: ^/assets
Expand All @@ -48,6 +49,7 @@ security:
main:
lazy: true
provider: all_users
user_checker: RZ\Roadiz\CoreBundle\Security\UserChecker

# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
Expand Down
3 changes: 0 additions & 3 deletions lib/OpenId/src/Authentication/OpenIdAuthenticator.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ final class OpenIdAuthenticator extends AbstractAuthenticator
private string $targetPathParameter;
private array $defaultRoles;
private bool $forceSsl;
private UserProviderInterface $accountProvider;
private bool $requiresLocalUsers;

public function __construct(
Expand All @@ -59,7 +58,6 @@ public function __construct(
JwtRoleStrategy $roleStrategy,
OpenIdJwtConfigurationFactory $jwtConfigurationFactory,
UrlGeneratorInterface $urlGenerator,
UserProviderInterface $accountProvider,
string $returnPath,
string $defaultRoute,
?string $oauthClientId,
Expand Down Expand Up @@ -87,7 +85,6 @@ public function __construct(
$this->urlGenerator = $urlGenerator;
$this->jwtConfigurationFactory = $jwtConfigurationFactory;
$this->forceSsl = $forceSsl;
$this->accountProvider = $accountProvider;
$this->requiresLocalUsers = $requiresLocalUsers;
}

Expand Down
2 changes: 2 additions & 0 deletions lib/RoadizCoreBundle/config/packages/security.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ security:
pattern: ^/api
stateless: true
provider: all_users
user_checker: RZ\Roadiz\CoreBundle\Security\UserChecker
login_throttling:
max_attempts: 3
json_login:
Expand All @@ -46,6 +47,7 @@ security:
main:
lazy: true
provider: all_users
user_checker: RZ\Roadiz\CoreBundle\Security\UserChecker

# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
Expand Down
2 changes: 1 addition & 1 deletion lib/RoadizCoreBundle/config/services.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
parameters:
roadiz_core.cms_version: '2.1.36'
roadiz_core.cms_version: '2.1.37'
roadiz_core.cms_version_prefix: 'main'
env(APP_NAMESPACE): "roadiz"
env(APP_VERSION): "0.1.0"
Expand Down
57 changes: 57 additions & 0 deletions lib/RoadizCoreBundle/migrations/Version20230803131631.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace RZ\Roadiz\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20230803131631 extends AbstractMigration
{
public function getDescription(): string
{
return 'Drop useless expired and credentials_expired fields from users table.';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX IDX_1483A5E9194FED4B ON users');
$this->addSql('ALTER TABLE users DROP expired, DROP credentials_expired');
$this->addSql('CREATE INDEX idx_users_username ON users (username)');
$this->addSql('CREATE INDEX idx_users_email ON users (email)');
$this->addSql('CREATE INDEX idx_users_credentials_expires_at ON users (credentials_expires_at)');
$this->addSql('CREATE INDEX idx_users_password_requested_at ON users (password_requested_at)');
$this->addSql('CREATE INDEX idx_users_last_login ON users (last_login)');
$this->addSql('CREATE INDEX idx_users_locked ON users (locked)');
$this->addSql('CREATE INDEX IDX_1483A5E98B8E8428 ON users (created_at)');
$this->addSql('CREATE INDEX IDX_1483A5E943625D9F ON users (updated_at)');
$this->addSql('ALTER TABLE users RENAME INDEX idx_1483a5e950f9bb84 TO idx_users_enabled');
$this->addSql('ALTER TABLE users RENAME INDEX idx_1483a5e9f9d83e2 TO idx_users_expires_at');
$this->addSql('ALTER TABLE users RENAME INDEX idx_1483a5e94180c698 TO idx_users_locale');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('DROP INDEX idx_users_username ON users');
$this->addSql('DROP INDEX idx_users_email ON users');
$this->addSql('DROP INDEX idx_users_credentials_expires_at ON users');
$this->addSql('DROP INDEX idx_users_password_requested_at ON users');
$this->addSql('DROP INDEX idx_users_last_login ON users');
$this->addSql('DROP INDEX idx_users_locked ON users');
$this->addSql('DROP INDEX IDX_1483A5E98B8E8428 ON users');
$this->addSql('DROP INDEX IDX_1483A5E943625D9F ON users');
$this->addSql('ALTER TABLE users ADD expired TINYINT(1) DEFAULT 0 NOT NULL, ADD credentials_expired TINYINT(1) DEFAULT 0 NOT NULL');
$this->addSql('CREATE INDEX IDX_1483A5E9194FED4B ON users (expired)');
$this->addSql('ALTER TABLE users RENAME INDEX idx_users_locale TO IDX_1483A5E94180C698');
$this->addSql('ALTER TABLE users RENAME INDEX idx_users_enabled TO IDX_1483A5E950F9BB84');
$this->addSql('ALTER TABLE users RENAME INDEX idx_users_expires_at TO IDX_1483A5E9F9D83E2');
}

public function isTransactional(): bool
{
return false;
}
}
60 changes: 38 additions & 22 deletions lib/RoadizCoreBundle/src/Console/UsersCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use RZ\Roadiz\CoreBundle\Entity\Role;
use RZ\Roadiz\CoreBundle\Entity\User;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
Expand Down Expand Up @@ -40,6 +41,19 @@ protected function configure(): void
);
}

protected function getUserTableRow(User $user): array
{
return [
'Id' => $user->getId(),
'Username' => $user->getUsername(),
'Email' => $user->getEmail(),
'Disabled' => (!$user->isEnabled() ? 'X' : ''),
'Expired' => (!$user->isAccountNonExpired() ? 'X' : ''),
'Locked' => (!$user->isAccountNonLocked() ? 'X' : ''),
'Groups' => implode(' ', $user->getGroupNames()),
];
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
Expand All @@ -54,18 +68,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if ($user === null) {
$io->error('User “' . $name . '” does not exist… use users:create to add a new user.');
} else {
$tableContent = [[
$user->getId(),
$user->getUsername(),
$user->getEmail(),
(!$user->isEnabled() ? 'X' : ''),
($user->getExpired() ? 'X' : ''),
(!$user->isAccountNonLocked() ? 'X' : ''),
implode(' ', $user->getGroupNames()),
implode(' ', $user->getRoles()),
]];
$tableContent = [$this->getUserTableRow($user)];
$io->table(
['Id', 'Username', 'Email', 'Disabled', 'Expired', 'Locked', 'Groups', 'Roles'],
array_keys($tableContent[0]),
$tableContent
);
}
Expand All @@ -77,20 +82,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if (count($users) > 0) {
$tableContent = [];
foreach ($users as $user) {
$tableContent[] = [
$user->getId(),
$user->getUsername(),
$user->getEmail(),
(!$user->isEnabled() ? 'X' : ''),
($user->getExpired() ? 'X' : ''),
(!$user->isAccountNonLocked() ? 'X' : ''),
implode(' ', $user->getGroupNames()),
implode(' ', $user->getRoles()),
];
$tableContent[] = $this->getUserTableRow($user);
}

$io->table(
['Id', 'Username', 'Email', 'Disabled', 'Expired', 'Locked', 'Groups', 'Roles'],
array_keys($tableContent[0]),
$tableContent
);
} else {
Expand All @@ -100,6 +96,26 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 0;
}

protected function getUserForInput(InputInterface $input): User
{
$name = $input->getArgument('username');

if (!\is_string($name) || empty($name)) {
throw new InvalidArgumentException('Username argument is required.');
}

/** @var User|null $user */
$user = $this->managerRegistry
->getRepository(User::class)
->findOneBy(['username' => $name]);

if (!($user instanceof User)) {
throw new InvalidArgumentException('User “' . $name . '” does not exist.');
}

return $user;
}

/**
* Get role by name, and create it if it does not exist.
*
Expand Down
59 changes: 59 additions & 0 deletions lib/RoadizCoreBundle/src/Console/UsersExpireCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

declare(strict_types=1);

namespace RZ\Roadiz\CoreBundle\Console;

use RZ\Roadiz\CoreBundle\Entity\User;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;

final class UsersExpireCommand extends UsersCommand
{
protected function configure(): void
{
$this->setName('users:expire')
->setDescription('Set a user account expiration date')
->addArgument(
'username',
InputArgument::REQUIRED,
'Username'
)
->addArgument(
'expiry',
InputArgument::OPTIONAL,
'Expiration date and time (Y-m-d H:i:s)'
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$name = $input->getArgument('username');
$user = $this->getUserForInput($input);
$expirationDate = new \DateTime($input->getArgument('expiry') ?? 'now');

$question = sprintf(
'<question>Do you really want to set user “%s” expiration date on %s?</question>',
$user->getUsername(),
$expirationDate->format('c')
);
$confirmation = new ConfirmationQuestion($question, false);
if (
!$input->isInteractive() || $io->askQuestion(
$confirmation
)
) {
$user->setExpiresAt($expirationDate);
$this->managerRegistry->getManagerForClass(User::class)->flush();
$io->success('User “' . $name . '” expiration date was set on ' . $expirationDate->format('c') . '.');
return 0;
} else {
$io->warning('User “' . $name . '” was not updated.');
return 1;
}
}
}
51 changes: 51 additions & 0 deletions lib/RoadizCoreBundle/src/Console/UsersLockCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types=1);

namespace RZ\Roadiz\CoreBundle\Console;

use RZ\Roadiz\CoreBundle\Entity\User;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Style\SymfonyStyle;

final class UsersLockCommand extends UsersCommand
{
protected function configure(): void
{
$this->setName('users:lock')
->setDescription('Lock a user account')
->addArgument(
'username',
InputArgument::REQUIRED,
'Username'
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$name = $input->getArgument('username');
$user = $this->getUserForInput($input);

$confirmation = new ConfirmationQuestion(
'<question>Do you really want to lock user “' . $user->getUsername() . '”?</question>',
false
);
if (
!$input->isInteractive() || $io->askQuestion(
$confirmation
)
) {
$user->setLocked(true);
$this->managerRegistry->getManagerForClass(User::class)->flush();
$io->success('User “' . $name . '” locked.');
return 0;
} else {
$io->warning('User “' . $name . '” was not locked.');
return 1;
}
}
}
Loading

0 comments on commit c423435

Please sign in to comment.