View source
<?php
namespace Drupal\samlauth_user_fields\Form;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\samlauth\Controller\SamlController;
use Drupal\samlauth_user_fields\EventSubscriber\UserFieldsEventSubscriber;
use Symfony\Component\DependencyInjection\ContainerInterface;
class SamlauthMappingEditForm extends FormBase {
const MAP_FIELD_TYPES = [
'address',
'boolean',
'email',
'float',
'integer',
'language',
'link',
'list_float',
'list_integer',
'list_string',
'string',
'string_long',
'telephone',
'timestamp',
];
const PREVENT_MAP_FIELDS = [
'name',
'mail',
'uid',
'status',
'access',
'login',
'init',
'langcode',
'default_langcode',
];
protected $entityFieldManager;
public function __construct(EntityFieldManagerInterface $entity_field_manager) {
$this->entityFieldManager = $entity_field_manager;
}
public static function create(ContainerInterface $container) {
return new static($container
->get('entity_field.manager'));
}
public function getFormId() {
return 'samlauth_user_fields_edit_form';
}
public function buildForm(array $form, FormStateInterface $form_state, $mapping_id = NULL) {
$user_fields = $this->entityFieldManager
->getFieldDefinitions('user', 'user');
$mappings = $this
->configFactory()
->get(UserFieldsEventSubscriber::CONFIG_OBJECT_NAME)
->get('field_mappings');
$form['attribute_name'] = [
'#type' => 'textfield',
'#title' => $this
->t('SAML Attribute'),
'#description' => $this
->t('The name of the SAML attribute you want to sync to the user profile.'),
'#required' => TRUE,
'#default_value' => $mappings[$mapping_id]['attribute_name'] ?? NULL,
];
$options = [
'' => $this
->t('- Select -'),
];
foreach ($user_fields as $name => $field) {
if (in_array($field
->getType(), static::MAP_FIELD_TYPES, TRUE) && !in_array($name, static::PREVENT_MAP_FIELDS, TRUE)) {
$subfields = $this
->getSubFields($field);
$label = $field
->getLabel();
if ($subfields) {
foreach ($subfields as $sub_name => $sub_label) {
$options["{$name}:{$sub_name}"] = "{$label}: {$sub_label}";
}
}
else {
$options[$name] = $label;
}
}
}
$field_name = NULL;
if ($mapping_id !== NULL) {
$field_name = $mappings[$mapping_id]['field_name'];
if (!isset($options[$field_name])) {
$this
->messenger()
->addError('Currently mapped user field %name is unknown. Saving this form will change the mapping.', [
'%name' => $field_name,
]);
$field_name = NULL;
}
}
$form['field_name'] = [
'#type' => 'select',
'#title' => $this
->t('User Field'),
'#description' => $this
->t('The user field you want to sync this attribute to.'),
'#required' => TRUE,
'#options' => $options,
'#default_value' => $field_name,
];
if ($this
->config(SamlController::CONFIG_OBJECT_NAME)
->get('map_users')) {
$form['link_user_order'] = [
'#type' => 'number',
'#size' => 2,
'#title' => $this
->t('Link user?'),
'#description' => $this
->t("Provide a value here if a first login should attempt to match an existing non-linked Drupal user on the basis of this field's value. The exact value only matters when multiple link attempts are defined (to determine order of attempts and/or combination with other fields). See the help text with the list for more info.") . '<br><em>' . $this
->t('Warning: if this attribute can be changed by the IdP user, this has security implications; it enables a user to influence which Drupal user they take over.') . '</em>',
'#default_value' => $mappings[$mapping_id]['link_user_order'] ?? NULL,
];
}
$form['mapping_id'] = [
'#type' => 'value',
'#value' => $mapping_id,
];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this
->t('Submit'),
];
return $form;
}
public function validateForm(array &$form, FormStateInterface $form_state) {
if (strpos($form_state
->getValue('field_name'), ':') !== FALSE && !in_array($form_state
->getValue('link_user_order'), [
'',
NULL,
], TRUE)) {
$form_state
->setErrorByName('link_user_order', $this
->t("Linking by a 'sub field' is not currently supported."));
}
$mappings = $this
->configFactory()
->get(UserFieldsEventSubscriber::CONFIG_OBJECT_NAME)
->get('field_mappings');
if (is_array($mappings)) {
$our_mapping_id = $form_state
->getValue('mapping_id');
$our_match_id = $form_state
->getValue('link_user_order');
foreach ($mappings as $mapping_id => $mapping) {
if ($mapping_id != $our_mapping_id || $our_mapping_id === '') {
if ($our_match_id !== '' && isset($mapping['link_user_order']) && $our_match_id == $mapping['link_user_order'] && $mapping['field_name'] === $form_state
->getValue('field_name')) {
$form_state
->setErrorByName('field_name', $this
->t("This user field is already used for the same 'Link' value."));
}
if (($our_match_id === '' || !isset($mapping['link_user_order']) || $our_match_id == $mapping['link_user_order']) && $mapping['field_name'] === $form_state
->getValue('field_name') && $mapping['attribute_name'] === $form_state
->getValue('attribute_name')) {
$form_state
->setErrorByName('field_name', $this
->t('This SAML attribute has already been mapped to this field.'));
}
}
}
}
}
public function submitForm(array &$form, FormStateInterface $form_state) {
$config = $this
->configFactory()
->getEditable(UserFieldsEventSubscriber::CONFIG_OBJECT_NAME);
$mappings = $config
->get('field_mappings');
$new_mapping = [
'attribute_name' => $form_state
->getValue('attribute_name'),
'field_name' => $form_state
->getValue('field_name'),
'link_user_order' => $form_state
->getValue('link_user_order'),
];
$mapping_id = $form_state
->getValue('mapping_id');
if (!is_array($mappings)) {
$mappings = [];
}
if ($mapping_id !== NULL) {
$mappings[$mapping_id] = $new_mapping;
}
else {
$mappings[] = $new_mapping;
}
$config
->set('field_mappings', $mappings)
->save();
$form_state
->setRedirect('samlauth_user_fields.list');
}
private function getSubFields(FieldDefinitionInterface $field) {
if ($field
->getType() !== 'address') {
return [];
}
$storage_definition = $field
->getFieldStorageDefinition();
$property_definitions = $storage_definition
->getPropertyDefinitions();
$schema = $storage_definition
->getSchema();
$columns = array_filter($schema['columns'], function ($column) {
return ($column['type'] ?? NULL) === 'varchar';
});
$subfields = [];
if ($columns) {
foreach (array_keys($columns) as $column_name) {
if (isset($property_definitions[$column_name]) && $property_definitions[$column_name] instanceof DataDefinition) {
$subfields[$column_name] = $property_definitions[$column_name]
->getLabel();
}
else {
$subfields[$column_name] = $column_name;
}
}
}
return $subfields;
}
}