diff --git a/plugins/AutomaticGroups.php b/plugins/AutomaticGroups.php new file mode 100644 index 0000000000000000000000000000000000000000..4fed112dc26a45f2962cf77caa6c398e686646ac --- /dev/null +++ b/plugins/AutomaticGroups.php @@ -0,0 +1,278 @@ +<?php + +class AutomaticGroups implements EndpointInterface +{ + private TaskGateway $gateway; + + public function __construct(TaskGateway $gateway) + { + $this->gateway = $gateway; + } + + /** + * @return array + * Part of the interface of orchestrator plugin to treat GET method + */ + public function processEndPointGet(): array + { + return $this->gateway->getObjectTypeTask('Automatic Groups'); + } + + /** + * @param array|null $data + * @return array + */ + public function processEndPointPost(array $data = NULL): array + { + return []; + } + + /** + * @param array|null $data + * @return array + * @throws Exception + */ + public function processEndPointPatch(array $data = NULL): array + { + return $this->processAutomaticGroups($this->gateway->getObjectTypeTask('Automatic Groups')); + } + + /** + * @param array|null $data + * @return array + */ + public function processEndPointDelete(array $data = NULL): array + { + return []; + } + + /** + * Process automatic group assignment tasks + * + * @param array $automaticGroupsTasks + * @return array + * @throws Exception + */ + public function processAutomaticGroups(array $automaticGroupsTasks): array + { + $result = []; + + if (empty($automaticGroupsTasks)) { + return ['No automatic groups tasks require processing.']; + } + + foreach ($automaticGroupsTasks as $task) { + try { + // Check if task should be processed (status and schedule) + if (!$this->gateway->statusAndScheduleCheck($task)) { + continue; + } + + // Get the DN of the user/group to process + $userDn = $task['fdtasksgranulardn'][0] ?? null; + if (empty($userDn)) { + throw new Exception("Missing user DN in task"); + } + + // Get main task configuration + $mainTaskConfig = $this->getAutomaticGroupsMainTask($task['fdtasksgranularmaster'][0]); + + // Get target group and resource/state criteria + $targetGroup = $mainTaskConfig[0]['fdtasksautomaticgroupsofname'][0] ?? null; + $resource = $mainTaskConfig[0]['fdtasksautomaticgroupsresource'][0] ?? null; + $state = $mainTaskConfig[0]['fdtasksautomaticgroupsstate'][0] ?? null; + $subState = $mainTaskConfig[0]['fdtasksautomaticgroupssubstate'][0] ?? null; + + if (empty($targetGroup)) { + throw new Exception("Missing target group in task configuration"); + } + + // Check if user meets the criteria (if resource/state specified) + $shouldAddToGroup = true; + if ($resource !== 'NONE' && !empty($resource) && !empty($state)) { + $userSupannState = $this->getUserSupannState($userDn); + $shouldAddToGroup = $this->checkUserSupannState($userSupannState, $resource, $state, $subState); + } + + // Add/remove user from group based on criteria + if ($shouldAddToGroup) { + $this->addUserToGroup($userDn, $targetGroup); + $result[$task['dn']]['result'] = "User $userDn successfully added to group $targetGroup"; + } else { + $this->removeUserFromGroup($userDn, $targetGroup); + $result[$task['dn']]['result'] = "User $userDn doesn't meet criteria - removed from group $targetGroup"; + } + + // Update task status + $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], '2'); + } catch (Exception $e) { + $result[$task['dn']]['result'] = "Error processing task: " . $e->getMessage(); + $this->gateway->updateTaskStatus($task['dn'], $task['cn'][0], $e->getMessage()); + } + } + + return $result; + } + + /** + * Get main task configuration + * + * @param string $mainTaskDn + * @return array + */ + private function getAutomaticGroupsMainTask(string $mainTaskDn): array + { + return $this->gateway->getLdapTasks( + '(objectClass=fdTasksAutomaticGroups)', + [ + 'fdTasksAutomaticGroupsOfName', + 'fdTasksAutomaticGroupsResource', + 'fdTasksAutomaticGroupsState', + 'fdTasksAutomaticGroupsSubState' + ], + '', + $mainTaskDn + ); + } + + /** + * Get user's Supann state + * + * @param string $userDn + * @return array + */ + private function getUserSupannState(string $userDn): array + { + $result = $this->gateway->getLdapTasks( + '(objectClass=*)', + ['supannRessourceEtat'], + '', + $userDn + ); + + $this->gateway->unsetCountKeys($result); + return $result; + } + + /** + * Check if user matches the required Supann state + * + * @param array $userSupannState + * @param string $resource + * @param string $state + * @param string|null $subState + * @return bool + */ + private function checkUserSupannState(array $userSupannState, string $resource, string $state, ?string $subState): bool + { + if (empty($userSupannState[0]['supannressourceetat'])) { + return false; + } + + foreach ($userSupannState[0]['supannressourceetat'] as $value) { + // Create the expected format for comparison + $expectedState = ''; + if (!empty($subState)) { + $expectedState = '{' . $resource . '}' . $state . ':' . $subState; + } else { + $expectedState = '{' . $resource . '}' . $state; + } + + if ($value === $expectedState) { + return true; + } + } + + return false; + } + + /** + * Add user to LDAP group + * + * @param string $userDn + * @param string $groupDn + * @return bool + * @throws Exception + */ + private function addUserToGroup(string $userDn, string $groupDn): bool + { + // Get current group members + $groupInfo = $this->gateway->getLdapTasks( + '(objectClass=groupOfNames)', + ['member'], + '', + $groupDn + ); + + $this->gateway->unsetCountKeys($groupInfo); + $members = $groupInfo[0]['member'] ?? []; + + // If member is already in the group, nothing to do + if (in_array($userDn, $members)) { + return true; + } + + // Add member to the group + $members[] = $userDn; + $entry = ['member' => $members]; + + // Update the group in LDAP + try { + $result = ldap_modify($this->gateway->ds, $groupDn, $entry); + if (!$result) { + throw new Exception("Failed to add $userDn to group $groupDn: " . ldap_error($this->gateway->ds)); + } + return true; + } catch (Exception $e) { + throw new Exception("Error adding member to group: " . $e->getMessage()); + } + } + + /** + * Remove user from LDAP group + * + * @param string $userDn + * @param string $groupDn + * @return bool + * @throws Exception + */ + private function removeUserFromGroup(string $userDn, string $groupDn): bool + { + // Get current group members + $groupInfo = $this->gateway->getLdapTasks( + '(objectClass=groupOfNames)', + ['member'], + '', + $groupDn + ); + + $this->gateway->unsetCountKeys($groupInfo); + $members = $groupInfo[0]['member'] ?? []; + + // If member is not in the group, nothing to do + if (!in_array($userDn, $members)) { + return true; + } + + // Remove member from the group + $members = array_diff($members, [$userDn]); + + // Groups must have at least one member, so check if this would empty the group + if (empty($members)) { + return true; // Do nothing if it would empty the group + } + + $entry = ['member' => $members]; + + // Update the group in LDAP + try { + $result = ldap_modify($this->gateway->ds, $groupDn, $entry); + if (!$result) { + throw new Exception("Failed to remove $userDn from group $groupDn: " . ldap_error($this->gateway->ds)); + } + return true; + } catch (Exception $e) { + throw new Exception("Error removing member from group: " . $e->getMessage()); + } + } +} \ No newline at end of file