From 79887ee7e65ec12b1c0b19442344834de2fd42cb Mon Sep 17 00:00:00 2001
From: Robert Nasarek
Date: Mon, 7 Aug 2023 15:47:35 +0200
Subject: [PATCH] add config page and validation
---
README.md | 20 +++-
.../WisskiCloudAccountManagerCreateForm.php | 12 +-
.../WisskiCloudAccountManagerSettingsForm.php | 43 ++++---
...skiCloudAccountManagerDaemonApiActions.php | 105 ++++++++++++++++--
wisski_cloud_account_manager.info.yml | 2 +-
wisski_cloud_account_manager.links.menu.yml | 5 +
wisski_cloud_account_manager.module | 44 ++++++++
wisski_cloud_account_manager.routing.yml | 13 ++-
wisski_cloud_account_manager.services.yml | 2 +
9 files changed, 210 insertions(+), 36 deletions(-)
create mode 100644 wisski_cloud_account_manager.links.menu.yml
create mode 100644 wisski_cloud_account_manager.module
diff --git a/README.md b/README.md
index 8155ca1..5e1b800 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,19 @@
-# WissKI Cloud Account Manager
\ No newline at end of file
+# WissKI Cloud Account Manager
+
+## Introduction
+This module provides an account creation page at `/cloud-account-manager/create` which can be used to create a new account.
+It is intended to be used in combination with the [WissKI Cloud API Daemon]().
+If you submit the account form, an email with a validation link is sent to the email address you provided. After the validation, the provision of your WissKI Cloud instance at .wisski.cloud starts and can be checked at `/cloud-account-manager/check/`.
+## Requirements
+* A correct configured and functional [WissKI Cloud API Daemon](). If you use docker, be sure to have the daemon and drupal site in the same network.
+* A valid SMTP System - you may need additional modules for SMTP, i.e. [SMTP Authentication Support](https://www.drupal.org/project/smtp).
+## Installation
+- Clone this repository into your modules folder and enable the module.
+- Enable the module `WissKI Cloud Account Manager` in the Drupal UI.
+
+## Configuration
+- Go to `/admin/config/system/cloud-account-manager` and configure the module.
+
+## Maintainers
+
+## License
diff --git a/src/Form/WisskiCloudAccountManagerCreateForm.php b/src/Form/WisskiCloudAccountManagerCreateForm.php
index f9abd75..447220c 100644
--- a/src/Form/WisskiCloudAccountManagerCreateForm.php
+++ b/src/Form/WisskiCloudAccountManagerCreateForm.php
@@ -51,7 +51,7 @@ class WisskiCloudAccountManagerCreateForm extends FormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
- $form['personname'] = [
+ $form['personName'] = [
'#type' => 'textfield',
'#title' => $this->t('Person name'),
'#description' => $this->t('Your first and last name.'),
@@ -117,7 +117,7 @@ class WisskiCloudAccountManagerCreateForm extends FormBase {
/**
* {@inheritdoc}
*/
- public function validateForm(array &$form, FormStateInterface $form_state) {
+ public function validateForm(array &$form, FormStateInterface $form_state): void {
// Check if account data is already in use.
// @todo Check if username is WissKI Cloud accounts, i.e add direct by admin?.
$dataToCheck['username'] = $form_state->getValue('username');
@@ -151,17 +151,19 @@ class WisskiCloudAccountManagerCreateForm extends FormBase {
try {
$field = $form_state->getValues();
- $account["personname"] = $field['personname'];
+ $account["personName"] = $field['personName'];
$account["organisation"] = $field['organisation'];
$account["email"] = $field['email'];
$account["username"] = $field['username'];
$account["password"] = $field['password'];
$account["subdomain"] = $field['subdomain'];
- $this->wisskiCloudAccountManagerDaemonApiActions->addAccount($account);
+ $accountResponse = $this->wisskiCloudAccountManagerDaemonApiActions->addAccount($account);
+ dpm($accountResponse, 'accountResponse');
+ $this->wisskiCloudAccountManagerDaemonApiActions->sendValidationEmail($accountResponse['user']['email'], $accountResponse['user']['validationCode']);
\Drupal::messenger()
- ->addMessage($this->t('The account data has been succesfully saved'));
+ ->addMessage($this->t('The account data has been successfully saved, please check your email for validation!'));
}
catch (\Exception $ex) {
\Drupal::logger('wisski_cloud_account_manager')->error($ex->getMessage());
diff --git a/src/Form/WisskiCloudAccountManagerSettingsForm.php b/src/Form/WisskiCloudAccountManagerSettingsForm.php
index 887efab..c32cf81 100644
--- a/src/Form/WisskiCloudAccountManagerSettingsForm.php
+++ b/src/Form/WisskiCloudAccountManagerSettingsForm.php
@@ -35,6 +35,27 @@ class WisskiCloudAccountManagerSettingsForm extends ConfigFormBase {
/** @var \Drupal\Core\Config\ImmutableConfig $config */
$config = $this->config('wisski_cloud_account_manager.settings');
+ $form['daemonUrl'] = [
+ '#type' => 'url',
+ '#title' => $this->t('The WissKI Cloud API Daemon URL'),
+ '#description' => $this->t('Provide the complete base URL with protocol, domain (resp. service name in docker), ports and API path, i. e. "http://wisski_cloud_api_daemon:3000/wisski-cloud-daemon/api/v1"'),
+ '#default_value' => $config->get('daemonUrl'),
+ ];
+
+ $form['userPostUrlPath'] = [
+ '#type' => 'url',
+ '#title' => $this->t('POST URL path'),
+ '#description' => $this->t('Provide the path to the POST endpoint, i. e. "/user"'),
+ '#default_value' => $config->get('userPostUrlPath'),
+ ];
+
+ $form['userFilterByData'] = [
+ '#type' => 'url',
+ '#title' => $this->t('Filter by Data URL path'),
+ '#description' => $this->t('Provide the path to the Get user by data endpoint, i. e. "/user/by_data"'),
+ '#default_value' => $config->get('userFilterByData'),
+ ];
+
return $form;
}
@@ -43,27 +64,13 @@ class WisskiCloudAccountManagerSettingsForm extends ConfigFormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $this->config('wisski_cloud_account_manager.settings');
- /*
- $config->set('', $form_state->getValue(''))
+
+ $config->set('daemonURL', $form_state->getValue('daemonURL'))
+ ->set('userPostUrlPath', $form_state->getValue('userPostUrlPath'))
+ ->set('userFilterByData', $form_state->getValue('userFilterByData'))
->save();
- */
parent::submitForm($form, $form_state);
}
- /**
- * {@inheritdoc}
- */
- public function validateForm(array &$form, FormStateInterface $form_state) {
- /*
- // Hash expiration validation.
- $hash_expiration = intval($form_state->getValue('hash_expiration'));
- if ($hash_expiration < 1) {
- $form_state->setErrorByName('hash_expiration', $this->t('The miminum hash expiration time is @min_value.', ['@min_value' => $this->t('one hour')]));
- }
- elseif ($hash_expiration > 48) {
- $form_state->setErrorByName('hash_expiration', $this->t('The maximum hash expiration time is @max_value.', ['@max_value' => $this->t('@count days', ['@count' => 2])]));
- }
- */
- }
}
diff --git a/src/WisskiCloudAccountManagerDaemonApiActions.php b/src/WisskiCloudAccountManagerDaemonApiActions.php
index ff243d0..8bc0b70 100644
--- a/src/WisskiCloudAccountManagerDaemonApiActions.php
+++ b/src/WisskiCloudAccountManagerDaemonApiActions.php
@@ -4,7 +4,11 @@ namespace Drupal\wisski_cloud_account_manager;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\DependencyInjection\DependencySerializationTrait;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Messenger\MessengerInterface;
+use Drupal\Core\Render\Markup;
use Drupal\Core\StringTranslation\TranslationInterface;
use GuzzleHttp\ClientInterface;
@@ -13,9 +17,28 @@ use GuzzleHttp\ClientInterface;
*/
class WisskiCloudAccountManagerDaemonApiActions {
- const DAEMON_URL = 'http://wisski_cloud_api_daemon:3000/wisski-cloud-daemon/api/v1/user/';
+ use DependencySerializationTrait;
- const FILTER_BY_DATA_URL_PART = 'by_data';
+ /**
+ * The base URL of the WissKI Cloud account manager daemon.
+ *
+ * @var string
+ */
+ private string $DAEMON_URL;
+
+ /**
+ * The URL path to the POST endpoint.
+ *
+ * @var string
+ */
+ private string $USER_POST_URL_PART = '/user';
+
+ /**
+ * The URL path to the GET endpoint.
+ *
+ * @var string
+ */
+ private string $FILTER_BY_DATA_URL_PART = '/user/by_data';
/**
* The string translation service.
@@ -45,15 +68,49 @@ class WisskiCloudAccountManagerDaemonApiActions {
*/
protected Config $settings;
+
+
+ /**
+ * The mail manager.
+ *
+ * @var \Drupal\Core\Mail\MailManagerInterface
+ */
+ protected MailManagerInterface $mailManager;
+
+ /**
+ * The language manager.
+ *
+ * @var \Drupal\Core\Language\LanguageManagerInterface
+ */
+ protected LanguageManagerInterface $languageManager;
+
/**
* Class constructor.
*/
- public function __construct(TranslationInterface $stringTranslation, MessengerInterface $messenger, ClientInterface $httpClient, ConfigFactoryInterface $configFactory) {
+ public function __construct(
+ TranslationInterface $stringTranslation,
+ MessengerInterface $messenger,
+ ClientInterface $httpClient,
+ ConfigFactoryInterface $configFactory,
+ MailManagerInterface $mailManager,
+ LanguageManagerInterface $languageManager) {
+ // Services from container.
$this->stringTranslation = $stringTranslation;
$this->messenger = $messenger;
$this->httpClient = $httpClient;
- $this->settings = $configFactory
+ $this->mailManager = $mailManager;
+ $this->languageManager = $languageManager;
+
+ // Settings.
+ $settings = $configFactory
->getEditable('wisski_cloud_account_manager.settings');
+ $this->settings = $settings;
+
+ // Set the daemon URL and the URL parts class variables.
+ $this->DAEMON_URL = $settings->get('daemonUrl') ?: 'http://wisski_cloud_api_daemon:3000/wisski-cloud-daemon/api/v1';
+ $this->USER_POST_URL_PART = $settings->get('userPostUrlPath') ?: '/user';
+ $this->FILTER_BY_DATA_URL_PART = $settings->get('userFilterByData') ?: '/user/by_data';
+
}
/**
@@ -66,16 +123,15 @@ class WisskiCloudAccountManagerDaemonApiActions {
* The response from the daemon (user id with validation code).
*/
public function addAccount(array $account): array {
- dpm($account, 'account');
$request = [
'headers' => [
'Content-Type' => 'application/json',
],
'body' => json_encode($account),
];
- dpm($request, 'request');
- $response = $this->httpClient->post(self::DAEMON_URL, $request);
- return json_decode($response->getBody()->getContents(), TRUE);
+ $userPostUrl = $this->DAEMON_URL . $this->USER_POST_URL_PART;
+ $response = $this->httpClient->post($userPostUrl, $request);
+ return array_merge(json_decode($response->getBody()->getContents(), TRUE), ['statusCode' => $response->getStatusCode()]);
}
/**
@@ -92,7 +148,7 @@ class WisskiCloudAccountManagerDaemonApiActions {
$query_string = http_build_query($dataToCheck);
// Combine the base URL and the query string.
- $request_url = self::DAEMON_URL . self::FILTER_BY_DATA_URL_PART . '?' . $query_string;
+ $request_url = $this->DAEMON_URL . $this->FILTER_BY_DATA_URL_PART . '?' . $query_string;
// Send the GET request using the `drupal_http_request()` function.
$response = $this->httpClient->get($request_url);
@@ -114,4 +170,35 @@ class WisskiCloudAccountManagerDaemonApiActions {
}
}
+ /**
+ * Sends a validation email to the given email address.
+ *
+ * @param string $email
+ * The email address to send the validation email to.
+ * @param string $validationCode
+ * The validation code to be used in the validation link.
+ */
+ public function sendValidationEmail(string $email, string $validationCode): void {
+ $module = 'wisski_cloud_account_manager';
+ $key = 'wisski_cloud_account_validation';
+ $langcode = $this->languageManager->getDefaultLanguage()->getId();
+ $to = $email;
+
+ $validationLink = \Drupal::request()->getSchemeAndHttpHost() . '/wisski-cloud-account-manager/validate/' . $validationCode;
+
+ $params['message'] = Markup::create($this->stringTranslation->translate('Please validate your account by clicking on this link or copy this to the address bar of your browser:
@validationLink
.
', ['@validationLink' => $validationLink]));
+ $params['subject'] = $this->stringTranslation->translate('WissKI Cloud account validation');
+
+ $result = $this->mailManager->mail($module, $key, $to, $langcode, $params, NULL, TRUE);
+ dpm($result, 'result');
+ if ($result['result'] === TRUE) {
+ $this->messenger
+ ->addMessage($this->stringTranslation->translate('Email sent successfully.'));
+ }
+ else {
+ $this->messenger
+ ->addMessage($this->stringTranslation->translate('There was an error sending the email.'), 'error');
+ }
+ }
+
}
diff --git a/wisski_cloud_account_manager.info.yml b/wisski_cloud_account_manager.info.yml
index a4d1640..4cf381f 100644
--- a/wisski_cloud_account_manager.info.yml
+++ b/wisski_cloud_account_manager.info.yml
@@ -1,6 +1,6 @@
name: 'WissKI cloud account manager'
type: module
-description: 'WissKI cloud manager with opt in service. Provides a common API for creating users for the WissKI Cloud.'
+description: 'WissKI cloud manager with opt in service. Provides a create form and handles communication with WissKI Cloud API Daemon for creating users for the WissKI Cloud.'
package: WissKI Cloud
configure: wisski_cloud_account_manager.settings
core_version_requirement: ^9 || ^10
diff --git a/wisski_cloud_account_manager.links.menu.yml b/wisski_cloud_account_manager.links.menu.yml
new file mode 100644
index 0000000..8cd3622
--- /dev/null
+++ b/wisski_cloud_account_manager.links.menu.yml
@@ -0,0 +1,5 @@
+wisski_cloud_account.settings.menu:
+ title: 'WissKI cloud account settings'
+ description: 'WissKI cloud account settings'
+ parent: system.admin_config_wisski_cloud_account
+ route_name: wisski_cloud_account.settings
diff --git a/wisski_cloud_account_manager.module b/wisski_cloud_account_manager.module
new file mode 100644
index 0000000..6d1d99b
--- /dev/null
+++ b/wisski_cloud_account_manager.module
@@ -0,0 +1,44 @@
+' . t('About') . '';
+ $output .= '' . t('This module provides a form to create a WissKI Cloud account. Create form can be found at the route "/wisski_cloud_account_manager/create" ', ['@createPage' => '/wisski_cloud_account_manager/create']). '
';
+ return $output;
+ }
+}
+
+/**
+ * Implements hook_mail().
+ */
+function wisski_cloud_account_manager_mail($key, &$message, $params) {
+ $options = array(
+ 'langcode' => $message['langcode'],
+ );
+ switch ($key) {
+ case 'wisski_cloud_account_validation':
+ $message['from'] = \Drupal::config('system.site')->get('mail');
+ $message['subject'] = t('@subject', array('@subject' => $params['subject']), $options);
+ $message['body'][] = $params['message'];
+
+ $headers = [
+ 'Content-Type' => 'text/html; charset=UTF-8; format=flowed',
+ 'MIME-Version' => '1.0',
+ 'Content-Transfer-Encoding' => '8Bit',
+ 'X-Mailer' => 'Drupal',
+ ];
+
+ $message['headers'] = $headers;
+
+ break;
+ }
+}
diff --git a/wisski_cloud_account_manager.routing.yml b/wisski_cloud_account_manager.routing.yml
index fcf67e4..1643fb1 100644
--- a/wisski_cloud_account_manager.routing.yml
+++ b/wisski_cloud_account_manager.routing.yml
@@ -1,5 +1,14 @@
+# Site config group
+system.admin_config_wisski_cloud_account:
+ path: '/admin/config/wisski-cloud-account-manager'
+ defaults:
+ _controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
+ _title: 'WissKI cloud account manager'
+ requirements:
+ _permission: 'administer site configuration'
+
wisski_cloud_account.settings:
- path: '/admin/config/system/wisski-cloud-account-manager/settings'
+ path: '/admin/config/wisski-cloud-account-manager/settings'
defaults:
_form: '\Drupal\wisski_cloud_account_manager\Form\WisskiCloudAccountManagerSettingsForm'
_title: 'WissKI cloud account settings'
@@ -18,6 +27,6 @@ wisski_cloud_account.terms_and_conditions:
path: '/wisski-cloud-account-manager/terms-and-conditions'
defaults:
_controller: '\Drupal\wisski_cloud_account_manager\Controller\WisskiCloudAccountManagerController::termsAndConditions'
- _title: 'Terms and Conditions'
+ _title: 'Terms and conditions'
requirements:
_access: 'TRUE'
diff --git a/wisski_cloud_account_manager.services.yml b/wisski_cloud_account_manager.services.yml
index f571156..5d97a8e 100644
--- a/wisski_cloud_account_manager.services.yml
+++ b/wisski_cloud_account_manager.services.yml
@@ -6,3 +6,5 @@ services:
- '@messenger'
- '@http_client'
- '@config.factory'
+ - '@plugin.manager.mail'
+ - '@language_manager'