microspid-8.x-1.0-beta12/src/Form/BasicSettingsForm.php
src/Form/BasicSettingsForm.php
<?php
namespace Drupal\microspid\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\AppendCommand;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Link;
use Drupal\Core\Url;
use \Drupal\block\entity\Block;
/**
* Form builder for the microspid basic settings form.
*/
class BasicSettingsForm extends ConfigFormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'microspid_basic_settings_form';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return ['microspid.settings'];
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
global $base_url;
$config = $this->config('microspid.settings');
$certman = \Drupal::service('microspid.certs.manager');
$form['basic'] = [
'#type' => 'fieldset',
'#title' => $this->t('Basic settings'),
'#collapsible' => FALSE,
];
$desc = $this->t('Click to activate SPID. ');
$exists = $certman->certExists();
if (!$exists) {
$desc .= Link::fromTextAndUrl($this->t('Create certificate'), Url::fromUri('internal:/admin/config/people/microspid/create_cert'))->toString();
}
$form['basic']['activate'] = [
'#type' => 'checkbox',
'#title' => $this->t('Activate authentication via MicroSPID'),
'#default_value' => $config->get('activate'),
'#description' => $desc,
];
$form['basic']['spid-button'] = [
'#type' => 'checkbox',
'#title' => $this->t('Attiva bottone SPID'),
'#default_value' => FALSE,
'#description' => $this->t('Selezionando questa casella verrà attivato il bottone SPID se non già presente.'),
];
$form['basic']['authlevel'] = array(
'#type' => 'radios',
'#title' => $this->t('Livello di autenticazione SPID'),
'#default_value' => $config->get('authlevel'),
'#description' => $this->t('Autenticazione livello 1 o 2'),
'#options' => array(
'SpidL1' => 'Livello 1',
'SpidL2' => 'Livello 2',
),
);
$form['basic']['index'] = array(
'#type' => 'textfield',
'#attributes' => array(
// Insert space before attribute name :)
' type' => 'number',
),
'#title' => $this->t('Service index'),
'#default_value' => $config->get('index'),
'#maxlength' => 2,
'#size' => 2,
'#description' => $this->t('Specify service index'),
);
$defval = @$config->get('entityid');
if (empty($defval)) $defval = $base_url . '/microspid_metadata';
$form['basic']['entityid'] = array(
'#type' => 'textfield',
'#title' => $this->t('SP Entity ID'),
'#default_value' => $defval,
'#description' => $this->t('If service is 0 leave unchanged else use service 0 value'),
);
$form['basic']['servicedir'] = array(
'#type' => 'textfield',
'#title' => $this->t('Service path'),
'#default_value' => $config->get('servicedir'),
'#description' => $this->t('Service path/alias'),
);
$form['basic']['privatepath'] = array(
'#type' => 'textfield',
'#title' => $this->t('private key path'),
'#default_value' => $config->get('privatepath'),
'#description' => $this->t('private key path, only if you change it'),
'#element_validate' => array(array($this, 'validatePrivatepath')),
);
$form['basic']['header_no_cache'] = [
'#type' => 'checkbox',
'#title' => $this->t('Use Header with: Cache-Control: no-cache'),
'#default_value' => $config->get('header_no_cache'),
'#description' => $this->t('Use a "Cache-Control: no-cache" header in the HTTP response to avoid the redirection be cached (e.g. when using a reverse-proxy layer).'),
];
$form['basic']['single_logout'] = [
'#type' => 'checkbox',
'#title' => $this->t('Do Single Logout'),
'#default_value' => $config->get('single_logout'),
'#description' => $this->t('Enable Single Logout (only for level 1).'),
];
$form['basic']['show_agid_link'] = [
'#type' => 'checkbox',
'#title' => t('mostrare idp Agid di Test'),
'#default_value' => $config->get('show_agid_link'),
'#description' => t('Va attivato per i controlli da parte di Agid.'),
];
$form['basic']['test_mode'] = [
'#type' => 'checkbox',
'#title' => $this->t('Test mode'),
'#default_value' => $config->get('test_mode'),
'#description' => $this->t('Use testing idp.'),
];
$form['debugging'] = [
'#type' => 'fieldset',
'#title' => $this->t('Debugging'),
'#collapsible' => FALSE,
];
$form['debugging']['debug'] = [
'#type' => 'checkbox',
'#title' => $this->t('Turn on debugging messages'),
'#default_value' => $config->get('debug'),
'#description' => $this->t('Expand the level of Drupal logging to include debugging information.'),
];
$form['user_provisioning'] = [
'#type' => 'fieldset',
'#title' => $this->t('User Provisioning'),
'#collapsible' => FALSE,
];
$form['user_provisioning']['register_users'] = [
'#type' => 'checkbox',
'#title' => $this->t('Register users (i.e., auto-provisioning)'),
'#default_value' => $config->get('register_users'),
'#description' => $this->t('Determines whether or not the module should automatically create/register new Drupal accounts for users that authenticate using MicroSPiD. Unless you\'ve done some custom work to provision Drupal accounts with the necessary authmap entries you will want this checked.<br /><br />NOTE: If unchecked each user must already have been provisioned a Drupal account correctly linked to the SAML authname attribute (e.g. by creating Drupal users with "Enable this user to leverage SAML authentication" checked). Otherwise they will receive a notice and be denied access.'),
];
$form['metadata'] = array(
// Fieldset.
'#type' => 'fieldset',
'#title' => $this->t('Spid IdP metadata'),
'#collapsible' => FALSE,
);
$form['metadata']['microspid_update'] = array(
'#type' => 'button',
'#default_value' => $this->t('Update IDPs metadata'),
'#ajax' => array(
'callback' => array($this, 'updateIDPs'),
'progress' => array('type' => 'throbber'),
),
);
$form['multi'] = array(
// Fieldset.
'#type' => 'details',
'#title' => $this->t('Multi portal setup'),
);
if ($config->get('index') > 0) {
$form['multi']['#attributes'] = array('style' => 'display:none');
}
$form['multi']['metadata_population'] = array(
'#type' => 'textarea',
'#title' => $this->t('Metadata generation for multi-portal'),
'#default_value' => $this->getMulti(),
'#description' => $this->t("A list of <strong>other</strong> services, one per line, in the form index|acs-location|service-name|service-description|extra-attributes. Default attributes are spidCode,name,familyName,fiscalNumber,email. Service 0 is not listed, so index starts from 1. Extra attributes are separated by colon. Example:<br /><em>1|https://miosito.it/cartella/microspid_acs|service-2|Service number 2|companyName:ivaCode</em>"),
);
$form['multi']['update_metadata'] = array(
'#type' => 'button',
'#default_value' => $this->t('Save infos as metadata'),
'#ajax' => array(
'callback' => array($this, 'updateMyMetadata'),
'progress' => array('type' => 'none'),
),
);
$class = 'confirm-regenerate';
$form['multi']['update_metadata']['#attributes']['class'][] = $class;
$form['multi']['#attached']['library'][] = 'microspid/ajax-confirm';
$form['multi']['#attached']['drupalSettings']['ajaxConfirm'][$class] = [
'text' => $this->t('WARNING: your metadata will be regenerated! Are you sure?'),
];
return parent::buildForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$config = $this->config('microspid.settings');
$config->set('activate', $form_state->getValue('activate'));
$config->set('index', $form_state->getValue('index'));
$config->set('entityid', $form_state->getValue('entityid'));
$config->set('servicedir', $form_state->getValue('servicedir'));
$config->set('privatepath', $form_state->getValue('privatepath'));
$config->set('authlevel', $form_state->getValue('authlevel'));
$config->set('show_agid_link', $form_state->getValue('show_agid_link'));
$config->set('single_logout', $form_state->getValue('single_logout'));
$config->set('test_mode', $form_state->getValue('test_mode'));
$config->set('debug', $form_state->getValue('debug'));
$config->set('register_users', $form_state->getValue('register_users'));
$config->set('header_no_cache', $form_state->getValue('header_no_cache'));
$config->save();
if ($form_state->getValue('spid-button')) {
$this->saveBlock();
}
Cache::invalidateTags(['rendered']);
}
/**
* Download IDP metadata from Agid server
*/
public function download() {
$file = drupal_get_path('module', 'microspid') . '/metadata/spid-entities-idps.xml';
$bak = $file . '.bak';
$url = 'https://registry.spid.gov.it/metadata/idp/spid-entities-idps.xml';
if (!@copy($file, $bak)) {
return FALSE;
}
$success = @copy($url, $file);
if (!$success && !file_exists($file)) {
rename($bak, $file);
}
@unlink($bak);
return $success;
}
/**
Detects the end-of-line character of a string.
@param string $str The string to check.
@return string The detected EOL, or default one.
*/
public function detectEOL($str) {
static $eols = array(
0 => "\n\r", // 0x0A - 0x0D - acorn BBC
1 => "\r\n", // 0x0D - 0x0A - Windows, DOS OS/2
2 => "\n", // 0x0A - - Unix, OSX
3 => "\r", // 0x0D - - Apple ][, TRS80
);
$curCount = 0;
$curEol = "\n";
foreach($eols as $k => $eol) {
if( ($count = substr_count($str, $eol)) > $curCount) {
$curCount = $count;
$curEol = $eol;
}
}
return $curEol;
} // detectEOL
/**
* converts from config textarea string to metadata template (saving it)
* @method setMulti
* @param $input string from configuration textarea 'microspid_metadata_population'
*
*/
public function setMulti($input) {
$spid = \Drupal::service('microspid.manager');
$sep = $this->detectEOL($input);
$lines = explode($sep, $input);
$data = array();
foreach($lines as $line) {
if (empty($line)) {
continue;
}
$data[] = explode('|', $line);
}
$md = $spid->loadMetadata('/templates/metadata.tpl.xml');
$metadata = $md->children("urn:oasis:names:tc:SAML:2.0:metadata");
$move_me = dom_import_simplexml($metadata->SPSSODescriptor->AttributeConsumingService);
$parent = $move_me->parentNode;
$move_me = $parent->removeChild($move_me);
foreach ($data as $datum) {
if (empty($datum)) {
continue;
}
$tmp = $metadata->SPSSODescriptor->addChild('AssertionConsumerService');
$tmp->addAttribute('Binding', "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
$tmp->addAttribute('Location', $datum[1]);
$tmp->addAttribute('index', $datum[0]);
}
$parent->appendChild($move_me);
foreach ($data as $datum) {
if (empty($datum)) {
continue;
}
$tmp = $metadata->SPSSODescriptor->addChild('AttributeConsumingService');
$tmp->addAttribute('index', $datum[0]);
$name = $tmp->addChild('ServiceName', $datum[2]);
$name->addAttribute('xml:lang','it','xml');
$desc = $tmp->addChild('ServiceDescription', $datum[3]);
$desc->addAttribute('xml:lang','it','xml');
$extras = explode(':', $datum[4]);
$defs = ['spidCode','fiscalNumber','name','familyName','email'];
foreach($defs as $def) {
$attr = $tmp->addChild('RequestedAttribute');
$attr->addAttribute('Name', $def);
$attr->addAttribute('NameFormat', 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic');
}
foreach($extras as $extra) {
if (empty($extra)) {
continue;
}
$attr = $tmp->addChild('RequestedAttribute');
$attr->addAttribute('Name', $extra);
$attr->addAttribute('NameFormat', 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic');
}
}
$destname = \Drupal::service('file_system')->realpath('public://microspid/metadata.xml');
copy($destname, $destname . '.bak'); // make a backup
$string = $md->asXML();
$doc = new \DOMDocument();
$doc->preserveWhiteSpace = FALSE;
$doc->formatOutput = TRUE;
$doc->loadXML($string);
$doc->save($destname);
}
/**
* converts from from metadata to config textarea string
* @method getMulti
* @return string to be used in configuration textarea 'microspid_metadata_population'
*
*/
public function getMulti() {
$spid = \Drupal::service('microspid.manager');
$array1 = $array2 = array();
$md = $spid->loadMetadata('/metadata.xml', TRUE);
$metadata = $md->children("urn:oasis:names:tc:SAML:2.0:metadata");
foreach($metadata->SPSSODescriptor->AssertionConsumerService as $acs) {
$array1[] = $acs->attributes()['index'] . '|' . $acs->attributes()['Location'];
}
foreach($metadata->SPSSODescriptor->AttributeConsumingService as $service) {
$extra = array();
$def = ['spidCode','fiscalNumber','name','familyName','email'];
foreach($service->RequestedAttribute as $attr) {
$name = $attr->attributes()['Name'];
if (in_array($name, $def)) {
continue;
}
$extra[] = $name;
}
$extra_attr = implode (':', $extra);
$array2[] = $service->ServiceName . '|' . $service->ServiceDescription . '|' . $extra_attr;
}
for ($i = 1; $i < count($array1); $i++) {
$array1[$i] .= '|' . $array2[$i];
}
array_shift($array1);
return implode("\n", $array1);
}
/**
* Ajax callback function.
*/
public function updateIDPs($form, &$form_state) : AjaxResponse {
$success = $this->download();
$result = $success ? $this->t('IDP metadata have been updated') : $this->t('Error: IDP metadata NOT updated!');
$response = new AjaxResponse();
$response->addCommand(new AppendCommand('body', '<script>alert("'.$result.'");</script>'));
return $response;
}
/**
* Ajax callback function.
*/
public function updateMyMetadata($form, &$form_state) : AjaxResponse {
$input = $form_state->getValue('metadata_population');
$this->setMulti($input);
$result = $this->t('Your metadata have been generated.');
$response = new AjaxResponse();
$response->addCommand(new AppendCommand('body', '<script>alert("'.$result.'");</script>'));
return $response;
}
public function validatePrivatepath($element, &$form_state, $form) {
$filepath = $element['#value'];
if (!empty($filepath) && !file_exists($filepath)) {
$form_state->setError($element, $this->t("Folder %filepath doesn't exist", array('%filepath' => $filepath)));
}
}
public function saveBlock() {
$blockEntityManager = \Drupal::service('entity.manager')->getStorage('block');
$theme = \Drupal::config('system.theme')->get('default');
$plugin_id = 'microspid_block';
$vis_config =
[
'request_path' =>
[
'id' => 'request_path',
'pages' => '/user/login',
'negate' => 0,
'context_mapping' => [],
]
];
$my_block = Block::load('spidauthstatus');
if ($my_block && $my_block->getTheme() == $theme) {
return;
}
if ($my_block) {
$my_block->delete();
}
$my_block = $blockEntityManager->create(
array(
'id'=> 'spidauthstatus',
'plugin' => $plugin_id,
'settings' => [
'id' => 'microspid_block',
'label' => $this->t('Otherwise'),
'provider' => 'microspid',
'label_display' => 'visible',
],
'region' => 'content',
'theme' => $theme,
'visibility' => $vis_config,
'weight' => 100,
)
);
$my_block->save();
}
}
