full working version

This commit is contained in:
rnsrk 2023-11-15 01:53:03 +01:00
parent 2178db78f5
commit 308e21941a
22 changed files with 1434 additions and 366 deletions

View file

@ -1,43 +1,4 @@
/* Reset the table styles */ .spinner-border {
table.wcam--table { height: 1em !important;
border-collapse: collapse; width: 1em !important;
width: 100%;
}
/* Style for table headers */
table.wcam--table th {
background-color: #f2f2f2;
padding: 8px;
text-align: center;
}
/* Alternate row background color */
table.wcam--table tr:nth-child(even) {
background-color: #f2f2f2;
}
/* Style for table cells */
table.wcam--table td {
padding: 8px;
border: 1px solid #dddddd;
}
/* Center-align certain cells */
table.wcam--table td.valid,
table.wcam--table td.provisioned {
text-align: center;
}
.wisski-cloud-account-manager-success {
background-color: green;
}
.wisski-cloud-account-manager-warning {
background-color: orange;
}
.wisski-cloud-account-manager-error {
background-color: red;
}
.wisski-cloud-account-manager-center {
text-align: center;
} }

29
js/accountOptions.js Normal file → Executable file
View file

@ -5,30 +5,37 @@
*/ */
Drupal.behaviors.accountOptions = { Drupal.behaviors.accountOptions = {
attach: function (context, settings) { attach: function (context, settings) {
once('accountOptions', '#wcam--table', context).forEach(function (form) { once('accountOptions', '#wcam--table', context).forEach(function () {
$('.wcam--select').change(function () { $('.wcam--select').change(function () {
console.log($(this))
let selectedOption = $(this).val(); let selectedOption = $(this).val();
let itemId = $(this).closest('tr').find('.wcam--row--item-id').text(); let aid = $(this).closest('tr').find('.wcam--row--account-id').text().trim();
switch (selectedOption) { switch (selectedOption) {
case 'delete': case 'delete':
// Führen Sie hier Ihren JavaScript-Code für 'delete' aus. // Construct the URL for the delete route.
let deleteUrl = Drupal.url('wisski-cloud-account-manager/delete/' + aid);
console.log('delete:', itemId); // Redirect to the delete route.
window.location.href = deleteUrl;
break; break;
case 'edit': case 'edit':
// Führen Sie hier Ihren JavaScript-Code für 'edit' aus. console.log('Edit:', aid);
console.log('Edit:', itemId);
break; break;
case 'provise': case 'provise':
// Führen Sie hier Ihren JavaScript-Code für 'provise' aus. // Construct the URL for the provise route.
console.log('Provise', itemId); let proviseUrl = Drupal.url('wisski-cloud-account-manager/provise/' + aid);
// Redirect to the provise route.
window.location.href = proviseUrl;
break;
case 'purge':
// Construct the URL for the purge route.
let purgeUrl = Drupal.url('wisski-cloud-account-manager/purge/' + aid);
// Redirect to the purge route.
window.location.href = purgeUrl;
break; break;
case 'validate': case 'validate':
// Führen Sie hier Ihren JavaScript-Code für 'validate' aus.
console.log('Die Option "validate" wurde ausgewählt.'); console.log('Die Option "validate" wurde ausgewählt.');
break; break;

37
js/provisionStatus.js Normal file
View file

@ -0,0 +1,37 @@
(function ($, Drupal, once) {
Drupal.behaviors.wcamProvisionStatus = {
attach: function (context, settings) {
once('wcamProvisionStatus', 'html', context).forEach(function () {
// Get the process idle animation.
let $animation = $('#process-idle-animation');
$animation.show(); // Show the spinner.
// Get the aid and initial provision status from the HTML.
let $rows = $('.wcam--table--row');
$rows.each(function () {
let $row = $(this);
let aid = $row.find('.wcam--row--account-id').text().trim();
let initialStatus = $row.find('#provision-status--aid-' + aid).text().trim();
// If the initial provision status is 'ongoing', start checking the provision status.
if (initialStatus === 'ongoing' || initialStatus === 'unknown') {
let intervalId = setInterval(function () {
$.get('/wisski-cloud-account-manager/provision-status/' + aid, function (data) {
// Update the provision status on the page.
let $status = $('#provision-status--aid-' + aid);
$status.text(data.status);
// If the provision status is not 'ongoing', stop the process idle animation.
if (data.status !== 'ongoing') {
if (data.status !== 'unknown') {
$row.find('#provision-status--row--aid-' + aid).text(data.status);
$animation.hide(); // Hide the spinner.
clearInterval(intervalId);
location.reload();
}
}
});
}, 3000);
}
});
});
}
};
})(jQuery, Drupal, once);

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\wisski_cloud_account_manager\Access;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Database\Connection;
class AccountRouteAccessCheck implements AccessInterface {
protected $currentUser;
protected $database;
public function __construct(AccountInterface $currentUser, Connection $database) {
$this->currentUser = $currentUser;
$this->database = $database;
}
public function access($aid) {
// If the user has the "Administer WissKI Cloud Account Manager" role, allow access.
if ($this->currentUser->hasPermission('Administer WissKI Cloud Account Manager')) {
return AccessResult::allowed()->cachePerUser();
}
// Otherwise, check if the aid matches the user's aid in the database.
$uid = $this->currentUser->id();
$query = $this->database->select('wisski_cloud_account_manager_accounts', 'w')
->fields('w', ['aid'])
->condition('uid', $uid)
->condition('aid', $aid)
->execute();
$result = $query->fetchField();
return AccessResult::allowedIf($result)->cachePerUser();
}
}

View file

@ -5,6 +5,7 @@ namespace Drupal\wisski_cloud_account_manager\Controller;
use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Controller\ControllerBase;
use Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions; use Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
/** /**
* The Wisski Cloud account manager info controller. * The Wisski Cloud account manager info controller.
@ -39,6 +40,63 @@ class WisskiCloudAccountManagerController extends ControllerBase {
); );
} }
/**
* Page list the account statuses.
*
* @return array
* The page build array.
*/
public function accountManagingPage(): array {
$currentUser = \Drupal::currentUser();
$healthCheck = $this->wisskiCloudAccountManagerDaemonApiActions->healthCheck();
$accounts = $this->wisskiCloudAccountManagerDaemonApiActions->getAccounts();
// If the user is not an admin, filter the accounts to only include their own.
if (!$currentUser->hasPermission('admister wisski cloud account manager')) {
$accounts = array_filter($accounts, function($account) use ($currentUser) {
return $account['uid'] === $currentUser->id();
});
}
return [
'#theme' => 'wisski_cloud_account_manager_account_managing_page',
'#accounts' => $accounts,
'#healthCheck' => $healthCheck,
'#attached' => [
'library' => [
'wisski_cloud_account_manager/accountOptions',
'wisski_cloud_account_manager/globalStyling',
'wisski_cloud_account_manager/provisionStatus'
],
],
'#cache' => [
'max-age' => 0,
],
];
}
// @todo Implement healthcheckPage().
public function healthcheckPage(): array {
return [
'#theme' => 'wisski_cloud_account_manager_healthcheck_page',
'#healthCheck' => $this->wisskiCloudAccountManagerDaemonApiActions->healthCheck(),
];
}
public function provisionStatusPage($aid) {
$accounts = $this->wisskiCloudAccountManagerDaemonApiActions->getAccounts($aid);
// Return the status as a JSON response.
switch ($accounts[0]['provisioned']) {
case '0':
return new JsonResponse(['status' => 'no']);
case '1':
return new JsonResponse(['status' => 'ongoing']);
case '2':
return new JsonResponse(['status' => 'yes']);
case '3':
return new JsonResponse(['status' => 'unknown']);
}
return new JsonResponse(['status' => $accounts[0]['provisioned']]);
}
/** /**
* Info page for terms and conditions. * Info page for terms and conditions.
* *
@ -63,41 +121,13 @@ class WisskiCloudAccountManagerController extends ControllerBase {
* The page build array. * The page build array.
*/ */
public function validationPage(string $validationCode): array { public function validationPage(string $validationCode): array {
$account = $this->wisskiCloudAccountManagerDaemonApiActions->validateAccount($validationCode); $user = $this->wisskiCloudAccountManagerDaemonApiActions->validateAccount($validationCode);
return [ return [
'#theme' => 'wisski_cloud_account_manager_validation_page', '#markup' => $this->t('Your WissKI Cloud account for user :name is now validated. You can now <a href="/user">login</a> and start a provision!.', [
'#account' => $account, ':name' => $user['name'],
'#attached' => [ ]),
'library' => [
'wisski_cloud_account_manager/wisski_cloud_account_manager',
],
],
'#cache' => [
'max-age' => 0,
],
]; ];
} }
/**
* Page list the account statuses.
*
* @return array
* The page build array.
*/
public function accountManagingPage(): array {
$accounts = $this->wisskiCloudAccountManagerDaemonApiActions->getAccounts();
return [
'#theme' => 'wisski_cloud_account_manager_account_managing_page',
'#accounts' => $accounts,
'#attached' => [
'library' => [
'wisski_cloud_account_manager/wisski_cloud_account_manager',
],
],
'#cache' => [
'max-age' => 0,
],
];
}
} }

View file

@ -16,9 +16,6 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
*/ */
class WisskiCloudAccountManagerCreateForm extends FormBase { class WisskiCloudAccountManagerCreateForm extends FormBase {
/** /**
* The config factory service. * The config factory service.
* *
@ -126,7 +123,7 @@ class WisskiCloudAccountManagerCreateForm extends FormBase {
'#type' => 'textfield', '#type' => 'textfield',
'#title' => $this->t('Username'), '#title' => $this->t('Username'),
'#maxlength' => 20, '#maxlength' => 20,
'#description' => $this->t('WissKI cloud login user. Only small caps (a-z), underscore (_), minus (-) and 20 letter maximum allowed, i.e. "wisski_user".'), '#description' => $this->t('WissKI cloud login - NOT your password for your instance - this will be send separately. Only small caps (a-z), underscore (_), minus (-) and 20 letter maximum allowed, i.e. "wisski_user".'),
'#pattern' => '[a-z]+([_-]{1}[a-z]+)*', '#pattern' => '[a-z]+([_-]{1}[a-z]+)*',
'#required' => TRUE, '#required' => TRUE,
]; ];
@ -175,38 +172,54 @@ class WisskiCloudAccountManagerCreateForm extends FormBase {
$dataToCheck['email'] = $form_state->getValue('email'); $dataToCheck['email'] = $form_state->getValue('email');
$dataToCheck['emailProvider'] = explode('@', $dataToCheck['email'])[1]; $dataToCheck['emailProvider'] = explode('@', $dataToCheck['email'])[1];
$dataToCheck['subdomain'] = $form_state->getValue('subdomain'); $dataToCheck['subdomain'] = $form_state->getValue('subdomain');
$dataToCheck['usernameBlacklist'] = $this->settings->get('usernameBlacklist') ?? '';
$dataToCheck['emailProviderBlacklist'] = $this->settings->get('emailProviderBlacklist') ?? '';
$dataToCheck['subdomainBlacklist'] = $this->settings->get('subdomainBlacklist') ?? '';
$response = $this->wisskiCloudAccountManagerDaemonApiActions->checkAccountData($dataToCheck); // Check if username is too short.
if (strlen($dataToCheck['username']) < 2) {
$form_state->setErrorByName('username', $this->t('The username "@username" is too short, please use at least 2 characters.', ['@username' => $dataToCheck['username']]));
}
if (!$response['success']) { // Check if username is in blacklist.
$this->messenger->addError('Can not communicate with the provision daemon, please try again later or write an email to cloud@wiss-ki.eu.'); if (in_array($dataToCheck['username'], preg_split('/\r\n|\r|\n/', $dataToCheck['usernameBlacklist']))) {
}
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'], 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']])); $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']])); // Check if username only contains lowercase letters, numbers, and underscores
if (!preg_match('/^[a-z0-9_]+$/', $dataToCheck['username'])) {
$form_state->setErrorByName('username', t('Username can only contain lowercase letters, numbers, and underscores.'));
} }
if (in_array($dataToCheck['emailProvider'], preg_split('/\r\n|\r|\n/', $this->settings->get('emailProviderBlacklist')))) { // Check if username is unique
$userFromUsername = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties(['name' =>$dataToCheck['username']]);
if (!empty($userFromUsername)) {
$form_state->setErrorByName('username', $this->t('The username is already taken.'));
}
// Check if email provider is in blacklist.
if (in_array($dataToCheck['emailProvider'], preg_split('/\r\n|\r|\n/', $dataToCheck['emailProviderBlacklist']))) {
$form_state->setErrorByName('email', $this->t('The email provider "@provider"is not allowed.', ['@provider' => $dataToCheck['emailProvider']])); $form_state->setErrorByName('email', $this->t('The email provider "@provider"is not allowed.', ['@provider' => $dataToCheck['emailProvider']]));
} }
if ($response['accountData']['accountWithEmail']) { // Check if email is already in use.
$userFromEmail = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties(['name' => $dataToCheck['email']]);
if (!empty($userFromEmail)) {
$form_state->setErrorByName('email', $this->t('The email "@email" is already in use.', ['@email' => $dataToCheck['email']])); $form_state->setErrorByName('email', $this->t('The email "@email" is already in use.', ['@email' => $dataToCheck['email']]));
} }
// Check if subdomain is too short.
if (strlen($dataToCheck['subdomain']) < 3) { if (strlen($dataToCheck['subdomain']) < 3) {
$form_state->setErrorByName('subdomain', $this->t('The subdomain "@subdomain" is too short, please use at least 3 characters.', ['@subdomain' => $dataToCheck['subdomain']])); $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'], preg_split('/\r\n|\r|\n/', $this->settings->get('subdomainBlacklist')))) { // Check if subdomain is in blacklist.
if (in_array($dataToCheck['subdomain'], preg_split('/\r\n|\r|\n/', $dataToCheck['emailProviderBlacklist']))) {
$form_state->setErrorByName('subdomain', $this->t('The subdomain "@subdomain" is not allowed.', ['@subdomain' => $dataToCheck['subdomain']])); $form_state->setErrorByName('subdomain', $this->t('The subdomain "@subdomain" is not allowed.', ['@subdomain' => $dataToCheck['subdomain']]));
} }
if ($response['accountData']['accountWithSubdomain']) {
// Check if subdomain is already in use.
if ($this->wisskiCloudAccountManagerDaemonApiActions->checkForRedundantAccountData('subdomain', $dataToCheck['subdomain'])) {
$form_state->setErrorByName('subdomain', $this->t('The subdomain "@subdomain" is already in use.', ['@subdomain' => $dataToCheck['subdomain']])); $form_state->setErrorByName('subdomain', $this->t('The subdomain "@subdomain" is already in use.', ['@subdomain' => $dataToCheck['subdomain']]));
} }
@ -230,11 +243,8 @@ class WisskiCloudAccountManagerCreateForm extends FormBase {
$account["password"] = $field['password']; $account["password"] = $field['password'];
$account["subdomain"] = $field['subdomain']; $account["subdomain"] = $field['subdomain'];
$accountResponse = $this->wisskiCloudAccountManagerDaemonApiActions->addAccount($account); $user = $this->wisskiCloudAccountManagerDaemonApiActions->addAccount($account);
if (!$accountResponse['success']) { $this->wisskiCloudAccountManagerDaemonApiActions->sendValidationEmail($user['email'], $user['personName'], $user['validationCode']);
throw new Exception('Get no valid response from api daemon. Can not send validiation email.');
};
$this->wisskiCloudAccountManagerDaemonApiActions->sendValidationEmail($accountResponse['data']['email'], $accountResponse['data']['validationCode']);
$this->messenger() $this->messenger()
->addMessage($this->t('The account data has been successfully saved, please check your email for validation!')); ->addMessage($this->t('The account data has been successfully saved, please check your email for validation!'));

View file

@ -0,0 +1,111 @@
<?php
namespace Drupal\wisski_cloud_account_manager\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions;
use Drupal\Core\Url;
class WisskiCloudAccountManagerDeleteForm extends ConfirmFormBase {
/**
* @var array
* The account.
*/
protected array $account;
/**
* @var \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
protected WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'wisski_cloud_account_manager_delete_form';
}
/**
* Class constructor.
*
* @param \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
public function __construct(WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions) {
$this->wisskiCloudAccountManagerDaemonApiActions = $wisskiCloudAccountManagerDaemonApiActions;
$aid = \Drupal::routeMatch()->getParameter('aid');
$this->account = $this->wisskiCloudAccountManagerDaemonApiActions->getAccounts($aid)[0];
}
/**
* Populate the reachable variables from services.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The class container.
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('wisski_cloud_account_manager.daemon_api.actions'),
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Do you really want to delete your WissKI Cloud Instance?');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return Url::fromRoute('wisski_cloud_account_manager.validate')
->setRouteParameter('validationCode', $this->account['validation_code']);
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('This will delete your WissKI Cloud instance. This action cannot be undone. Your Drupal user and WissKI Cloud account will remain.');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Delete');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$form['account_table'] = [
'#type' => 'table',
'#rows' => [
[$this->t('Subdomain'), $this->account['subdomain']],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$aid = \Drupal::routeMatch()->getParameter('aid');
$this->wisskiCloudAccountManagerDaemonApiActions->crudInstance('delete', $aid);
$form_state->setRedirect('wisski_cloud_account_manager.manage');
}
}

View file

@ -0,0 +1,115 @@
<?php
namespace Drupal\wisski_cloud_account_manager\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions;
use Drupal\Core\Url;
class WisskiCloudAccountManagerProvisionForm extends ConfirmFormBase {
/**
* @var \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
protected WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'wisski_cloud_account_manager_provision_form';
}
/**
* Class constructor.
*
* @param \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
public function __construct(WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions) {
$this->wisskiCloudAccountManagerDaemonApiActions = $wisskiCloudAccountManagerDaemonApiActions;
}
/**
* Populate the reachable variables from services.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The class container.
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('wisski_cloud_account_manager.daemon_api.actions'),
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Do you really want to start WissKI Cloud provision?');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return Url::fromRoute('wisski_cloud_account_manager.settings');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('This will start a WissKI Cloud account provision.');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Provise a WissKI Cloud account');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$aid = \Drupal::routeMatch()->getParameter('aid');
$account = $this->wisskiCloudAccountManagerDaemonApiActions->getAccounts($aid)[0];
$form['info'] = [
'#type' => 'table',
'#header' => [
$this->t('Account id'),
$this->t('Account name'),
$this->t('Account email'),
$this->t('Domain'),
],
'#rows' => [
[
$account['aid'],
$account['name'],
$account['mail'],
$account['subdomain'] . '.wisski.cloud',
],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$aid = \Drupal::routeMatch()->getParameter('aid');
$result = $this->wisskiCloudAccountManagerDaemonApiActions->crudInstance('create', $aid);
$form_state->setRedirect('wisski_cloud_account_manager.manage');
}
}

View file

@ -0,0 +1,105 @@
<?php
namespace Drupal\wisski_cloud_account_manager\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions;
use Drupal\Core\Url;
class WisskiCloudAccountManagerPurgeForm extends ConfirmFormBase {
/**
* @var \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
protected WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'wisski_cloud_account_manager_purge_form';
}
/**
* Class constructor.
*
* @param \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
public function __construct(WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions) {
$this->wisskiCloudAccountManagerDaemonApiActions = $wisskiCloudAccountManagerDaemonApiActions;
}
/**
* Populate the reachable variables from services.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The class container.
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('wisski_cloud_account_manager.daemon_api.actions'),
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Do you really want to purge your WissKI Cloud account?');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return Url::fromRoute('wisski_cloud_account_manager.settings');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('This will delete your WissKI Cloud account, your Drupal user, your WissKI Cloud instance and all data associated with it. This action cannot be undone.');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Purge');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$aid = \Drupal::routeMatch()->getParameter('aid');
$account = $this->wisskiCloudAccountManagerDaemonApiActions->getAccounts($aid)[0];
$form['account_table'] = [
'#type' => 'table',
'#rows' => [
[$this->t('Subdomain'), $account['subdomain']],
[$this->t('Account name'), $account['name'] ?: $this->t('Seems that Drupal user has already been deleted, delete the remaining account data and instance.')],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$aid = \Drupal::routeMatch()->getParameter('aid');
$this->wisskiCloudAccountManagerDaemonApiActions->purgeAccount($aid);
$form_state->setRedirect('wisski_cloud_account_manager.manage');
}
}

View file

@ -42,32 +42,39 @@ class WisskiCloudAccountManagerSettingsForm extends ConfigFormBase {
'#default_value' => $config->get('daemonUrl'), '#default_value' => $config->get('daemonUrl'),
]; ];
$form['allAccounts'] = [ $form['wsUrl'] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => $this->t('All accounts URL path'), '#title' => $this->t('The websocket URL of the WissKI Cloud'),
'#description' => $this->t('Provide the endpoint to the GET endpoint for all accounts, i. e. "/account/all"'), '#description' => $this->t('Provide the complete base URL with protocol, domain (resp. service name in docker), ports and API path, i. e. "wss://panel.wisski.cloud/api/v1/ws"'),
'#default_value' => $config->get('allAccounts'), '#default_value' => $config->get('wsUrl'),
]; ];
$form['accountPostUrlPath'] = [ $form['wsToken'] = [
'#type' => 'textfield', '#type' => 'password',
'#title' => $this->t('POST URL path'), '#title' => $this->t('The websocket access token of the WissKI Cloud provisioning account.'),
'#description' => $this->t('Provide the path to the POST endpoint, i. e. "/account"'), '#description' => $this->t('Provide the access token for the websocket, i. e. "1234567890"'),
'#default_value' => $config->get('accountPostUrlPath'), '#default_value' => $config->get('wsToken'),
]; ];
$form['accountFilterByData'] = [ $form['provisionRoute'] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => $this->t('Filter by Data URL path'), '#title' => $this->t('Instance provision URL path'),
'#description' => $this->t('Provide the path to the Get account by data endpoint, i. e. "/account/by_data"'), '#description' => $this->t('Provide the path to the account validation PUT endpoint, i. e. "/provision"'),
'#default_value' => $config->get('accountFilterByData'), '#default_value' => $config->get('provisionRoute'),
]; ];
$form['accountValidation'] = [ $form['deleteRoute'] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => $this->t('User Validation URL path'), '#title' => $this->t('Instance delete URL path'),
'#description' => $this->t('Provide the path to the account validation PUT endpoint, i. e. "/account/validation"'), '#description' => $this->t('Provide the path to the account validation DELETE endpoint, i. e. "/delete"'),
'#default_value' => $config->get('accountValidation'), '#default_value' => $config->get('deleteRoute'),
];
$form['healthCheckRoute'] = [
'#type' => 'textfield',
'#title' => $this->t('Deamon health check URL path'),
'#description' => $this->t('Provide the path to the health check GET endpoint, i. e. "/health-check"'),
'#default_value' => $config->get('healthCheckRoute'),
]; ];
$form['usernameBlacklist'] = [ $form['usernameBlacklist'] = [
@ -121,15 +128,16 @@ class WisskiCloudAccountManagerSettingsForm extends ConfigFormBase {
public function submitForm(array &$form, FormStateInterface $form_state) { public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $this->config('wisski_cloud_account_manager.settings'); $config = $this->config('wisski_cloud_account_manager.settings');
$config->set('daemonUrl', $form_state->getValue('daemonUrl')) $config
->set('accountPostUrlPath', $form_state->getValue('accountPostUrlPath')) ->set('daemonUrl', $form_state->getValue('daemonUrl'))
->set('allAccounts', $form_state->getValue('allAccounts')) ->set('deleteRoute', $form_state->getValue('deleteRoute'))
->set('accountFilterByData', $form_state->getValue('accountFilterByData'))
->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('emailProviderBlacklist', $form_state->getValue('emailProviderBlacklist'))
->set('healthCheckRoute', $form_state->getValue('healthCheckRoute'))
->set('provisionRoute', $form_state->getValue('provisionRoute'))
->set('usernameBlacklist', $form_state->getValue('usernameBlacklist'))
->set('subdomainBlacklist', $form_state->getValue('subdomainBlacklist')) ->set('subdomainBlacklist', $form_state->getValue('subdomainBlacklist'))
->set('wsUrl', $form_state->getValue('wsUrl'))
->set('wsToken', $form_state->getValue('wsToken'))
->save(); ->save();
parent::submitForm($form, $form_state); parent::submitForm($form, $form_state);

View file

@ -0,0 +1,113 @@
<?php
namespace Drupal\wisski_cloud_account_manager\Form;
use Drupal\Core\Form\ConfirmFormBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions;
use Drupal\Core\Url;
class WisskiCloudAccountManagerValidationForm extends ConfirmFormBase {
/**
* @var \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
protected WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'wisski_cloud_account_manager_validation_form';
}
/**
* Class constructor.
*
* @param \Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions
* The WissKi Cloud account manager daemon API actions service.
*/
public function __construct(WisskiCloudAccountManagerDaemonApiActions $wisskiCloudAccountManagerDaemonApiActions) {
$this->wisskiCloudAccountManagerDaemonApiActions = $wisskiCloudAccountManagerDaemonApiActions;
}
/**
* Populate the reachable variables from services.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The class container.
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('wisski_cloud_account_manager.daemon_api.actions'),
);
}
/**
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Do you really want to start WissKI Cloud validation?');
}
/**
* {@inheritdoc}
*/
public function getCancelUrl() {
return Url::fromRoute('wisski_cloud_account_manager.settings');
}
/**
* {@inheritdoc}
*/
public function getDescription() {
return $this->t('This will start a WissKI Cloud account validation.');
}
/**
* {@inheritdoc}
*/
public function getConfirmText() {
return $this->t('Validate a WissKI Cloud account');
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form = parent::buildForm($form, $form_state);
$aid = \Drupal::routeMatch()->getParameter('aid');
$account = $this->wisskiCloudAccountManagerDaemonApiActions->getAccounts($aid)[0];
$form['info'] = [
'#type' => 'table',
'#header' => [
$this->t('Account id'),
$this->t('Account name'),
$this->t('Account email'),
$this->t('Domain'),
],
'#rows' => [
[
$account['aid'],
$account['name'],
$account['mail'],
$account['subdomain'] . '.wisski.cloud',
],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$aid = \Drupal::routeMatch()->getParameter('aid');
$this->wisskiCloudAccountManagerDaemonApiActions->validateAccount($aid);
$form_state->setRedirect('wisski_cloud_account_manager.manage');
}
}

View file

@ -5,13 +5,17 @@ namespace Drupal\wisski_cloud_account_manager;
use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\RequestStack;
use Drupal\Core\Config\Config; use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\Schema\Undefined;
use Drupal\Core\Database\Connection;
use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Mail\MailManagerInterface; use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\Markup; use Drupal\Core\Render\Markup;
use Drupal\Core\Template\TwigEnvironment;
use Drupal\Core\StringTranslation\TranslationInterface; use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\user\Entity\User;
use GuzzleHttp\ClientInterface; use GuzzleHttp\ClientInterface;
/** /**
@ -22,32 +26,19 @@ class WisskiCloudAccountManagerDaemonApiActions {
use DependencySerializationTrait; use DependencySerializationTrait;
/** /**
* The URL path to all account data GET endpoint. * The admin email address.
* *
* @var string * @var string
*/ */
private string $ALL_ACCOUNTS = '/account/all'; private string $ADMIN_EMAIL;
/** /**
* The URL path to the POST endpoint. * The URL path to provision PUT endpoint.
* *
* @var string * @var string
*/ */
private string $ACCOUNT_POST_URL_PART = '/account'; private string $PROVISION_ROUTE;
/**
* The URL path to provision and validation GET endpoint.
*
* @var string
*/
private string $ACCOUNT_PROVISION_AND_VALIDATION_URL_PART;
/**
* The URL path to provision and validation GET endpoint.
*
* @var string
*/
private string $ACCOUNT_VALIDATION_URL_PART = '/account/validation';
/** /**
* The base URL of the WissKI Cloud account manager daemon. * The base URL of the WissKI Cloud account manager daemon.
@ -57,18 +48,34 @@ class WisskiCloudAccountManagerDaemonApiActions {
private string $DAEMON_URL; private string $DAEMON_URL;
/** /**
* The URL path to the filter by account data GET endpoint. * The URL path to delete DELETE endpoint.
* *
* @var string * @var string
*/ */
private string $FILTER_BY_DATA_URL_PART = '/account/by_data'; private string $DELETE_ROUTE;
/** /**
* The settings config. * The database.
* *
* @var \Drupal\Core\Config\Config * @var \Drupal\Core\Database\Connection
*/ */
protected Config $settings; protected Connection $database;
/**
* The Route to the health check GET endpoint.
*
* @var string
*/
private string $HEALTH_CHECK_ROUTE;
/**
* The Route to the info GET endpoint.
*
* @var string
* @todo not yet implemented
*/
private string $INFO_ROUTE = '/info';
/** /**
* The HTTP client. * The HTTP client.
@ -77,6 +84,13 @@ class WisskiCloudAccountManagerDaemonApiActions {
*/ */
protected ClientInterface $httpClient; protected ClientInterface $httpClient;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected LanguageManagerInterface $languageManager;
/** /**
* The logger factory. * The logger factory.
* *
@ -84,6 +98,13 @@ class WisskiCloudAccountManagerDaemonApiActions {
*/ */
protected LoggerChannelFactoryInterface $loggerFactory; protected LoggerChannelFactoryInterface $loggerFactory;
/**
* The mail manager.
*
* @var \Drupal\Core\Mail\MailManagerInterface
*/
protected MailManagerInterface $mailManager;
/** /**
* The messenger service. * The messenger service.
* *
@ -91,20 +112,6 @@ class WisskiCloudAccountManagerDaemonApiActions {
*/ */
protected MessengerInterface $messenger; protected MessengerInterface $messenger;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface
*/
protected LanguageManagerInterface $languageManager;
/**
* The mail manager.
*
* @var \Drupal\Core\Mail\MailManagerInterface
*/
protected MailManagerInterface $mailManager;
/** /**
* The request stack. * The request stack.
* *
@ -112,6 +119,15 @@ class WisskiCloudAccountManagerDaemonApiActions {
*/ */
protected RequestStack $requestStack; protected RequestStack $requestStack;
/**
* The settings config.
*
* @var \Drupal\Core\Config\Config
*/
protected Config $settings;
/** /**
* The string translation service. * The string translation service.
* *
@ -119,61 +135,212 @@ class WisskiCloudAccountManagerDaemonApiActions {
*/ */
protected TranslationInterface $stringTranslation; protected TranslationInterface $stringTranslation;
/**
* The Twig renderer.
*
* @var \Drupal\Core\Template\TwigEnvironment
*/
protected TwigEnvironment $twig;
/** /**
* Class constructor. * Class constructor.
*/ */
public function __construct( public function __construct(
ConfigFactoryInterface $configFactory, ConfigFactoryInterface $configFactory,
Connection $database,
ClientInterface $httpClient, ClientInterface $httpClient,
LanguageManagerInterface $languageManager, LanguageManagerInterface $languageManager,
LoggerChannelFactoryInterface $loggerFactory, LoggerChannelFactoryInterface $loggerFactory,
MessengerInterface $messenger,
MailManagerInterface $mailManager, MailManagerInterface $mailManager,
MessengerInterface $messenger,
RequestStack $requestStack, RequestStack $requestStack,
TranslationInterface $stringTranslation, TranslationInterface $stringTranslation,
TwigEnvironment $twig
) { ) {
// Services from container. // Services from container.
$settings = $configFactory $settings = $configFactory
->getEditable('wisski_cloud_account_manager.settings'); ->getEditable('wisski_cloud_account_manager.settings');
$this->httpClient = $httpClient;
$this->database = $database;
$this->languageManager = $languageManager;
$this->loggerFactory = $loggerFactory;
$this->mailManager = $mailManager;
$this->messenger = $messenger;
$this->requestStack = $requestStack;
$this->settings = $settings; $this->settings = $settings;
$this->stringTranslation = $stringTranslation; $this->stringTranslation = $stringTranslation;
$this->loggerFactory = $loggerFactory; $this->twig = $twig;
$this->messenger = $messenger;
$this->httpClient = $httpClient;
$this->mailManager = $mailManager;
$this->requestStack = $requestStack;
$this->languageManager = $languageManager;
// Set the daemon URL and the URL parts class variables. // 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->DAEMON_URL = $settings->get('daemonUrl') ?: 'http://wisski_cloud_api_daemon_app:2912/wisski-cloud-daemon/api/v1';
$this->ALL_ACCOUNTS = $settings->get('allAccounts') ?: '/account/all'; $this->DELETE_ROUTE = $settings->get('deleteRoute') ?: '/delete';
$this->ACCOUNT_POST_URL_PART = $settings->get('accountPostUrlPath') ?: '/account'; $this->PROVISION_ROUTE = $settings->get('provisionRoute') ?: '/provision';
$this->FILTER_BY_DATA_URL_PART = $settings->get('accountFilterByData') ?: '/account/by_data'; $this->ADMIN_EMAIL = \Drupal::config('system.site')->get('mail');
$this->ACCOUNT_PROVISION_AND_VALIDATION_URL_PART = $settings->get('accountProvisionAndValidationUrlPart') ?: '/account/provision_and_validation'; $this->HEALTH_CHECK_ROUTE= $settings->get('healthCheckRoute') ?: '/health-check';
$this->ACCOUNT_VALIDATION_URL_PART = $settings->get('accountValidationUrlPart') ?: '/account/validation';
} }
/** /**
* Adds a new account to the WissKI Cloud account manager daemon. * Adds a new account to the instance.
*
* First a new Drupal user is created. Additional data is
* stored in the wisski_cloud_account_manager_accounts table.
* *
* @param array $account * @param array $account
* The account to add. * The account to add.
* *
* @return array * @return array
* The response from the daemon (account id with validation code). * The account id with validation code.
*/ */
public function addAccount(array $account): array { public function addAccount(array $account): array {
try { try {
$request = [
'headers' => [ // Get current language.
'Content-Type' => 'application/json', $language = $this->languageManager->getCurrentLanguage()->getId();
], // Create Drupal user object.
'body' => json_encode($account), $user = User::create();
// Mandatory.
$user->setPassword($account['password']);
$user->enforceIsNew();
$user->setEmail($account['email']);
$user->setUsername($account['username']); // This username must be unique and accepts only string. It must be a minimum of two characters and can contain only lowercase letters, numbers, and underscores.
// Optional.
$user->set('init', $account['email']);
$user->set('langcode', $language);
$user->set('preferred_langcode', $language);
$user->set('preferred_admin_langcode', $language);
// Save user.
$result = $user->save();
$validationCode = $this->generateValidationCode();
$database = $this->database;
// Check if a record with this user ID already exists.
$query = $database->select('wisski_cloud_account_manager_accounts', 'w')
->fields('w', ['uid'])
->condition('w.uid', $user->id(), '=');
$result = $query->execute()->fetchField();
if ($result) {
throw new \Exception('A record with this user ID already exists.');
}
$query = $database->insert('wisski_cloud_account_manager_accounts')
->fields([
'uid' => $user->id(),
'person_name' => $account['personName'],
'organisation' => $account['organisation'],
'subdomain' => $account['subdomain'],
'validation_code' => $validationCode,
]);
$query->execute();
return [
'userId' => $user->id(),
'username' => $account['username'],
'personName' => $account['personName'],
'email' => $account['email'],
'subdomain' => $account['subdomain'],
'validationCode' => $validationCode ,
]; ];
$accountPostUrl = $this->DAEMON_URL . $this->ACCOUNT_POST_URL_PART; }
$response = $this->httpClient->post($accountPostUrl, $request); catch (\Exception $e) {
return json_decode($response->getBody() // Request failed, handle the error.
->getContents(), TRUE); $this->loggerFactory
->get('wisski_cloud_account_manager')
->error('Can not create account: ' . $e->getMessage());
$this->messenger
->addError($this->stringTranslation->translate('Error creating account. See logs for details.'));
return [];
}
}
/**
* Check for redundant account data.
*
* @param string $column
* The column to check.
* @param string $value
* The value to check.
*/
public function checkForRedundantAccountData(string $column, string $value) {
$database = \Drupal::database();
$query = $database->select('wisski_cloud_account_manager_accounts', 'w');
$query->fields('w', [$column]);
$query->condition('w.' . $column, $value, '=');
if ($query->execute()->fetchField()) {
return TRUE;
}
else {
return FALSE;
}
}
/**
* Provisions an account in the WissKI Cloud account manager daemon.
*
* @param string $action
* The action to perform: create, delete, get.
* @param int $aid
* The account ID to provision.
*
* @return array
* The response from the daemon.
*/
public function crudInstance($action, $aid) {
try {
$aid = trim($aid);
// Build the query string from the parameters.
$query_string = http_build_query([
'aid' => $aid,
]);
// Determine the route part depending on the action.
switch ($action) {
case 'create':
$restMethod = 'put';
$routePart = $this->PROVISION_ROUTE;
break;
case 'delete':
$restMethod = 'delete';
$routePart = $this->DELETE_ROUTE;
break;
default:
$restMethod = 'get';
$routePart = $this->INFO_ROUTE;
break;
}
// Combine the base URL and the query string.
$request_url = $this->DAEMON_URL . $routePart . '?' . $query_string;
// Send the GET request using the `drupal_http_request()` function.
$response = $this->httpClient->request($restMethod, $request_url);
// Check the response and handle the data accordingly.
if ($response->getStatusCode() == 200 || $response->getStatusCode() == 201) {
// Request successful, handle the data in $response->data.
$resultArray = json_decode($response->getBody()->getContents(), TRUE);
$this->messenger
->addMessage($this->stringTranslation->translate('@message', ['@message' => $resultArray['message']]));
return $resultArray;
}
if ($response->getStatusCode() == 404) {
// Request successful, handle the data in $response->data.
$resultArray = json_decode($response->getBody()->getContents(), TRUE);
$this->messenger
->addError($this->stringTranslation->translate('@message', ['@message' => $resultArray['message']]));
return $resultArray;
}
else {
// Request failed, handle the error.
return [
"message" => 'Request failed with code: ' . $response->getStatusCode(),
"data" => [],
'success' => FALSE,
'error' => $response->getBody()->getContents(),
];
}
} }
catch (\Exception $e) { catch (\Exception $e) {
// Request failed, handle the error. // Request failed, handle the error.
@ -181,60 +348,130 @@ class WisskiCloudAccountManagerDaemonApiActions {
->get('wisski_cloud_account_manager') ->get('wisski_cloud_account_manager')
->error('Request failed with exception: ' . $e->getMessage()); ->error('Request failed with exception: ' . $e->getMessage());
$this->messenger $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.')); ->addError($this->stringTranslation->translate('Can not communicate with the WissKI Cloud account manager daemon. Try again later or contact @email.',
['@email'
=> $this->ADMIN_EMAIL]));
return [ return [
"message" => 'Request failed with exception: ' . $e->getMessage(), "message" => 'Request failed with exception.',
"data" => [ "data" => [],
'email' => NULL,
'validationCode' => NULL,
],
'success' => FALSE, 'success' => FALSE,
'error' => $e->getMessage(),
]; ];
}
} }
}
/** /**
* Check if an account with the given data already exists. * Deletes an account from the WissKI Cloud account manager daemon.
* *
* @param array $dataToCheck * @param int $uid
* The data to check. Possible keys are: * The user ID of the account to delete.
* - email
* - subdomain
* - username.
* *
* @return array * @return array
* The response from the daemon. * The response from the daemon.
*/ */
public function checkAccountData(array $dataToCheck): array { public function deleteAccount(int $aid): array {
try { try {
// Build the query string from the parameters. $database = $this->database;
$query_string = http_build_query($dataToCheck);
// Select the user ID from the accounts table.
$selectQuery = $database->select('wisski_cloud_account_manager_accounts', 'w')
->fields('w', ['uid'])
->condition('w.aid', $aid, '=');
$uid = $selectQuery->execute()->fetchField();
// Delete the account from the accounts table.
$deleteQuery = $database->delete('wisski_cloud_account_manager_accounts')
->condition('aid', $aid, '=');
$deleteQuery->execute();
// Delete the user if exists.
$user = User::load($uid);
$user ? $user->delete() : NULL;
$this->messenger
->addMessage($this->stringTranslation->translate('Account deleted successfully.'));
return [
'message' => 'Account deleted successfully.',
'success' => 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('Something went wrong!' . $e->getMessage()));
}
}
/**
* Generates a random validation code with 32 characters.
*
* @return string
* The generated validation code.
*/
function generateValidationCode() {
// Generate 16 random bytes and convert them to a 32 characters hexadecimal string
$code = bin2hex(random_bytes(16));
return $code;
}
/**
* Query accounts from the WissKI Cloud account manager daemon.
*
* @param int $aid
* The account ID to query.
*
* @return array[aid, name, mail, organisation, person_name, provisioned, status, subdomain, uid, validation_code]
* The accounts response from the daemon.
*/
public function getAccounts($aid = null): array {
try {
$query = $this->database->select('wisski_cloud_account_manager_accounts', 'w');
$query->fields('w', ['aid', 'organisation', 'person_name', 'provisioned', 'subdomain', 'uid', 'validation_code']);
$query->leftjoin('users_field_data', 'u', 'w.uid = u.uid');
$query->fields('u', ['name', 'mail', 'status']);
if ($aid) {
$query->condition('w.aid', $aid, '=');
}
$accounts = $query->execute()->fetchAll(\PDO::FETCH_ASSOC);
return $accounts;
}
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 [];
}
}
/**
* Checks if the WissKI Cloud account manager daemon is available.
*
* @return array[message:string, success:boolean]
*
*/
public function healthCheck() {
try {
// Combine the base URL and the query string. // Combine the base URL and the query string.
$request_url = $this->DAEMON_URL . $this->FILTER_BY_DATA_URL_PART . '?' . $query_string; $request_url = $this->DAEMON_URL . $this->HEALTH_CHECK_ROUTE;
// Send the GET request using the `drupal_http_request()` function. // Send the GET request using the `drupal_http_request()` function.
$response = $this->httpClient->get($request_url); $response = $this->httpClient->request('get', $request_url);
// Check the response and handle the data accordingly. // Check the response and handle the data accordingly.
if ($response->getStatusCode() == 200) { if ($response->getStatusCode() == 200) {
// Request successful, handle the data in $response->data. // Request successful, handle the data in $response->data.
return [ return [
"message" => "Get account data", "message" => "WissKI Cloud account manager daemon is available.",
"accountData" => json_decode($response->getBody()->getContents(),
TRUE)['data'],
'success' => TRUE, 'success' => TRUE,
]; ];
} }
else { else {
// Request failed, handle the error. // Request failed, handle the error.
return [ return [
"message" => 'Request failed with code: ' . $response->getStatusCode(), "message" => 'WissKI Cloud account manager daemon is not available: ' . $response->getStatusCode(),
"accountData" => [
'accountWithUsername' => NULL,
'accountWithEmail' => NULL,
'accountWithSubdomain' => NULL,
],
'success' => FALSE, 'success' => FALSE,
]; ];
} }
@ -243,90 +480,93 @@ class WisskiCloudAccountManagerDaemonApiActions {
// Request failed, handle the error. // Request failed, handle the error.
$this->loggerFactory $this->loggerFactory
->get('wisski_cloud_account_manager') ->get('wisski_cloud_account_manager')
->error('Request failed with exception: ' . $e->getMessage()); ->error('Something went wrong: ' . $e->getMessage());
$this->messenger $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.')); ->addError($this->stringTranslation->translate('Can not communicate with the WissKI Cloud account manager daemon. Try again later or contact @adminMail.',
return [ ['@adminMail'
"message" => 'Request failed with exception: ' . $e->getMessage(), => $this->ADMIN_EMAIL]));
"accountData" => [
'accountWithUsername' => NULL,
'accountWithEmail' => NULL,
'accountWithSubdomain' => NULL,
],
'success' => FALSE,
];
} }
} }
/** /**
* Gets all accounts from the WissKI Cloud account manager daemon. * Purges an account via the WissKI Cloud account manager daemon.
* * Deletes the account from the accounts table and the Drupal user.
* Deletes the instance via the daemon from the WissKI Cloud.
* @param int $aid
* The account ID to purge.
* @return array * @return array
* The accounts response from the daemon. * The response from the daemon.
*/ */
public function getAccounts(): array { public function purgeAccount(int $aid) {
try { try {
// Combine the base URL and the query string. // Get the account ID from the route.
$request_url = $this->DAEMON_URL . $this->ALL_ACCOUNTS;
// Send the GET request using the `drupal_http_request()` function. // @todo Why is there a space in the account ID?
$response = $this->httpClient->get($request_url); $aid = trim($aid);
return json_decode($response->getBody()->getContents(), TRUE);
// Delete the instance via the daemon from the WissKI Cloud.
$response = $this->crudInstance('delete', $aid);
if ($response['success']) {
// Delete the account and Drupal user.
$this->deleteAccount($aid);
$this->messenger
->addMessage($this->stringTranslation->translate('Account purged successfully.'));
return [
'data' => NULL,
'error' => NULL,
'message' => 'Account purged successfully.',
'success' => TRUE,
];
}
else {
if (!$response['error']) {
// No success and no error.
$this->messenger
->addMessage($this->stringTranslation->translate('Account not found or already purged.'));
return [
'data' => NULL,
'error' => NULL,
'message' => $response['message'],
'success' => FALSE,
];
}
else {
// No success and error.
$this->messenger
->addError($this->stringTranslation->translate('Something went wrong: ' . $response['message']));
$this->loggerFactory->get('wisski_cloud_account_manager')->error($response['error']);
return [
'data' => NULL,
'error' => $response['error'],
'message' => 'Something went wrong: ' . $response['message'],
'success' => FALSE,
];
}
}
} }
catch (\Exception $e) { catch (\Exception $e) {
// Request failed, handle the error. // Request failed, handle the error.
$this->loggerFactory $this->loggerFactory
->get('wisski_cloud_account_manager') ->get('wisski_cloud_account_manager')
->error('Request failed with exception: ' . $e->getMessage()); ->error('Request failed with exception: ' . $e->getMessage());
$this->messenger $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.')); ->addError($this->stringTranslation->translate('Something went wrong!' . $e->getMessage()));
return [
"message" => 'Request failed with exception: ' . $e->getMessage(),
"accounts" => [],
'success' => FALSE,
];
} }
} }
/** /**
* Checks the validation status of the given validation code.
*
* @param string $validationCode
* The validation code to check.
*
* @return array
* The account data from the daemon.
*/
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,
];
}
}
/**
* Sends a validation email to the given email address. * Sends a validation email to the given email address.
* *
* @param string $email * @param string $email
* The email address to send the validation email to. * The email address to send the validation email to.
* @param string $personName
* The person name to be used in the validation email.
* @param string $validationCode * @param string $validationCode
* The validation code to be used in the validation link. * The validation code to be used in the validation link.
*/ */
public function sendValidationEmail(string $email, string $validationCode): void { public function sendValidationEmail(string $email, string $personName, string $validationCode): void {
try { try {
$module = 'wisski_cloud_account_manager'; $module = 'wisski_cloud_account_manager';
$key = 'wisski_cloud_account_validation'; $key = 'wisski_cloud_account_validation';
@ -336,17 +576,19 @@ class WisskiCloudAccountManagerDaemonApiActions {
$validationLink = $this->requestStack->getCurrentRequest() $validationLink = $this->requestStack->getCurrentRequest()
->getSchemeAndHttpHost() . '/wisski-cloud-account-manager/validate/' . $validationCode; ->getSchemeAndHttpHost() . '/wisski-cloud-account-manager/validate/' . $validationCode;
$params['message'] = Markup::create($this->stringTranslation->translate('<p>Please validate your account by clicking on this <a href="@validationLink" target="_blank">link</a> or copy this to the address bar of your browser: <p>@validationLink</p>.</p>', ['@validationLink' => $validationLink])); $message = $this->twig->render('@wisski_cloud_account_manager/wisski-cloud-account-manager-validation-email.html.twig', [
$params['subject'] = $this->stringTranslation->translate('WissKI Cloud account validation'); 'personName' => $personName,
$result = $this->mailManager->mail($module, $key, $to, $langcode, $params, NULL, TRUE); 'validationLink' => $validationLink,
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');
$params['subject'] = $this->stringTranslation->translate('WissKI Cloud account validation');
$params['message'] = Markup::create($message);
$result = $this->mailManager->mail($module, $key, $to, $langcode, $params, NULL, TRUE);
if ($result['result'] != TRUE) {
$this->loggerFactory
->get('wisski_cloud_account_manager')
->error('Email sending operation ended with error: ' . $result['message']);
} }
} }
catch (\Exception $e) { catch (\Exception $e) {
@ -360,4 +602,58 @@ class WisskiCloudAccountManagerDaemonApiActions {
} }
/**
* Validates the account.
*
* @param string $validationCode
* The validation code to check.
*
* @return array [uid, name]
*/
public function validateAccount(string $validationCode): array {
try {
$selectQuery = $this->database->select('wisski_cloud_account_manager_accounts', 'w')
->fields('w', ['aid', 'organisation', 'person_name', 'provisioned','subdomain', 'validation_code'])
->condition('w.validation_code', $validationCode, '=');
$selectQuery->join('users_field_data', 'u', 'w.uid = u.uid');
$selectQuery->fields('u', ['uid', 'name', 'mail', 'status']);
$account = $selectQuery->execute()->fetchAll(\PDO::FETCH_ASSOC);
if (isset($account[0]['status'])) {
if ($account[0]['status'] == 0) {
$updateQuery = $this->database->update('users_field_data')
->fields(['status' => 1])
->condition('uid', $account['0']['uid'], '=');
$updateQuery->execute();
$account = $selectQuery->execute()->fetchAll(\PDO::FETCH_ASSOC);
$this->messenger
->addMessage($this->stringTranslation->translate('Account validated successfully.'));
} else {
$this->messenger
->addMessage($this->stringTranslation->translate('Account already validated.'));
}
return [
'uid' => $account[0]['uid'],
'name' => $account[0]['name']];
}
else {
$this->messenger
->addError($this->stringTranslation->translate('Account validation failed. Please contact @adminEmail.',
['@adminEmail'
=> $this->ADMIN_EMAIL]));
return [];
}
}
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 [];
}
}
} }

View file

@ -1,56 +1,88 @@
{# wisski_cloud_account_manager/templates/wisski_cloud_account_manager_account_managing_page.html.twig #} {# wisski_cloud_account_manager/templates/wisski_cloud_account_manager_account_managing_page.html.twig #}
<div> <div>
{% if healthCheck %}
<div>
<p class="{{ healthCheck.success ? 'text-success' : 'text-error' }}">{{ healthCheck.message }}</p>
</div>
{% endif %}
{% if accounts is not empty %} {% if accounts is not empty %}
<table id="wcam--table" class="wcam--table"> <table id="wcam--table" class="table">
<thead>
<tr> <tr>
<th>Id</th> <th scope="col">AID</th>
<th>Person name</th> <th scope="col">UID</th>
<th>Email</th> <th scope="col">Person name</th>
<th>Username</th> <th scope="col">Organisation</th>
<th>Subdomain</th> <th scope="col">Email</th>
<th>Valid</th> <th scope="col">Username</th>
<th>Provisioned</th> <th scope="col">Subdomain</th>
<th>Options</th> <th scope="col">Valid</th>
<th scope="col">Provisioned</th>
<th scope="col">Options</th>
</tr> </tr>
{% for item in accounts.data %} </thead>
<tr class="wcam--table--row"> {% for item in accounts %}
<td class="wcam--row--item-id">{{ item._id }}</td> <tr class="wcam--table--row {% if item.status is null %}table-danger{% endif %}">
<td>{{ item.personName }}</td> <td class="wcam--row--account-id" scope="row"> {{ item.aid }} </td>
<td>{{ item.email }}</td> <td>{{ item.uid }} {% if item.status is null %}<span class="align-bottom material-icons" title="Drupal user does not exist, please contact cloud admin!">error_outline</span>{% endif %}</td>
<td>{{ item.username }}</td> <td>{{ item.person_name }}</td>
<td>{{ item.subdomain }}</td> <td>{{ item.organisation }}</td>
<td>
<a href="mailto:{{ item.mail }}">{{ item.mail }}</a>
</td>
<td>{{ item.name }}</td>
<td>
{% if item.provisioned == 2 %}
<a href="https://{{ item.subdomain }}.wisski.cloud" target="_blank">{{ item.subdomain }}.wisski.cloud</a>
{% else %}
{{ item.subdomain }}.wisski.cloud
{% endif %}
</td>
<td class="valid"> <td class="valid">
{% if item.valid == 1 %} {% if item.status is same as("1") %}
yes yes
{% elseif item.valid == 0 %} {% elseif item.status is same as("0") %}
no no
{% else %} {% else %}
unknown unknown
{% endif %} {% endif %}
</td> </td>
<td class="provisioned">{% if item.provisioned == 1 %} <td id="provision-status--row--aid-{{ item.aid }}" class="provisioned">
ongoing {% if item.provisioned == 1 %}
<div id="process-idle-animation--aid-{{ item.aid }}" class="spinner-border" role="status">
<span id="provision-status--aid-{{ item.aid }}" class="visually-hidden">ongoing</span>
</div>
{% elseif item.provisioned == 2 %} {% elseif item.provisioned == 2 %}
yes yes
{% elseif item.provisioned == 3 %} {% elseif item.provisioned == 0 %}
no no
{% else %} {% else %}
unknown <div id="process-idle-animation--aid-{{ item.aid }}" class="spinner-border" role="status">
<span id="provision-status--aid-{{ item.aid }}" class="visually-hidden">unknown</span>
</div>
{% endif %}</td> {% endif %}</td>
<td><label for="wcam--account-edit--{{ item._id }}"></label> <td><label for="wcam--account-edit--account-{{ item.aid }}"></label>
<select class="wcam--select" name="account-edit" id="wcam--account-edit--{{ item._id }}"> <select class="wcam--select" name="account-edit" id="wcam--account-edit--account-{{ item.aid }}">
<option value="">select...</option> <option value="">select...</option>
<option value="edit">edit</option> {% if item.status is not null %}
{% if item.valid == 0 %} <option title="Edit account data." value="edit">edit</option>
<option value="validate">validate</option> {% if item.status == 0 %}
<option title="Validates the account." value="validate">validate</option>
{% endif %} {% endif %}
{% if item.provisioned == 0 %} {% if item.provisioned == 0 %}
<option value="provise">provise</option> <option title="Create a new WissKI Cloud Instance with provided account data." value="provise">provise</option>
{% endif %} {% endif %}
<option value="delete">delete</option> {% endif %}
{% if item.provisioned >= 2 %}
<option title="Deletes only the WissKI Cloud instance, user and account remain." value="delete">delete</option>
{% endif %}
<option title="Deletes everything, nothing will remain." value="purge">purge</option>
</select></td> </select></td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
{% else %}
<p>No accounts found. Go get some friends.</p>
{% endif %} {% endif %}
</div> </div>

View file

@ -0,0 +1,27 @@
{# wisski_cloud_account_manager/templates/wisski_cloud_account_manager_validation_page.html.twig #}
<div>
<table class="table">
<thead>
<tr>
<th scope="col">Check</th>
<th scope="col">Values</th>
<th scope="col">Test</th>
<th scope="col">Result</th>
</tr>
</thead>
<tbody>
{% if healthCheck %}
<tr>
<td>healthCheck</td>
<td>-</td>
<td>
<form action="/wisski-cloud-account-manager/health-check" method="get">
<button type="submit">Check Health</button>
</form>
</td>
<td id="health-check-result"></td>
</tr>
{% endif %}
</tbody>
</table>
</div>

View file

@ -0,0 +1,10 @@
<div>
<p>Hi {{ personName }},</p>
<p>Thank you for registering to our site.</p>
<p>Please validate your account by clicking on this
<a href="{{ validationLink }}" target="_blank">link</a> or copy this to the address bar of your browser:
</p>
<p>{{ validationLink }}</p>
<p>You have 24 hours to validate your account.</p>
</div>

View file

@ -1,52 +1,58 @@
{# wisski_cloud_account_manager/templates/wisski_cloud_account_manager_validation_page.html.twig #} {# wisski_cloud_account_manager/templates/wisski_cloud_account_manager_validation_page.html.twig #}
<div> <div>
{% if account is not empty %} {% if account is not empty %}
<table class="wisski-cloud-account-manager-table"> <table class="table">
<thead>
<tr> <tr>
<th>Person name</th> <th scope="col">Account ID</th>
<th>Email</th> <th scope="col">Person name</th>
<th>Username</th> <th scope="col">Organisation</th>
<th>Subdomain</th> <th scope="col">Email</th>
<th>Valid</th> <th scope="col">Username</th>
<th>Provisioned</th> <th scope="col">Subdomain</th>
<th scope="col">Valid</th>
<th scope="col">Provisioned</th>
</tr> </tr>
</thead>
<tbody>
<tr> <tr>
<td>{{ account.data.personName }}</td> <th scope="row"> {{ account.aid }} </th>
<td>{{ account.data.email }}</td> <td>{{ account.person_name }}</td>
<td>{{ account.data.username }}</td> <td>{{ account.organisation }}</td>
<td>{{ account.data.subdomain }}</td> <td>{{ account.mail }}</td>
<td>{{ account.name }}</td>
<td>{{ account.subdomain }}</td>
<td class="valid"> <td class="valid">
{% if account.data.valid == 1 %} {% if account.status == 1 %}
yes yes
{% elseif account.data.valid == 0 %} {% elseif account.status == 0 %}
no no
{% else %} {% else %}
unknown unknown
{% endif %} {% endif %}
</td> </td>
<td class="provisioned">{% if account.data.provisioned == 1 %} <td class="provisioned">{% if account.provisioned == 1 %}
ongoing ongoing
{% elseif account.data.provisioned == 2 %} {% elseif account.provisioned == 2 %}
yes yes
{% elseif account.data.provisioned == 3 %} {% elseif account.provisioned == 3 %}
failed
{% else %}
unknown unknown
{% endif %}</td> {% endif %}</td>
</tr> </tr>
</tbody>
</table> </table>
<hr> <hr>
<div class="wisski-cloud-account-manager-center"> <div class="d-flex justify-content-center">
{% if account.data.valid == 1 and account.data.provisioned == 2 %} {% if account.status == 1 and account.provisioned == 2 %}
<p class="wisski-cloud-account-manager-success"><strong> Your account is valid and provisioned. You can now log in to your account at <a href="https://{{ account.data.subdomain }}.wisski.cloud">https://{{ account.data.subdomain }}.wisski.cloud</a>.</strong></p> <p class="text-success"><strong> Your account is valid and provisioned. You can now log in to your account at <a href="https://{{ account.subdomain }}.wisski.cloud">https://{{ account.subdomain }}.wisski.cloud</a>.</strong></p>
{% elseif account.data.valid == 1 and (account.data.provisioned == 0 or account.data.provisioned == 3)%} {% elseif account.status == 1 and (account.provisioned == 0 or account.provisioned == 3)%}
<p class="wisski-cloud-account-manager-error"><strong>Your account is valid but the provision failed or the state is unknown. Please refresh this site and if the state persists contact <a href="mailto:info@wiss-ki.eu">cloud@wiss-ki.eu</a> to resolve this issue.</strong></p> <p class="text-warning"><strong>Your account is valid but the provision failed or the state is unknown. Please refresh this site and if the state persists contact <a href="mailto:info@wiss-ki.eu">cloud@wiss-ki.eu</a> to resolve this issue.</strong></p>
{% elseif account.data.valid == 1 and account.data.provisioned == 1 %} {% elseif account.status == 1 and account.provisioned == 1 %}
<p class="wisski-cloud-account-manager-warning"><strong>Your account is valid and the provision of your WissKI Cloud instance has started. Please wait a few minutes and refresh this page.</strong></p> <p class="text-info"><strong>Your account is valid and the provision of your WissKI Cloud instance has started. Please wait a few minutes and refresh this page.</strong></p>
{% elseif account.data.valid == 0 %} {% elseif account.status == 0 %}
<p class="wisski-cloud-account-manager-error"><strong>Your account is not valid. Please contact <a href="mailto:info@wiss-ki.eu">info@wiss-ki.eu</a> to resolve this issue.</strong></p> <p class="text-warning"><strong>Your account is not valid. Please contact <a href="mailto:info@wiss-ki.eu">info@wiss-ki.eu</a> to resolve this issue.</strong></p>
{% else %} {% else %}
<p class="wisski-cloud-account-manager-error"><strong>Something went wrong. Please contact <a href="mailto:info@wiss-ki.eu">info@wiss-ki.eu</a> to resolve this issue.</strong></p> <p class="text-warning"><strong>Something went wrong. Please contact <a href="mailto:info@wiss-ki.eu">info@wiss-ki.eu</a> to resolve this issue.</strong></p>
{% endif %} {% endif %}
</div> </div>

View file

@ -0,0 +1,61 @@
<?php
/**
* Implements hook_schema().
*/
function wisski_cloud_account_manager_schema() {
$schema['wisski_cloud_account_manager_accounts'] = [
'description' => 'The base table for accounts.',
'fields' => [
'aid' => [
'description' => 'The primary identifier for the account.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
],
'uid' => [
'description' => 'The primary identifier for the account. Same id as user id in drupal.',
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
],
'person_name' => [
'description' => 'The name of the person.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
],
'organisation' => [
'description' => 'The organisation of the person.',
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
],
'subdomain' => [
'description' => 'The subdomain of the account.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
],
'validation_code' => [
'description' => 'The validation code of the account.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
],
'provisioned' => [
'description' => 'The provisioning status of the account. 0 = no, 1 = ongoing, 2 = yes 3 = unknown',
'type' => 'int',
'size' => 'tiny',
'not null' => TRUE,
'default' => 0,
],
],
'primary key' => ['aid'],
'unique keys' => [
'subdomain' => ['subdomain'],
'validation_code' => ['validation_code'],
],
];
return $schema;
}

View file

@ -1,11 +1,20 @@
wisski_cloud_account_manager: globalStyling:
version: 1.x version: 1.x
css: css:
theme: theme:
css/style.css: {} css/style.css: {}
accountOptions:
js: js:
js/accountOptions.js: {} js/accountOptions.js: {}
dependencies: dependencies:
- core/jquery - core/jquery
- core/drupalSettings - core/drupalSettings
- core/Drupal - core/Drupal
provisionStatus:
js:
js/provisionStatus.js: {}
dependencies:
- core/jquery
- core/Drupal

View file

@ -1,3 +1,15 @@
wisski_cloud_account_manager.create_account_page:
title: 'WissKI cloud account manager create account page'
description: 'Create an account for the WissKI cloud'
parent: wisski_cloud_account_manager.settings_menu_block
route_name: wisski_cloud_account_manager.create
wisski_cloud_account_manager.overview_page:
title: 'WissKI cloud account manager overview page'
description: 'WissKI cloud account manager overview page'
parent: wisski_cloud_account_manager.settings_menu_block
route_name: wisski_cloud_account_manager.manage
wisski_cloud_account_manager.settings_menu_block: wisski_cloud_account_manager.settings_menu_block:
title: 'WissKI cloud account manager' title: 'WissKI cloud account manager'
description: 'WissKI cloud account manager' description: 'WissKI cloud account manager'
@ -10,8 +22,3 @@ wisski_cloud_account_manager.settings_form:
parent: wisski_cloud_account_manager.settings_menu_block parent: wisski_cloud_account_manager.settings_menu_block
route_name: wisski_cloud_account_manager.settings route_name: wisski_cloud_account_manager.settings
wisski_cloud_account_manager.overview_page:
title: 'WissKI cloud account manager overview page'
description: 'WissKI cloud account manager overview page'
parent: wisski_cloud_account_manager.settings_menu_block
route_name: wisski_cloud_account_manager.manage

View file

@ -1,8 +1,44 @@
<?php <?php
use Drupal\Core\Mail\MailManagerInterface; /**
use Drupal\Component\Utility\SafeMarkup; * Implements hook_cron().
use Drupal\Component\Utility\Html; */
function wisski_cloud_account_manager_cron() {
$time_limit = \Drupal::time()->getRequestTime() - 24 * 60 * 60;
$ids = \Drupal::entityQuery('user')
->condition('status', 0)
->condition('created', $time_limit, '<')
->accessCheck(TRUE)
->execute();
// Delete rows from the wisski_cloud_account_manager table.
$connection = \Drupal::database();
$wisskiCloudUsers = $connection->select('wisski_cloud_account_manager', 'wcam')
->fields('wcam', ['uid'])
->condition('uid', $ids, 'IN')
->execute()
->fetchAll();
$ids = array_map(function($wisskiCloudUser) {
return $wisskiCloudUser->uid;
}, $wisskiCloudUsers);
if (empty($ids)) {
return;
}
\Drupal::logger('wisski_cloud_account_manager')->notice('Deleting users, who missed the validation: @ids', ['@ids' => implode(', ', $ids)]);
$connection->delete('wisski_cloud_account_manager')
->condition('uid', $ids, 'IN')
->execute();
$storage_handler = \Drupal::entityTypeManager()->getStorage('user');
$entities = $storage_handler->loadMultiple($ids);
$storage_handler->delete($entities);
}
/** /**
* Implements hook_help(). * Implements hook_help().
@ -28,6 +64,7 @@ function wisski_cloud_account_manager_help($route_name, \Drupal\Core\Routing\Rou
return $output; return $output;
} }
/** /**
* Implements hook_mail(). * Implements hook_mail().
*/ */
@ -63,12 +100,22 @@ function wisski_cloud_account_manager_theme($existing, $type, $theme, $path) {
'variables' => ['date' => NULL], 'variables' => ['date' => NULL],
], ],
'wisski_cloud_account_manager_account_managing_page' => [ 'wisski_cloud_account_manager_account_managing_page' => [
'variables' => ['accounts' => NULL], 'variables' => [
'accounts' => NULL,
'healthCheck' => NULL,],
], ],
'wisski_cloud_account_manager_validation_page' => [ 'wisski_cloud_account_manager_validation_page' => [
'variables' => ['account' => NULL], 'variables' => ['account' => NULL],
], ],
'wisski_cloud_account_manager_health_check_page' => [
'variables' => ['healthCheck' => NULL],
],
'wisski_cloud_account_manager_validation_email' => [
'variables' => [
'personName' => NULL,
'validationLink' => NULL,
],
],
]; ];
} }

View file

@ -14,13 +14,45 @@ wisski_cloud_account_manager.create:
requirements: requirements:
_access: 'TRUE' _access: 'TRUE'
wisski_cloud_account_manager.delete:
path: '/wisski-cloud-account-manager/delete/{aid}'
defaults:
_form: '\Drupal\wisski_cloud_account_manager\Form\WisskiCloudAccountManagerDeleteForm'
_title: 'Delete WissKI cloud account'
requirements:
_custom_access: 'wisski_cloud_account_manager.account_route_access_check:access'
wisski_cloud_account_manager.provision_status:
path: '/wisski-cloud-account-manager/provision-status/{aid}'
defaults:
_controller: '\Drupal\wisski_cloud_account_manager\Controller\WisskiCloudAccountManagerController::provisionStatusPage'
_title: 'Provision status'
requirements:
_custom_access: 'wisski_cloud_account_manager.account_route_access_check:access'
wisski_cloud_account_manager.purge:
path: '/wisski-cloud-account-manager/purge/{aid}'
defaults:
_form: '\Drupal\wisski_cloud_account_manager\Form\WisskiCloudAccountManagerPurgeForm'
_title: 'Purge WissKI cloud account and WissKI instance'
requirements:
_custom_access: 'wisski_cloud_account_manager.account_route_access_check:access'
wisski_cloud_account_manager.manage: wisski_cloud_account_manager.manage:
path: '/wisski-cloud-account-manager/manage' path: '/wisski-cloud-account-manager/manage'
defaults: defaults:
_controller: '\Drupal\wisski_cloud_account_manager\Controller\WisskiCloudAccountManagerController::accountManagingPage' _controller: '\Drupal\wisski_cloud_account_manager\Controller\WisskiCloudAccountManagerController::accountManagingPage'
_title: 'Account managing page' _title: 'Account managing page'
requirements: requirements:
_permission: 'admister wisski cloud account manager' _permission: 'access content'
wisski_cloud_account_manager.provise:
path: '/wisski-cloud-account-manager/provise/{aid}'
defaults:
_form: '\Drupal\wisski_cloud_account_manager\Form\WisskiCloudAccountManagerProvisionForm'
_title: 'Provision WissKI cloud account'
requirements:
_custom_access: 'wisski_cloud_account_manager.account_route_access_check:access'
wisski_cloud_account_manager.settings: wisski_cloud_account_manager.settings:
path: '/admin/config/wisski-cloud-account-manager/settings' path: '/admin/config/wisski-cloud-account-manager/settings'
@ -42,7 +74,7 @@ wisski_cloud_account_manager.validate:
path: '/wisski-cloud-account-manager/validate/{validationCode}' path: '/wisski-cloud-account-manager/validate/{validationCode}'
defaults: defaults:
_controller: '\Drupal\wisski_cloud_account_manager\Controller\WisskiCloudAccountManagerController::validationPage' _controller: '\Drupal\wisski_cloud_account_manager\Controller\WisskiCloudAccountManagerController::validationPage'
_title: 'Check validation and provision of your WissKI Cloud account' _title: ' WissKI Cloud account validation'
requirements: requirements:
_access: 'TRUE' _access: 'TRUE'
token: '[a-zA-Z0-9]+' token: '[a-zA-Z0-9]+'

View file

@ -3,11 +3,19 @@ services:
class: Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions class: Drupal\wisski_cloud_account_manager\WisskiCloudAccountManagerDaemonApiActions
arguments: arguments:
- '@config.factory' - '@config.factory'
- '@database'
- '@http_client' - '@http_client'
- '@language_manager' - '@language_manager'
- '@logger.factory' - '@logger.factory'
- '@messenger'
- '@plugin.manager.mail' - '@plugin.manager.mail'
- '@messenger'
- '@request_stack' - '@request_stack'
- '@string_translation' - '@string_translation'
- '@twig'
wisski_cloud_account_manager.account_route_access_check:
class: Drupal\wisski_cloud_account_manager\Access\AccountRouteAccessCheck
arguments:
- '@current_user'
- '@database'
tags:
- { name: access_check, applies_to: _account_route_access_check }