diff --git a/README.md b/README.md index f7ce3b0ca..d1b1a07f9 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This repository holds configuration for the Hel.fi platform. - [Development](documentation/development.md) - [Translations in Helfi Platform](documentation/translations.md) - [Update instructions (2.x to 3.x)](documentation/update.md) +- [Two-factor authentication/TFA/MFA](/modules/helfi_tfa/README.md) ## Contact diff --git a/composer.json b/composer.json index 259860c7c..c69d59e33 100644 --- a/composer.json +++ b/composer.json @@ -6,8 +6,8 @@ "minimum-stability": "dev", "require": { "ext-curl": "*", - "drupal/core": "^10.1.5", - "drupal/core-composer-scaffold": "^10.0", + "drupal/core": "^10", + "drupal/core-composer-scaffold": "^10", "drupal/admin_toolbar": "^3.0", "drupal/allowed_formats": "^2.0", "drupal/ckeditor": "^1.0", @@ -46,6 +46,7 @@ "drupal/paragraphs_asymmetric_translation_widgets": "^1.0", "drupal/pathauto": "^1.8", "drupal/publication_date": "^2.0@beta", + "drupal/real_aes": "^2.6", "drupal/rdf": "^2.0", "drupal/readonly_field_widget": "^1.0", "drupal/redirect": "^1.6", @@ -57,6 +58,7 @@ "drupal/siteimprove": "^2.0", "drupal/social_media": "^2.0", "drupal/stomp": "^2.0", + "drupal/tfa": "^1.7", "drupal/token": "^1.9", "drupal/translatable_menu_link_uri": "^2.0", "drupal/view_unpublished": "^1.0", @@ -71,7 +73,6 @@ "drupal/ctools": "<3.11 || ^4.0.1", "drupal/gin_toolbar": ">1.0.0-rc6", "drupal/helfi_media_map": "*", - "drupal/simple_sitemap": ">4.1.7", "drush/drush": "<12" }, "extra": { diff --git a/modules/helfi_tfa/README.md b/modules/helfi_tfa/README.md new file mode 100644 index 000000000..4d01f359c --- /dev/null +++ b/modules/helfi_tfa/README.md @@ -0,0 +1,42 @@ +# Hel.fi: TFA + +Provides default configuration for TFA module. + +## Installation + +1. Generate a random 256-bit key: `dd if=/dev/urandom bs=32 count=1 | base64 -i -` +2. Add a new secret called `TFA-ENCRYPTION-KEY` to Azure KeyVault and copy the key as value +3. Enable `helfi_tfa` module + +## Reset TFA settings for other users + +At the moment, users logging-in using Tunnistamo cannot reset/disable TFA for other users due to password requirements. + +You can use Drush to reset TFA settings until this is fixed: + +```bash +drush tfa:reset-user --uid xyz +``` + +## Exclude roles + +Go to Configuration -> TFA Settings (`/admin/config/people/tfa`) and uncheck the role from `Roles required to set up TFA` and save the form. + +Alternatively, you can override the roles setting in `all.settings.php`: +```php +$config['tfa.settings']['required_roles'] = [ + 'content_producer' => 'content_producer', + 'admin' => 'admin', + 'read_only' => '0', +]; +``` +Setting the value to `0` means the role does not require TFA. + +## Testing on local + +Modify `local.settings.php` file and add: + +```php +$config['key.key.tfa']['key_provider_settings']['key_value'] = 'your-base64-encoded-random-256-bit-key'; +$config['key.key.tfa']['key_provider_settings']['base64_encoded'] = TRUE; +``` diff --git a/modules/helfi_tfa/config/install/encrypt.profile.real_aes.yml b/modules/helfi_tfa/config/install/encrypt.profile.real_aes.yml new file mode 100644 index 000000000..e857d3c74 --- /dev/null +++ b/modules/helfi_tfa/config/install/encrypt.profile.real_aes.yml @@ -0,0 +1,13 @@ +uuid: 90d7b880-aa02-4cff-aeb9-69e03db7a21b +langcode: en +status: true +dependencies: + config: + - key.key.tfa + module: + - real_aes +id: real_aes +label: 'Real AES' +encryption_method: real_aes +encryption_method_configuration: { } +encryption_key: tfa diff --git a/modules/helfi_tfa/config/install/key.key.tfa.yml b/modules/helfi_tfa/config/install/key.key.tfa.yml new file mode 100644 index 000000000..88d86866b --- /dev/null +++ b/modules/helfi_tfa/config/install/key.key.tfa.yml @@ -0,0 +1,17 @@ +uuid: 05f354f6-4d19-4cb0-9d95-0d16a1573e58 +langcode: en +status: true +dependencies: { } +id: tfa +label: TFA +description: '' +key_type: encryption +key_type_settings: + key_size: 256 +key_provider: config +key_provider_settings: + key_value: thisvaluewillbeoverridden1234567 + base64_encoded: true +key_input: text_field +key_input_settings: + base64_encoded: false diff --git a/modules/helfi_tfa/config/rewrite/tfa.settings.yml b/modules/helfi_tfa/config/rewrite/tfa.settings.yml new file mode 100644 index 000000000..285f63cd8 --- /dev/null +++ b/modules/helfi_tfa/config/rewrite/tfa.settings.yml @@ -0,0 +1,40 @@ +langcode: en +enabled: true +send_plugins: { } +login_plugins: { } +login_plugin_settings: + tfa_trusted_browser: + cookie_allow_subdomains: true + cookie_expiration: 30 + cookie_name: tfa-trusted-browser +allowed_validation_plugins: + tfa_totp: tfa_totp +default_validation_plugin: tfa_totp +validation_plugin_settings: + tfa_recovery_code: + recovery_codes_amount: 10 + tfa_hotp: + counter_window: 10 + site_name_prefix: 1 + name_prefix: TFA + issuer: Drupal + tfa_totp: + time_skew: 2 + site_name_prefix: 1 + name_prefix: TFA + issuer: Hel.fi +validation_skip: 3 +users_without_tfa_redirect: false +reset_pass_skip_enabled: true +encryption: real_aes +tfa_flood_uid_only: 1 +tfa_flood_window: 300 +tfa_flood_threshold: 6 +help_text: 'Contact support to reset your access' +mail: + tfa_enabled_configuration: + subject: 'Your [site:name] account now has two-factor authentication' + body: "[user:display-name],\r\n\r\nThanks for configuring two-factor authentication on your [site:name] account!\r\n\r\nThis additional level of security will help to ensure that only you are able to log in to your account.\r\n\r\nIf you ever lose the device you configured, you should act quickly to delete its association with this account.\r\n\r\n--\r\n[site:name] team" + tfa_disabled_configuration: + subject: 'Your [site:name] account no longer has two-factor authentication' + body: "[user:display-name],\r\n\r\nTwo-factor authentication has been disabled on your [site:name] account.\r\n\r\nIf you did not take this action, please contact a site administrator immediately.\r\n\r\n--\r\n[site:name] team" diff --git a/modules/helfi_tfa/helfi_tfa.info.yml b/modules/helfi_tfa/helfi_tfa.info.yml new file mode 100644 index 000000000..8e5f443d7 --- /dev/null +++ b/modules/helfi_tfa/helfi_tfa.info.yml @@ -0,0 +1,9 @@ +name: Hel.fi TFA +type: module +description: 'Integrates Hel.fi user roles module with TFA' +package: Custom +core_version_requirement: ^10 || ^11 +dependencies: + - helfi_user_roles:helfi_user_roles + - real_aes:real_aes + - tfa:tfa diff --git a/modules/helfi_tfa/helfi_tfa.install b/modules/helfi_tfa/helfi_tfa.install new file mode 100644 index 000000000..55533f963 --- /dev/null +++ b/modules/helfi_tfa/helfi_tfa.install @@ -0,0 +1,85 @@ +get('key_provider_settings'); + + if (empty($key['key_value']) || $key['key_value'] === 'thisvaluewillbeoverridden1234567') { + $requirements['helfi_tfa'] = [ + 'title' => t('Hel.fi: TFA'), + 'severity' => REQUIREMENT_ERROR, + 'value' => t('The TFA key is not set or is using the default value.'), + ]; + } + return $requirements; +} + +/** + * Grants required permissions. + */ +function helfi_tfa_grant_permissions() : void { + $roles = Role::loadMultiple(); + $permissions = []; + + // Require all roles to setup TFA. + foreach ($roles as $role) { + if ($role->id() === AccountInterface::ANONYMOUS_ROLE) { + continue; + } + $permissions[$role->id()] = [ + 'setup own tfa', + 'disable own tfa', + ]; + } + + helfi_platform_config_grant_permissions($permissions); +} + +/** + * Implements hook_install(). + */ +function helfi_tfa_install($is_syncing) : void { + // Do not perform following steps if the module is being installed as part + // of a configuration import. + if ($is_syncing) { + return; + } + $roles = Role::loadMultiple(); + $requiredRoles = []; + + $bypassRoles = [ + AccountInterface::ANONYMOUS_ROLE, + AccountInterface::AUTHENTICATED_ROLE, + 'read_only', + ]; + // Require all roles except 'read_only' to setup TFA. + foreach ($roles as $role) { + if (in_array($role->id(), $bypassRoles)) { + continue; + } + $requiredRoles[$role->id()] = $role->id(); + } + $config = \Drupal::configFactory()->getEditable('tfa.settings'); + $config->set('required_roles', $requiredRoles) + ->save(); + + helfi_tfa_grant_permissions(); +}