diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bf081e4c42afe8cefd44feff48d105f28816c803..f8126f62264bba005fe788c1f7b614608278c907 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,7 +32,7 @@ phpcs: # Uncomment the lines below if you want to override any of the variables. The following is just an example. ################ variables: - OPT_IN_TEST_PREVIOUS_MAJOR: '1' + OPT_IN_TEST_NEXT_MAJOR: '1' OPT_IN_TEST_NEXT_MINOR: '1' _CSPELL_WORDS: 'Unblockmanager,batigolix,Doesborg,fabianderijk,Rijk' # SKIP_ESLINT: '1' diff --git a/composer.json b/composer.json index 7b61bd6f7df1f4aec5518d41ba34e6b07c0ce620..ef8e0701642b198158b1f9e24f1c5cf6565e1508 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "type": "drupal-module", "license": "GPL-2.0-or-later", "require": { - "drupal/core": "^9 || ^10" + "drupal/core": "^10.2 || ^11" }, "extra": { "drush": { diff --git a/flood_control.info.yml b/flood_control.info.yml index a60e4eb4733722008e91340c02c721e6b56070b7..ede7f0c48e9b283e2a03335e5be971b816f1ecb8 100644 --- a/flood_control.info.yml +++ b/flood_control.info.yml @@ -3,4 +3,4 @@ description: "Allows configuring hidden flood control options and unblocking IP configure: flood_control.settings package: 'Administration' type: module -core_version_requirement: ^9 || ^10 +core_version_requirement: ^10.2 || ^11 diff --git a/flood_control.install b/flood_control.install index 3320f9120b80711a25e189eac02d09708771a79f..1f05d88dba450a6df79736168d8f534abc736677 100644 --- a/flood_control.install +++ b/flood_control.install @@ -6,12 +6,14 @@ */ use Drupal\user\Entity\Role; +use Drupal\user\RoleInterface; /** * Assign newly added permissions to the appropriate roles. */ function flood_control_update_9201() { - $roles = user_role_names(FALSE, 'access flood unblock'); + $names = array_filter(Role::loadMultiple(), fn(RoleInterface $role) => $role->hasPermission('access flood unblock')); + $roles = array_map(fn(RoleInterface $role) => $role->label(), $names); foreach ($roles as $roleKey => $roleName) { user_role_grant_permissions($roleKey, [ 'administer flood unblock', diff --git a/src/FloodUnblockManager.php b/src/FloodUnblockManager.php deleted file mode 100755 index 8ade1f791ab2152e8c16d57cc468641938c4d758..0000000000000000000000000000000000000000 --- a/src/FloodUnblockManager.php +++ /dev/null @@ -1,235 +0,0 @@ -<?php - -namespace Drupal\flood_control; - -use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Database\Connection; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Flood\FloodInterface; -use Drupal\Core\Logger\LoggerChannelFactoryInterface; -use Drupal\Core\Messenger\MessengerInterface; -use Drupal\Core\StringTranslation\StringTranslationTrait; - -/** - * Provides Flood Unblock actions. - */ -class FloodUnblockManager implements FloodUnblockManagerInterface { - - use StringTranslationTrait; - - /** - * The Database Connection. - * - * @var \Drupal\Core\Database\Connection - */ - protected $database; - - /** - * The Entity Type Manager Interface. - * - * @var \Drupal\Core\Entity\EntityTypeManagerInterface - */ - protected $entityTypeManager; - - /** - * The Flood Interface. - * - * @var \Drupal\Core\Flood\FloodInterface - */ - protected $flood; - - /** - * The Immutable Config. - * - * @var \Drupal\Core\Config\ImmutableConfig - */ - protected $config; - - /** - * The messenger. - * - * @var \Drupal\Core\Messenger\MessengerInterface - */ - protected $messenger; - - /** - * The logger factory. - * - * @var \Drupal\Core\Logger\LoggerChannelFactory - */ - protected $loggerFactory; - - /** - * FloodUnblockAdminForm constructor. - * - * @param \Drupal\Core\Database\Connection $database - * The database connection. - * @param \Drupal\Core\Flood\FloodInterface $flood - * The flood interface. - * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory - * The Config Factory Interface. - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager - * The Entity Type Manager Interface. - * @param \Drupal\Core\Messenger\MessengerInterface $messenger - * The Messenger Interface. - * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory - * The logger factory. - */ - public function __construct(Connection $database, FloodInterface $flood, ConfigFactoryInterface $configFactory, EntityTypeManagerInterface $entityTypeManager, MessengerInterface $messenger, LoggerChannelFactoryInterface $logger_factory) { - $this->database = $database; - $this->flood = $flood; - $this->entityTypeManager = $entityTypeManager; - $this->config = $configFactory->get('user.flood'); - $this->messenger = $messenger; - $this->loggerFactory = $logger_factory->get('flood_control'); - } - - /** - * {@inheritdoc} - */ - public function fetchIdentifiers($results) { - $identifiers = []; - - foreach ($results as $result) { - - // Sets ip as default value and adds to identifiers array. - $identifiers[$result] = $result; - - // Sets location as value and adds to identifiers array. - if (function_exists('smart_ip_get_location')) { - $location = smart_ip_get_location($result); - $location_string = sprintf(" (%s %s %s)", $location['city'], $location['region'], $location['country_code']); - $identifiers[$result] = "$location_string ($result)"; - } - - // Sets link to user as value and adds to identifiers array. - $parts = explode('-', $result); - if (isset($parts[0]) && isset($parts[1])) { - $uid = $parts[0]; - - /** @var \Drupal\user\Entity\User $user */ - $user = $this->entityTypeManager->getStorage('user') - ->load($uid); - if (isset($user)) { - $user_link = $user->toLink($user->getAccountName()); - } - else { - $user_link = $this->t('Deleted user: @user', ['@user' => $uid]); - } - $identifiers[$result] = $user_link; - } - - } - return $identifiers; - } - - /** - * {@inheritdoc} - */ - public function floodUnblockClearEvent($fid) { - $txn = $this->database->startTransaction('flood_unblock_clear'); - try { - $query = $this->database->delete('flood') - ->condition('fid', $fid); - $success = $query->execute(); - if ($success) { - $this->messenger->addMessage($this->t('Flood entries cleared.'), 'status', FALSE); - } - } - catch (\Exception $e) { - // Something went wrong somewhere, so roll back now. - $txn->rollback(); - - // Log the exception to drupal. - $this->loggerFactory->error($e); - $this->messenger->addMessage($this->t('Error: @error', ['@error' => (string) $e]), 'error'); - } - } - - /** - * {@inheritdoc} - */ - public function getEvents() { - return [ - 'user.failed_login_ip' => [ - 'type' => 'ip', - 'label' => $this->t('User failed login IP'), - ], - 'user.failed_login_user' => [ - 'type' => 'user', - 'label' => $this->t('User failed login user'), - ], - 'user.http_login' => [ - 'type' => 'user', - 'label' => $this->t('User failed http login'), - ], - 'user.password_request_ip' => [ - 'type' => 'user', - 'label' => $this->t('User failed password request IP'), - ], - 'user.password_request_user' => [ - 'type' => 'user', - 'label' => $this->t('User failed password request user'), - ], - ]; - } - - /** - * {@inheritdoc} - */ - public function getEventLabel($event) { - $event_mapping = $this->getEvents(); - if (array_key_exists($event, $event_mapping)) { - return $event_mapping[$event]['label']; - } - - return ucfirst(str_replace(['.', '_'], ' ', $event)); - } - - /** - * {@inheritdoc} - */ - public function getEventType($event) { - $event_mapping = $this->getEvents(); - if (array_key_exists($event, $event_mapping)) { - return $event_mapping[$event]['type']; - } - - $parts = explode('.', $event); - return $parts[0]; - } - - /** - * {@inheritdoc} - */ - public function isBlocked($identifier, $event) { - $type = $this->getEventType($event); - switch ($type) { - case 'user': - return !$this->flood->isAllowed($event, $this->config->get('user_limit'), $this->config->get('user_window'), $identifier); - - case 'ip': - return !$this->flood->isAllowed($event, $this->config->get('ip_limit'), $this->config->get('ip_window'), $identifier); - } - return FALSE; - } - - /** - * {@inheritdoc} - */ - public function getEventIds($event, $identifier = NULL) { - $event_ids = []; - $query = $this->database->select('flood', 'f'); - $query->condition('event', $event); - if ($identifier) { - $query->condition('f.identifier', $identifier, 'LIKE'); - } - $query->fields('f', ['fid']); - $result = $query->execute(); - foreach ($result as $record) { - $event_ids[] = $record->fid; - } - return $event_ids; - } - -} diff --git a/src/FloodUnblockManagerBase.php b/src/FloodUnblockManagerBase.php index b3d3e9056d90c80ebb90eef04c1f3629cfc05910..8ca4b495d4f98ac79912aceae89d40b139292ba6 100644 --- a/src/FloodUnblockManagerBase.php +++ b/src/FloodUnblockManagerBase.php @@ -25,6 +25,13 @@ abstract class FloodUnblockManagerBase implements FloodUnblockManagerInterface { */ protected $config; + /** + * The Entity Type Manager Interface. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + /** * {@inheritdoc} */ diff --git a/src/FloodUnblockManagerDatabase.php b/src/FloodUnblockManagerDatabase.php index 869769ac1b502e850c422fd7bd0ac3258610b3cc..7a08f0f8a2921ac28c453e55590261105042c989 100755 --- a/src/FloodUnblockManagerDatabase.php +++ b/src/FloodUnblockManagerDatabase.php @@ -21,6 +21,20 @@ class FloodUnblockManagerDatabase extends FloodUnblockManagerBase { */ protected $database; + /** + * The Flood Service. + * + * @var \Drupal\Core\Flood\FloodInterface + */ + protected $flood; + + /** + * The config factory. + * + * @var \Drupal\Core\Config\ConfigFactoryInterface + */ + protected $config; + /** * The Entity Type Manager Interface. * diff --git a/src/Form/FloodControlSettingsForm.php b/src/Form/FloodControlSettingsForm.php index 39fb8b76f5706ae665dcc79256ec14f967c265e7..573c403bbd9bc53afaeb20a1d23b23f533b5e6b8 100644 --- a/src/Form/FloodControlSettingsForm.php +++ b/src/Form/FloodControlSettingsForm.php @@ -3,6 +3,7 @@ namespace Drupal\flood_control\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Form\ConfigFormBase; @@ -32,8 +33,13 @@ class FloodControlSettingsForm extends ConfigFormBase { /** * {@inheritdoc} */ - public function __construct(ConfigFactoryInterface $config_factory, DateFormatterInterface $dateFormatter, ModuleHandlerInterface $module_handler) { - parent::__construct($config_factory); + public function __construct( + ConfigFactoryInterface $configFactory, + TypedConfigManagerInterface $typedConfigManager, + DateFormatterInterface $dateFormatter, + ModuleHandlerInterface $module_handler, + ) { + parent::__construct($configFactory, $typedConfigManager); $this->dateFormatter = $dateFormatter; $this->moduleHandler = $module_handler; } @@ -44,8 +50,9 @@ class FloodControlSettingsForm extends ConfigFormBase { public static function create(ContainerInterface $container) { return new static( $container->get('config.factory'), + $container->get('config.typed'), $container->get('date.formatter'), - $container->get('module_handler') + $container->get('module_handler'), ); } diff --git a/tests/src/Functional/FloodControlUiPageTest.php b/tests/src/Functional/FloodControlUiPageTest.php index 66825eee441d091b729da50b4745f0010c97e77f..6c0447af1c026de8555984e1d10dcb0374d71179 100644 --- a/tests/src/Functional/FloodControlUiPageTest.php +++ b/tests/src/Functional/FloodControlUiPageTest.php @@ -4,6 +4,8 @@ namespace Drupal\Tests\flood_control\Functional; use Drupal\Tests\BrowserTestBase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; /** * Tests that the Flood control UI pages are reachable. @@ -55,7 +57,7 @@ class FloodControlUiPageTest extends BrowserTestBase { /** * Create required user and other objects in order to run tests. */ - public function setUp(): void { + protected function setUp(): void { parent::setUp(); $this->adminUser = $this->drupalCreateUser([]); @@ -69,6 +71,7 @@ class FloodControlUiPageTest extends BrowserTestBase { // Flood backends need a request object. Create a dummy one and insert it // to the container. $request = Request::createFromGlobals(); + $request->setSession(new Session(new MockArraySessionStorage())); $this->container->get('request_stack')->push($request); // The flood table is opportunistically created during first use. In these