From e0db22915b21cd2279cf29065028283d5a557165 Mon Sep 17 00:00:00 2001 From: Robert Nasarek Date: Mon, 4 Sep 2023 14:38:02 +0200 Subject: [PATCH] more validation --- .../WisskiCloudAccountManagerController.php | 7 +- .../WisskiCloudAccountManagerCreateForm.php | 11 +- .../WisskiCloudAccountManagerSettingsForm.php | 33 +++- ...skiCloudAccountManagerDaemonApiActions.php | 148 +++++++++++++----- ...nt-manager-account-managing-page.html.twig | 2 - ...nager-terms-and-conditions-page.html.twig} | 0 ...-account-manager-validation-page.html.twig | 2 - wisski_cloud_account_manager.links.menu.yml | 23 ++- wisski_cloud_account_manager.module | 25 ++- wisski_cloud_account_manager.permissions.yml | 6 + wisski_cloud_account_manager.routing.yml | 20 ++- wisski_cloud_account_manager.services.yml | 1 + 12 files changed, 196 insertions(+), 82 deletions(-) rename templates/{wisski_cloud_account_manager_terms-and-conditions-page.html.twig => wisski-cloud-account-manager-terms-and-conditions-page.html.twig} (100%) create mode 100644 wisski_cloud_account_manager.permissions.yml diff --git a/src/Controller/WisskiCloudAccountManagerController.php b/src/Controller/WisskiCloudAccountManagerController.php index c9f54f2..5a651d7 100644 --- a/src/Controller/WisskiCloudAccountManagerController.php +++ b/src/Controller/WisskiCloudAccountManagerController.php @@ -47,7 +47,7 @@ class WisskiCloudAccountManagerController extends ControllerBase { */ public function termsAndConditionsPage(): array { $build = [ - '#theme' => 'terms_and_conditions_page', + '#theme' => 'wisski_cloud_account_manager_terms_and_conditions_page', '#date' => date('Y'), ]; return $build; @@ -63,10 +63,7 @@ class WisskiCloudAccountManagerController extends ControllerBase { * The page build array. */ public function validationPage(string $validationCode): array { - $validationResponse = $this->wisskiCloudAccountManagerDaemonApiActions->validateAccount($validationCode); - - $account = json_decode($validationResponse->getBody() - ->getContents(), TRUE); + $account = $this->wisskiCloudAccountManagerDaemonApiActions->validateAccount($validationCode); return [ '#theme' => 'wisski_cloud_account_manager_validation_page', '#account' => $account, diff --git a/src/Form/WisskiCloudAccountManagerCreateForm.php b/src/Form/WisskiCloudAccountManagerCreateForm.php index 7edeb11..3e77fff 100644 --- a/src/Form/WisskiCloudAccountManagerCreateForm.php +++ b/src/Form/WisskiCloudAccountManagerCreateForm.php @@ -172,6 +172,7 @@ class WisskiCloudAccountManagerCreateForm extends FormBase { // @todo Check if username is WissKI Cloud accounts, i.e add direct by admin?. $dataToCheck['username'] = $form_state->getValue('username'); $dataToCheck['email'] = $form_state->getValue('email'); + $dataToCheck['emailProvider'] = explode('@', $dataToCheck['email'])[1]; $dataToCheck['subdomain'] = $form_state->getValue('subdomain'); $response = $this->wisskiCloudAccountManagerDaemonApiActions->checkAccountData($dataToCheck); @@ -182,13 +183,17 @@ class WisskiCloudAccountManagerCreateForm extends FormBase { if (strlen($dataToCheck['username']) < 3) { $form_state->setErrorByName('username', $this->t('The username "@username" is too short, please use at least 3 characters.', ['@username' => $dataToCheck['username']])); } - if (in_array($dataToCheck['username'], explode(',', $this->settings->get('usernameBlacklist')))) { + if (in_array($dataToCheck['username'], preg_split('/\r\n|\r|\n/', $this->settings->get('usernameBlacklist')))) { $form_state->setErrorByName('username', $this->t('The username "@username" is not allowed.', ['@username' => $dataToCheck['username']])); } if ($response['accountData']['accountWithUsername']) { $form_state->setErrorByName('username', $this->t('The username "@username" is already in use.', ['@username' => $dataToCheck['username']])); } + if (in_array($dataToCheck['emailProvider'], preg_split('/\r\n|\r|\n/', $this->settings->get('emailProviderBlacklist')))) { + $form_state->setErrorByName('email', $this->t('The email provider "@provider"is not allowed.', ['@provider' => $dataToCheck['emailProvider']])); + } + if ($response['accountData']['accountWithEmail']) { $form_state->setErrorByName('email', $this->t('The email "@email" is already in use.', ['@email' => $dataToCheck['email']])); } @@ -197,7 +202,7 @@ class WisskiCloudAccountManagerCreateForm extends FormBase { $form_state->setErrorByName('subdomain', $this->t('The subdomain "@subdomain" is too short, please use at least 3 characters.', ['@subdomain' => $dataToCheck['subdomain']])); } - if (in_array($dataToCheck['subdomain'], explode(',', $this->settings->get('subdomainBlacklist')))) { + if (in_array($dataToCheck['subdomain'], preg_split('/\r\n|\r|\n/', $this->settings->get('subdomainBlacklist')))) { $form_state->setErrorByName('subdomain', $this->t('The subdomain "@subdomain" is not allowed.', ['@subdomain' => $dataToCheck['subdomain']])); } if ($response['accountData']['accountWithSubdomain']) { @@ -231,6 +236,8 @@ class WisskiCloudAccountManagerCreateForm extends FormBase { ->addMessage($this->t('The account data has been successfully saved, please check your email for validation!')); } catch (\Exception $ex) { + $this->messenger() + ->addError($this->t('The account data could not be saved, please try again later or write an email to cloud@wiss-ki.eu.')); $this->logger('wisski_cloud_account_manager')->error($ex->getMessage()); } } diff --git a/src/Form/WisskiCloudAccountManagerSettingsForm.php b/src/Form/WisskiCloudAccountManagerSettingsForm.php index ed63ad9..ca1c710 100644 --- a/src/Form/WisskiCloudAccountManagerSettingsForm.php +++ b/src/Form/WisskiCloudAccountManagerSettingsForm.php @@ -71,15 +71,28 @@ class WisskiCloudAccountManagerSettingsForm extends ConfigFormBase { ]; $form['usernameBlacklist'] = [ - '#type' => 'textfield', + '#type' => 'textarea', '#title' => $this->t('Username blacklist'), - '#description' => $this->t('Provide blocked usernames with a comma separated list, i. e. "admin,root"'), + '#rows' => '5', + '#cols' => '60', + '#description' => $this->t('Provide blocked usernames separeated by new lines, i. e. "\n admin \n root"'), '#default_value' => $config->get('usernameBlacklist'), ]; + $form['emailProviderBlacklist'] = [ + '#type' => 'textarea', + '#title' => $this->t('Email provider blacklist'), + '#rows' => '5', + '#cols' => '60', + '#resizable' => 'vertical', + '#description' => $this->t('Provide blocked email providers with a comma separated list, i. e. "\n admin\nroot"'), + '#default_value' => $config->get('emailProviderBlacklist'), + ]; $form['subdomainBlacklist'] = [ - '#type' => 'textfield', + '#type' => 'textarea', + '#rows' => '5', + '#cols' => '60', '#title' => $this->t('Subdomain blacklist'), - '#description' => $this->t('Provide blocked subdomain with a comma separated list, i. e. "www,admin,root"'), + '#description' => $this->t('Provide blocked subdomain with a comma separated list, i. e. "\nwww\nadmin\nroot"'), '#default_value' => $config->get('subdomainBlacklist'), ]; @@ -91,11 +104,14 @@ class WisskiCloudAccountManagerSettingsForm extends ConfigFormBase { */ public function validateForm(array &$form, FormStateInterface $form_state): void { parent::validateForm($form, $form_state); - if (!preg_match("/^(?:\w+(?:,\w+)*)?$/", $form_state->getValue('usernameBlacklist'))) { - $form_state->setErrorByName('usernameBlacklist', $this->t('The username blacklist is not valid. Only words separated by commas are allowed.')); + if (!preg_match("/^[a-zA-Z0-9\-]+(\r?\n[a-zA-Z0-9\-]+)*$/", $form_state->getValue('usernameBlacklist'))) { + $form_state->setErrorByName('usernameBlacklist', $this->t('The username blacklist is not valid. Only words separated by new lines are allowed.')); } - if (!preg_match("/^(?:\w+(?:,\w+)*)?$/", $form_state->getValue('subdomainBlacklist'))) { - $form_state->setErrorByName('subdomainBlacklist', $this->t('The subdomain blacklist is not valid. Only words separated by commas are allowed.')); + if (!preg_match("/^([a-zA-Z0-9-]+\.[a-zA-Z0-9-]+)+(\r?\n[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+)*$/", $form_state->getValue('emailProviderBlacklist'))) { + $form_state->setErrorByName('emailProviderBlacklist', $this->t('The email provider blacklist is not valid. Only <second level domain> <dot> <first level domain> separated by new lines are allowed.')); + } + if (!preg_match("/^[a-zA-Z0-9\-]+(\r?\n[a-zA-Z0-9\-]+)*$/", $form_state->getValue('subdomainBlacklist'))) { + $form_state->setErrorByName('subdomainBlacklist', $this->t('The subdomain blacklist is not valid. Only words separated by new lines are allowed.')); } } @@ -111,6 +127,7 @@ class WisskiCloudAccountManagerSettingsForm extends ConfigFormBase { ->set('accountProvisionAndValidationCheck', $form_state->getValue('accountProvisionAndValidationCheck')) ->set('accountValidation', $form_state->getValue('accountValidation')) ->set('usernameBlacklist', $form_state->getValue('usernameBlacklist')) + ->set('emailProviderBlacklist', $form_state->getValue('emailProviderBlacklist')) ->set('subdomainBlacklist', $form_state->getValue('subdomainBlacklist')) ->save(); diff --git a/src/WisskiCloudAccountManagerDaemonApiActions.php b/src/WisskiCloudAccountManagerDaemonApiActions.php index 93630d1..75b7148 100644 --- a/src/WisskiCloudAccountManagerDaemonApiActions.php +++ b/src/WisskiCloudAccountManagerDaemonApiActions.php @@ -2,6 +2,7 @@ namespace Drupal\wisski_cloud_account_manager; +use Symfony\Component\HttpFoundation\RequestStack; use Drupal\Core\Config\Config; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\DependencyInjection\DependencySerializationTrait; @@ -12,7 +13,6 @@ use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Render\Markup; use Drupal\Core\StringTranslation\TranslationInterface; use GuzzleHttp\ClientInterface; -use Psr\Http\Message\ResponseInterface; /** * Handles the communication with the WissKI Cloud account manager daemon. @@ -105,6 +105,13 @@ class WisskiCloudAccountManagerDaemonApiActions { */ protected MailManagerInterface $mailManager; + /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected RequestStack $requestStack; + /** * The string translation service. * @@ -122,21 +129,21 @@ class WisskiCloudAccountManagerDaemonApiActions { LoggerChannelFactoryInterface $loggerFactory, MessengerInterface $messenger, MailManagerInterface $mailManager, + RequestStack $requestStack, TranslationInterface $stringTranslation, ) { // Services from container. + $settings = $configFactory + ->getEditable('wisski_cloud_account_manager.settings'); + $this->settings = $settings; $this->stringTranslation = $stringTranslation; $this->loggerFactory = $loggerFactory; $this->messenger = $messenger; $this->httpClient = $httpClient; $this->mailManager = $mailManager; + $this->requestStack = $requestStack; $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->ALL_ACCOUNTS = $settings->get('allAccounts') ?: '/account/all'; @@ -156,16 +163,34 @@ class WisskiCloudAccountManagerDaemonApiActions { * The response from the daemon (account id with validation code). */ public function addAccount(array $account): array { - $request = [ - 'headers' => [ - 'Content-Type' => 'application/json', - ], - 'body' => json_encode($account), - ]; - $accountPostUrl = $this->DAEMON_URL . $this->ACCOUNT_POST_URL_PART; - $response = $this->httpClient->post($accountPostUrl, $request); - return array_merge(json_decode($response->getBody() - ->getContents(), TRUE), ['statusCode' => $response->getStatusCode()]); + try { + $request = [ + 'headers' => [ + 'Content-Type' => 'application/json', + ], + 'body' => json_encode($account), + ]; + $accountPostUrl = $this->DAEMON_URL . $this->ACCOUNT_POST_URL_PART; + $response = $this->httpClient->post($accountPostUrl, $request); + return json_decode($response->getBody() + ->getContents(), TRUE); + } + catch (\Exception $e) { + // Request failed, handle the error. + $this->loggerFactory + ->get('wisski_cloud_account_manager') + ->error('Request failed with exception: ' . $e->getMessage()); + $this->messenger + ->addError($this->stringTranslation->translate('Can not communicate with the WissKI Cloud account manager daemon. Try again later or contact cloud@wiss-ki.eu.')); + return [ + "message" => 'Request failed with exception: ' . $e->getMessage(), + "data" => [ + 'email' => NULL, + 'validationCode' => NULL, + ], + 'success' => FALSE, + ]; + } } /** @@ -219,6 +244,8 @@ class WisskiCloudAccountManagerDaemonApiActions { $this->loggerFactory ->get('wisski_cloud_account_manager') ->error('Request failed with exception: ' . $e->getMessage()); + $this->messenger + ->addError($this->stringTranslation->translate('Can not communicate with the WissKI Cloud account manager daemon. Try again later or contact cloud@wiss-ki.eu.')); return [ "message" => 'Request failed with exception: ' . $e->getMessage(), "accountData" => [ @@ -238,11 +265,26 @@ class WisskiCloudAccountManagerDaemonApiActions { * The accounts response from the daemon. */ public function getAccounts(): array { - // Combine the base URL and the query string. - $request_url = $this->DAEMON_URL . $this->ALL_ACCOUNTS; - // Send the GET request using the `drupal_http_request()` function. - $response = $this->httpClient->get($request_url); - return json_decode($response->getBody()->getContents(), TRUE); + try { + // Combine the base URL and the query string. + $request_url = $this->DAEMON_URL . $this->ALL_ACCOUNTS; + // Send the GET request using the `drupal_http_request()` function. + $response = $this->httpClient->get($request_url); + return json_decode($response->getBody()->getContents(), TRUE); + } + catch (\Exception $e) { + // Request failed, handle the error. + $this->loggerFactory + ->get('wisski_cloud_account_manager') + ->error('Request failed with exception: ' . $e->getMessage()); + $this->messenger + ->addError($this->stringTranslation->translate('Can not communicate with the WissKI Cloud account manager daemon. Try again later or contact cloud@wiss-ki.eu.')); + return [ + "message" => 'Request failed with exception: ' . $e->getMessage(), + "accounts" => [], + 'success' => FALSE, + ]; + } } /** @@ -251,12 +293,29 @@ class WisskiCloudAccountManagerDaemonApiActions { * @param string $validationCode * The validation code to check. * - * @return \Psr\Http\Message\ResponseInterface - * The response from the daemon. + * @return array + * The account data from the daemon. */ - public function validateAccount(string $validationCode): ResponseInterface { - $url = $this->DAEMON_URL . $this->ACCOUNT_VALIDATION_URL_PART . '/' . $validationCode; - return $this->httpClient->put($url); + public function validateAccount(string $validationCode): array { + try { + $url = $this->DAEMON_URL . $this->ACCOUNT_VALIDATION_URL_PART . '/' . $validationCode; + $validationResponse = $this->httpClient->put($url); + return json_decode($validationResponse->getBody() + ->getContents(), TRUE); + } + catch (\Exception $e) { + // Request failed, handle the error. + $this->loggerFactory + ->get('wisski_cloud_account_manager') + ->error('Request failed with exception: ' . $e->getMessage()); + $this->messenger + ->addError($this->stringTranslation->translate('Can not communicate with the WissKI Cloud account manager daemon. Try again later or contact cloud@wiss-ki.eu.')); + return [ + "message" => 'Request failed with exception: ' . $e->getMessage(), + "accounts" => [], + 'success' => FALSE, + ]; + } } /** @@ -268,26 +327,37 @@ class WisskiCloudAccountManagerDaemonApiActions { * 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; + try { + $module = 'wisski_cloud_account_manager'; + $key = 'wisski_cloud_account_validation'; + $langcode = $this->languageManager->getDefaultLanguage()->getId(); + $to = $email; - $validationLink = \Drupal::request() + $validationLink = $this->requestStack->getCurrentRequest() ->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'); + $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); + if ($result['result'] === TRUE) { + $this->messenger + ->addMessage($this->stringTranslation->translate('Email send successfully.')); + } + else { + $this->messenger + ->addMessage($this->stringTranslation->translate('There was an error sending the email.'), 'error'); - $result = $this->mailManager->mail($module, $key, $to, $langcode, $params, NULL, TRUE); - if ($result['result'] === TRUE) { - $this->messenger - ->addMessage($this->stringTranslation->translate('Email sent successfully.')); + } } - else { + catch (\Exception $e) { + // Request failed, handle the error. + $this->loggerFactory + ->get('wisski_cloud_account_manager') + ->error('Email sending operation ended with exception: ' . $e->getMessage()); $this->messenger - ->addMessage($this->stringTranslation->translate('There was an error sending the email.'), 'error'); + ->addError($this->stringTranslation->translate('Email sending operation ended with error. Try again later or contact cloud@wiss-ki.eu.')); } + } } diff --git a/templates/wisski-cloud-account-manager-account-managing-page.html.twig b/templates/wisski-cloud-account-manager-account-managing-page.html.twig index e457fe3..ffb3f5b 100644 --- a/templates/wisski-cloud-account-manager-account-managing-page.html.twig +++ b/templates/wisski-cloud-account-manager-account-managing-page.html.twig @@ -10,7 +10,6 @@ Subdomain Valid Provisioned - Error Options {% for item in accounts.data %} @@ -38,7 +37,6 @@ {% else %} unknown {% endif %} - {{ accounts.error }}