View source
<?php
namespace Drupal\ldap_user\Processor;
use Drupal\ldap_servers\Entity\Server;
use Drupal\ldap_servers\Helper\ConversionHelper;
use Drupal\ldap_servers\Processor\TokenProcessor;
use Drupal\ldap_user\Exception\LdapBadParamsException;
use Drupal\ldap_user\Helper\LdapConfiguration;
use Drupal\ldap_servers\LdapUserAttributesInterface;
use Drupal\ldap_user\Helper\SyncMappingHelper;
use Drupal\user\Entity\User;
class LdapUserProcessor implements LdapUserAttributesInterface {
private $config;
private $detailLog;
protected $tokenProcessor;
public function __construct() {
$this->config = \Drupal::config('ldap_user.settings')
->get();
$this->detailLog = \Drupal::service('ldap.detail_log');
$this->tokenProcessor = \Drupal::service('ldap.token_processor');
}
public function syncToLdapEntry(User $account, array $ldapUser = [], $testQuery = FALSE) {
if (is_object($account) && $account
->id() == 1) {
return FALSE;
}
$result = FALSE;
if ($this->config['ldapEntryProvisionServer']) {
$server = Server::load($this->config['ldapEntryProvisionServer']);
$params = [
'direction' => self::PROVISION_TO_LDAP,
'prov_events' => [
self::EVENT_SYNC_TO_LDAP_ENTRY,
],
'module' => 'ldap_user',
'function' => 'syncToLdapEntry',
'include_count' => FALSE,
];
try {
$proposedLdapEntry = $this
->drupalUserToLdapEntry($account, $server, $params, $ldapUser);
} catch (\Exception $e) {
\Drupal::logger('ldap_user')
->error('Unable to prepare LDAP entry: %message', [
'%message',
$e
->getMessage(),
]);
return FALSE;
}
if (is_array($proposedLdapEntry) && isset($proposedLdapEntry['dn'])) {
$attributes = [];
foreach ($proposedLdapEntry as $attributeName => $attributeValues) {
if ($attributeName != 'dn') {
if (isset($attributeValues['count'])) {
unset($attributeValues['count']);
}
if (count($attributeValues) == 1) {
$attributes[$attributeName] = $attributeValues[0];
}
else {
$attributes[$attributeName] = $attributeValues;
}
}
}
if ($testQuery) {
$proposedLdapEntry = $attributes;
$result = [
'proposed' => $proposedLdapEntry,
'server' => $server,
];
}
else {
$proposedDnLowerCase = mb_strtolower($proposedLdapEntry['dn']);
$ldap_entries = [
$proposedDnLowerCase => $attributes,
];
$context = [
'action' => 'update',
'corresponding_drupal_data' => [
$proposedDnLowerCase => $attributes,
],
'corresponding_drupal_data_type' => 'user',
'account' => $account,
];
\Drupal::moduleHandler()
->alter('ldap_entry_pre_provision', $ldap_entries, $server, $context);
$attributes = $ldap_entries[$proposedDnLowerCase];
$result = $server
->modifyLdapEntry($proposedLdapEntry['dn'], $attributes);
if ($result) {
\Drupal::moduleHandler()
->invokeAll('ldap_entry_post_provision', [
$ldap_entries,
$server,
$context,
]);
}
}
}
else {
$result = FALSE;
}
}
$tokens = [
'%dn' => isset($proposedLdapEntry['dn']) ? $proposedLdapEntry['dn'] : 'null',
'%sid' => $this->config['ldapEntryProvisionServer'],
'%username' => $account
->getAccountName(),
'%uid' => !method_exists($account, 'id') || empty($account
->id()) ? '' : $account
->id(),
'%action' => $result ? t('synced') : t('not synced'),
];
\Drupal::logger('ldap_user')
->info('LDAP entry on server %sid %action dn=%dn for username=%username, uid=%uid', $tokens);
return $result;
}
public function drupalUserToLdapEntry(User $account, Server $ldap_server, array $params, $ldapUserEntry = NULL) {
$provision = isset($params['function']) && $params['function'] == 'provisionLdapEntry';
if (!$ldapUserEntry) {
$ldapUserEntry = [];
}
if (!is_object($account) || !is_object($ldap_server)) {
throw new LdapBadParamsException('Missing user or server.');
}
$include_count = isset($params['include_count']) && $params['include_count'];
$direction = isset($params['direction']) ? $params['direction'] : self::PROVISION_TO_ALL;
$prov_events = empty($params['prov_events']) ? LdapConfiguration::getAllEvents() : $params['prov_events'];
$syncMapper = new SyncMappingHelper();
$mappings = $syncMapper
->getSyncMappings($direction, $prov_events);
foreach ($mappings as $field_key => $field_detail) {
list($ldapAttributeName, $ordinal) = $this
->extractTokenParts($field_key);
$ordinal = !$ordinal ? 0 : $ordinal;
if ($ldapUserEntry && isset($ldapUserEntry[$ldapAttributeName]) && is_array($ldapUserEntry[$ldapAttributeName]) && isset($ldapUserEntry[$ldapAttributeName][$ordinal])) {
continue;
}
$synced = $syncMapper
->isSynced($field_key, $params['prov_events'], self::PROVISION_TO_LDAP);
if ($synced) {
$token = $field_detail['user_attr'] == 'user_tokens' ? $field_detail['user_tokens'] : $field_detail['user_attr'];
$value = $this->tokenProcessor
->tokenReplace($account, $token, 'user_account');
if (substr($token, 0, 10) == '[password.' && (!$value || $value == $token)) {
if (!$provision) {
continue;
}
}
if ($ldapAttributeName == 'dn' && $value) {
$ldapUserEntry['dn'] = $value;
}
elseif ($value) {
if (!isset($ldapUserEntry[$ldapAttributeName]) || !is_array($ldapUserEntry[$ldapAttributeName])) {
$ldapUserEntry[$ldapAttributeName] = [];
}
$ldapUserEntry[$ldapAttributeName][$ordinal] = $value;
if ($include_count) {
$ldapUserEntry[$ldapAttributeName]['count'] = count($ldapUserEntry[$ldapAttributeName]);
}
}
}
}
\Drupal::moduleHandler()
->alter('ldap_entry', $ldapUserEntry, $params);
return $ldapUserEntry;
}
private function extractTokenParts($token) {
$attributes = [];
ConversionHelper::extractTokenAttributes($attributes, $token);
if (is_array($attributes)) {
$keys = array_keys($attributes);
$attr_name = $keys[0];
$attr_data = $attributes[$attr_name];
$ordinals = array_keys($attr_data['values']);
$ordinal = $ordinals[0];
return [
$attr_name,
$ordinal,
];
}
else {
return [
NULL,
NULL,
];
}
}
public function provisionLdapEntry($account, array $ldap_user = NULL) {
$result = [
'status' => NULL,
'ldap_server' => NULL,
'proposed' => NULL,
'existing' => NULL,
'description' => NULL,
];
if (is_scalar($account)) {
$account = user_load_by_name($account);
}
if (is_object($account) && $account
->id() == 1) {
$result['status'] = 'fail';
$result['error_description'] = 'can not provision Drupal user 1';
return $result;
}
if ($account == FALSE || $account
->isAnonymous()) {
$result['status'] = 'fail';
$result['error_description'] = 'can not provision LDAP user unless corresponding Drupal account exists first.';
return $result;
}
if (!$this->config['ldapEntryProvisionServer']) {
$result['status'] = 'fail';
$result['error_description'] = 'no provisioning server enabled';
return $result;
}
$factory = \Drupal::service('ldap.servers');
$ldapServer = $factory
->getServerById($this->config['ldapEntryProvisionServer']);
$params = [
'direction' => self::PROVISION_TO_LDAP,
'prov_events' => [
self::EVENT_CREATE_LDAP_ENTRY,
],
'module' => 'ldap_user',
'function' => 'provisionLdapEntry',
'include_count' => FALSE,
];
try {
$proposedLdapEntry = $this
->drupalUserToLdapEntry($account, $ldapServer, $params, $ldap_user);
} catch (\Exception $e) {
\Drupal::logger('ldap_user')
->error('User or server is missing during LDAP provisioning: %message', [
'%message',
$e
->getMessage(),
]);
return [
'status' => 'fail',
'ldap_server' => $ldapServer,
'created' => NULL,
'existing' => NULL,
];
}
if (is_array($proposedLdapEntry) && isset($proposedLdapEntry['dn']) && $proposedLdapEntry['dn']) {
$proposedDn = $proposedLdapEntry['dn'];
}
else {
$proposedDn = NULL;
}
$proposedDnLowercase = mb_strtolower($proposedDn);
$existingLdapEntry = $proposedDn ? $ldapServer
->checkDnExistsIncludeData($proposedDn, [
'objectclass',
]) : NULL;
if (!$proposedDn) {
return [
'status' => 'fail',
'description' => t('failed to derive dn and or mappings'),
];
}
elseif ($existingLdapEntry) {
$result['status'] = 'conflict';
$result['description'] = 'can not provision LDAP entry because exists already';
$result['existing'] = $existingLdapEntry;
$result['proposed'] = $proposedLdapEntry;
$result['ldap_server'] = $ldapServer;
}
else {
$ldapEntries = [
$proposedDnLowercase => $proposedLdapEntry,
];
$context = [
'action' => 'add',
'corresponding_drupal_data' => [
$proposedDnLowercase => $account,
],
'corresponding_drupal_data_type' => 'user',
'account' => $account,
];
\Drupal::moduleHandler()
->alter('ldap_entry_pre_provision', $ldapEntries, $ldapServer, $context);
$proposedLdapEntry = $ldapEntries[$proposedDnLowercase];
$ldapEntryCreated = $ldapServer
->createLdapEntry($proposedLdapEntry, $proposedDn);
$callbackParams = [
$ldapEntries,
$ldapServer,
$context,
];
if ($ldapEntryCreated) {
\Drupal::moduleHandler()
->invokeAll('ldap_entry_post_provision', $callbackParams);
$result = [
'status' => 'success',
'description' => 'ldap account created',
'proposed' => $proposedLdapEntry,
'created' => $ldapEntryCreated,
'ldap_server' => $ldapServer,
];
$ldap_user_prov_entry = $ldapServer
->id() . '|' . $proposedLdapEntry['dn'];
if (NULL !== $account
->get('ldap_user_prov_entries')) {
$account
->set('ldap_user_prov_entries', []);
}
$ldapUserProvisioningEntryExists = FALSE;
if ($account
->get('ldap_user_prov_entries')->value) {
foreach ($account
->get('ldap_user_prov_entries')->value as $fieldValueInstance) {
if ($fieldValueInstance == $ldap_user_prov_entry) {
$ldapUserProvisioningEntryExists = TRUE;
}
}
}
if (!$ldapUserProvisioningEntryExists) {
$prov_entries = $account
->get('ldap_user_prov_entries')->value;
$prov_entries[] = [
'value' => $ldap_user_prov_entry,
'format' => NULL,
'save_value' => $ldap_user_prov_entry,
];
$account
->set('ldap_user_prov_entries', $prov_entries);
$account
->save();
}
}
else {
$result = [
'status' => 'fail',
'proposed' => $proposedLdapEntry,
'created' => $ldapEntryCreated,
'ldap_server' => $ldapServer,
'existing' => NULL,
];
}
}
$tokens = [
'%dn' => isset($result['proposed']['dn']) ? $result['proposed']['dn'] : NULL,
'%sid' => isset($result['ldap_server']) && $result['ldap_server'] ? $result['ldap_server']
->id() : 0,
'%username' => @$account
->getAccountName(),
'%uid' => @$account
->id(),
'%description' => @$result['description'],
];
if (isset($result['status'])) {
if ($result['status'] == 'success') {
$this->detailLog
->log('LDAP entry on server %sid created dn=%dn. %description. username=%username, uid=%uid', $tokens, 'ldap_user');
}
elseif ($result['status'] == 'conflict') {
$this->detailLog
->log('LDAP entry on server %sid not created because of existing LDAP entry. %description. username=%username, uid=%uid', $tokens, 'ldap_user');
}
elseif ($result['status'] == 'fail') {
\Drupal::logger('ldap_user')
->error('LDAP entry on server %sid not created because of error. %description. username=%username, uid=%uid, proposed dn=%dn', $tokens);
}
}
return $result;
}
public function deleteProvisionedLdapEntries(User $account) {
$result = FALSE;
$entries = $account
->get('ldap_user_prov_entries')
->getValue();
foreach ($entries as $entry) {
$parts = explode('|', $entry['value']);
if (count($parts) == 2) {
list($sid, $dn) = $parts;
$factory = \Drupal::service('ldap.servers');
$ldap_server = $factory
->getServerById($sid);
if (is_object($ldap_server) && $dn) {
$result = $ldap_server
->deleteLdapEntry($dn);
$tokens = [
'%sid' => $sid,
'%dn' => $dn,
'%username' => $account
->getAccountName(),
'%uid' => $account
->id(),
];
if ($result) {
\Drupal::logger('ldap_user')
->info('LDAP entry on server %sid deleted dn=%dn. username=%username, uid=%uid', $tokens);
}
else {
\Drupal::logger('ldap_user')
->error('LDAP entry on server %sid not deleted because error. username=%username, uid=%uid', $tokens);
}
}
else {
$result = FALSE;
}
}
}
return $result;
}
public function getProvisionRelatedLdapEntry(User $account, $prov_events = NULL) {
if (!$prov_events) {
$prov_events = LdapConfiguration::getAllEvents();
}
$sid = $this->config['ldapEntryProvisionServer'];
if (!$sid) {
return FALSE;
}
$factory = \Drupal::service('ldap.servers');
$ldap_server = $factory
->getServerById($sid);
$params = [
'direction' => self::PROVISION_TO_LDAP,
'prov_events' => $prov_events,
'module' => 'ldap_user',
'function' => 'getProvisionRelatedLdapEntry',
'include_count' => FALSE,
];
try {
$proposed_ldap_entry = $this
->drupalUserToLdapEntry($account, $ldap_server, $params);
} catch (\Exception $e) {
\Drupal::logger('ldap_user')
->error('Unable to prepare LDAP entry: %message', [
'%message',
$e
->getMessage(),
]);
return FALSE;
}
if (!(is_array($proposed_ldap_entry) && isset($proposed_ldap_entry['dn']) && $proposed_ldap_entry['dn'])) {
return FALSE;
}
$ldap_entry = $ldap_server
->checkDnExistsIncludeData($proposed_ldap_entry['dn'], []);
return $ldap_entry;
}
}