' . t('This module synchronizes departments from an LDAP server to groups through cron.') . '

'; } } /** * Implements hook_cron(). */ function ldap_departments_sync_cron() { // Check if module is properly configured $config = \Drupal::config('ldap_departments_sync.settings'); $ldap_server_id = $config->get('ldap_server_id'); $ldap_query_id = $config->get('ldap_query_id'); // Validate LDAP server try { $entity_type_manager = \Drupal::entityTypeManager(); $server_storage = $entity_type_manager->getStorage('ldap_server'); $server = $server_storage->load($ldap_server_id); if (!$server || !$server->get('status')) { \Drupal::logger('ldap_departments_sync')->warning('Synchronization cancelled: LDAP server "@server_id" not found or inactive. Configure at /admin/config/local-modules/ldap-departments-sync', [ '@server_id' => $ldap_server_id, ]); return; } } catch (\Exception $e) { \Drupal::logger('ldap_departments_sync')->warning('Synchronization cancelled: error checking LDAP server: @message', [ '@message' => $e->getMessage(), ]); return; } // Validate LDAP query try { if ($entity_type_manager->hasDefinition('ldap_query_entity')) { $query_storage = $entity_type_manager->getStorage('ldap_query_entity'); $query = $query_storage->load($ldap_query_id); if (!$query || !$query->get('status')) { \Drupal::logger('ldap_departments_sync')->warning('Synchronization cancelled: LDAP query "@query_id" not found or inactive. Configure at /admin/config/local-modules/ldap-departments-sync', [ '@query_id' => $ldap_query_id, ]); return; } } else { \Drupal::logger('ldap_departments_sync')->warning('Synchronization cancelled: ldap_query module not available.'); return; } } catch (\Exception $e) { \Drupal::logger('ldap_departments_sync')->warning('Synchronization cancelled: error checking LDAP query: @message', [ '@message' => $e->getMessage(), ]); return; } // Validate group type try { $group_type_id = $config->get('group_type_id'); $group_type_storage = $entity_type_manager->getStorage('group_type'); $group_type = $group_type_storage->load($group_type_id); if (!$group_type) { \Drupal::logger('ldap_departments_sync')->warning('Synchronization cancelled: group type "@type_id" not found. Configure at /admin/config/local-modules/ldap-departments-sync', [ '@type_id' => $group_type_id, ]); return; } } catch (\Exception $e) { \Drupal::logger('ldap_departments_sync')->warning('Synchronization cancelled: error checking group type: @message', [ '@message' => $e->getMessage(), ]); return; } // Get LDAP synchronization service $ldap_sync = \Drupal::service('ldap_departments_sync.sync'); // Execute departments synchronization try { $ldap_sync->syncDepartments(); \Drupal::logger('ldap_departments_sync')->info('Departments synchronization executed successfully.'); } catch (\Exception $e) { \Drupal::logger('ldap_departments_sync')->error('Error in departments synchronization: @message', [ '@message' => $e->getMessage(), ]); } } /** * Implements hook_entity_access(). * * Evaluates access rules configured in the module settings for view, update * and delete operations on existing entities. */ function ldap_departments_sync_entity_access(EntityInterface $entity, $operation, AccountInterface $account) { // Skip group-related entities to avoid conflicts with the group module. $skip_types = ['group', 'group_content', 'group_relationship', 'group_role', 'group_type']; if (in_array($entity->getEntityTypeId(), $skip_types, TRUE)) { return AccessResult::neutral(); } return \Drupal::service('ldap_departments_sync.access_rules') ->checkAccess($entity, $operation, $account); } /** * Implements hook_entity_create_access(). * * Evaluates access rules configured in the module settings for create * operations on new entities. */ function ldap_departments_sync_entity_create_access(AccountInterface $account, array $context, $entity_bundle) { $entity_type_id = $context['entity_type_id'] ?? ''; // Skip group-related entities. $skip_types = ['group', 'group_content', 'group_relationship', 'group_role', 'group_type']; if (in_array($entity_type_id, $skip_types, TRUE)) { return AccessResult::neutral(); } return \Drupal::service('ldap_departments_sync.access_rules') ->checkCreateAccess($account, $entity_type_id, $entity_bundle ?? ''); } /** * Implements hook_entity_field_access(). * * Nega acesso de edição aos campos gerenciados pelo LDAP sync em formulários * e APIs (REST, JSON:API). Saves programáticos não são afetados. * * Campos protegidos no usuário: * - field_user_category: categoria do usuário, populado pelo ldap_user. * - field_user_dept_code: código do departamento, populado pelo ldap_user. * - field_user_department: referência ao grupo, populado pelo nosso sync. * Proteger este campo evita o erro de validação "Esta entidade não pode * ser referenciada" que ocorre quando o widget tenta validar o grupo * referenciado contra o acesso do usuário atual. * - field_user_work_phone: telefone de trabalho, populado pelo ldap_user. */ function ldap_departments_sync_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, ?FieldItemListInterface $items = NULL) { $protected_user_fields = [ 'field_user_category', 'field_user_dept_code', 'field_user_department', 'field_user_work_phone', ]; if ( $field_definition->getTargetEntityTypeId() === 'user' && in_array($field_definition->getName(), $protected_user_fields, TRUE) && $operation === 'edit' ) { // Sempre nega edição via UI/API, inclusive para o uid 1. // Saves programáticos (ldap_user, drush) não passam por este hook. return AccessResult::forbidden('This field is managed exclusively by LDAP synchronization.'); } return AccessResult::neutral(); } /** * Implements hook_user_presave(). * * Protege os campos gerenciados pelo LDAP sync de alterações programáticas * não autorizadas em usuários existentes. Permite: * - Saves durante sync LDAP autorizado (LdapDepartmentsSync::$syncing TRUE) * - Criação de novos usuários (provisionamento inicial via ldap_user) * - Salvas por usuários com a permissão 'edit ldap managed user fields' */ function ldap_departments_sync_user_presave(\Drupal\user\UserInterface $user) { // Permite durante qualquer sync LDAP autorizado. if (LdapDepartmentsSync::isSyncing()) { return; } // Permite na criação inicial do usuário (primeiro login LDAP). if ($user->isNew()) { return; } // Permite se o usuário atual tem a permissão de bypass. if (\Drupal::currentUser()->hasPermission('edit ldap managed user fields')) { return; } $original = $user->original; if ($original === NULL) { return; } // Campos gerenciados pelo LDAP que não podem ser alterados externamente. $protected_fields = [ 'field_user_category', 'field_user_dept_code', 'field_user_department', 'field_user_work_phone', ]; foreach ($protected_fields as $field_name) { if (!$user->hasField($field_name)) { continue; } $original_value = $original->get($field_name)->getValue(); $new_value = $user->get($field_name)->getValue(); if ($original_value !== $new_value) { $user->set($field_name, $original_value); \Drupal::logger('ldap_departments_sync')->warning( 'Tentativa não autorizada de alterar @field do usuário @username foi bloqueada. Use a sincronização LDAP para atualizar este campo.', [ '@field' => $field_name, '@username' => $user->getAccountName(), ] ); } } } /** * Implements hook_ENTITY_TYPE_predelete() for user entities. * * Cleans up user references in department groups when a user is deleted. */ function ldap_departments_sync_user_predelete(\Drupal\user\UserInterface $user) { $config = \Drupal::config('ldap_departments_sync.settings'); $group_type_id = $config->get('group_type_id') ?: 'departments'; // Fields that may contain user references $user_reference_fields = [ 'field_dept_coord', 'field_dept_coord_assoc', ]; try { $group_storage = \Drupal::entityTypeManager()->getStorage('group'); $user_id = $user->id(); // Find groups that reference this user foreach ($user_reference_fields as $field_name) { $groups = $group_storage->loadByProperties([ 'type' => $group_type_id, $field_name => $user_id, ]); foreach ($groups as $group) { if ($group->hasField($field_name)) { $group->set($field_name, NULL); $group->save(); \Drupal::logger('ldap_departments_sync')->info('Removed user @username reference from @field in group @group', [ '@username' => $user->getAccountName(), '@field' => $field_name, '@group' => $group->label(), ]); } } } } catch (\Exception $e) { \Drupal::logger('ldap_departments_sync')->error('Error cleaning up user references: @message', [ '@message' => $e->getMessage(), ]); } }