diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index 675b1f0..7796d6b 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -107,7 +107,7 @@ public function beforeRender() { $this->set('vv_config', $cfg); $this->set('title', _txt('pl.grouperlite.title.groupmember')); - $this->set('vv_is_user_owner', $this->GrouperGroup->isUserOwner($this->userId ?? '', $cfg) ); + $this->set('vv_is_user_owner', $this->GrouperGroup->isUserGroupOwner($this->userId ?? '', $cfg) ); // $this->set('vv_is_template_user', $this->GrouperGroup->isTemplateUser($this->userId ?? '', $cfg) ); // $this->set('vv_is_grouper_visible', $this->GrouperGroup->isGrouperVisible($this->userId ?? '', $cfg)); $this->set('vv_coid', $this->cur_co['Co']['id']); @@ -189,6 +189,9 @@ public function groupSubscribers(): void */ public function addSubscriber(): void { + $this->layout = null; + $this->autoRender = false; + $groupName = urldecode($this->request->query['group']); $addUserId = urldecode($this->request->query['userId']); @@ -199,10 +202,14 @@ public function addSubscriber(): void // : $groupName; try { - $this->GrouperGroup->addGroupMember($this->userId, - $groupName, - $addUserId, - $this->CoGrouperLiteWidget->getConfig()); + if(!$this->GrouperGroup->addGroupMember($this->userId, + $groupName, + $addUserId, + $this->CoGrouperLiteWidget->getConfig())) { + // The Request returned unsuccessful, but we have not more infomration. In this case we will just return + // forbidden since we do not actually now what happened + $this->restResponse(HttpStatusCodesEnum::HTTP_FORBIDDEN); + } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true)); throw $e; @@ -243,32 +250,35 @@ public function findSubscriber(): void * Called from all pages via AJAX call * * TODO: We need to appropriately handle Unathenticated call. We have to bubble up the response and do something. + * @throws JsonException */ public function removeSubscriber(): void { + $this->layout = null; + $this->autoRender = false; + $groupName = urldecode($this->request->query['group']); $remUserId = urldecode($this->request->query['userId']); - $resultRemove = false; //Need to see if coming from AdHoc or from a WG (Working Group) - $groupNameFormatted = (strpos($groupName, ':') === false) ? 'ref:incommon-collab:' . $groupName . ':users' - : $groupName; +// $groupNameFormatted = (strpos($groupName, ':') === false) ? 'ref:incommon-collab:' . $groupName . ':users' +// : $groupName; try { - $resultRemove = $this->GrouperGroup->removeGroupMember($this->userId, - $groupNameFormatted, - $remUserId, - $this->CoGrouperLiteWidget->getConfig()); + if(!$this->GrouperGroup->removeGroupMember($this->userId, + $groupName, + $remUserId, + $this->CoGrouperLiteWidget->getConfig())) { + // The Request returned unsuccessful, but we have not more infomration. In this case we will just return + // forbidden since we do not actually now what happened + $this->restResponse(HttpStatusCodesEnum::HTTP_FORBIDDEN); + } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true)); + throw $e; } - if (!$resultRemove) { - $this->restResponse(HttpStatusCodesEnum::HTTP_NOT_FOUND, ErrorsEnum::Error); - } - - $this->set(compact($resultRemove ? GrouperResultCodesEnum::SUCCESS : '')); - $this->set('_serialize', 'resultRemove'); + $this->restResponse(HttpStatusCodesEnum::HTTP_OK); } /** @@ -289,7 +299,7 @@ public function groupOwnerApi() { $scope['searchpage'] = 'ownerGroups'; $errorHint = 'Search'; } else { - $scope['method'] = 'ownerGroups'; + $scope['method'] = 'getOwnedGroups'; $errorHint = ''; } try { @@ -429,19 +439,26 @@ public function groupCreateTemplate() /** * Process to join a group displayed on the "Optin" page * + * @throws Exception */ public function joinGroup(): void { + $this->layout = null; + $this->autoRender = false; // todo: add Subscriber and joinGroup should accept the same query parameters. Currently the join Group // accepts a GroupName, while the addSubscriber accepts a group parameter - $name = urldecode($this->request->query['GroupName']); + $groupName = urldecode($this->request->query['GroupName']); try { // Add myself - $this->GrouperGroup->addGroupMember($this->userId, - $name, - $this->userId, - $this->CoGrouperLiteWidget->getConfig()); + if(!$this->GrouperGroup->addGroupMember($this->userId, + $groupName, + $this->userId, + $this->CoGrouperLiteWidget->getConfig())) { + // The Request returned unsuccessful, but we have not more infomration. In this case we will just return + // forbidden since we do not actually now what happened + $this->restResponse(HttpStatusCodesEnum::HTTP_FORBIDDEN); + } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true)); throw $e; @@ -456,25 +473,25 @@ public function joinGroup(): void */ public function leaveGroup(): void { - $name = urldecode($this->request->query['GroupName']); - $resultRemove = false; + $this->layout = null; + $this->autoRender = false; + $groupName = urldecode($this->request->query['GroupName']); try { - $resultRemove = $this->GrouperGroup->leaveGroup($this->userId, - $name, - $this->CoGrouperLiteWidget->getConfig()); + if(!$this->GrouperGroup->removeGroupMember($this->userId, + $groupName, + $this->userId, + $this->CoGrouperLiteWidget->getConfig())) { + // The Request returned unsuccessful, but we have not more infomration. In this case we will just return + // forbidden since we do not actually now what happened + $this->restResponse(HttpStatusCodesEnum::HTTP_FORBIDDEN); + } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true)); + throw $e; } -// $this->restResponse(HttpStatusCodesEnum::HTTP_UNAUTHORIZED, ErrorsEnum::NotDeleted); - - if (!$resultRemove) { - $this->restResponse(HttpStatusCodesEnum::HTTP_NOT_FOUND, ErrorsEnum::Error); - } - - $this->set(compact($resultRemove ? GrouperResultCodesEnum::SUCCESS : '')); - $this->set('_serialize', 'resultRemove'); + $this->restResponse(HttpStatusCodesEnum::HTTP_OK); } /** diff --git a/Lib/GrouperApiAccess.php b/Lib/GrouperApiAccess.php index 867aafb..7c3b7b5 100644 --- a/Lib/GrouperApiAccess.php +++ b/Lib/GrouperApiAccess.php @@ -392,57 +392,6 @@ public function getUserGroupMemberships(string $actorUserId, string $userId): ar return $results['WsGetGroupsLiteResult']['wsGroups'] ?? []; } - /** - * Get members associated to a specific Grouper Group - * - * @param string $actorUserId - * @param string $groupName - * - * @return array Listing of Members belonging to Grouper Group - * @throws GrouperLiteWidgetException - * @throws JsonException - */ - public function getGroupMembers(string $actorUserId, string $groupName): array - { - //Build request logic - $usersToShow = [ - 'WsRestGetMembersRequest' => [ - 'actAsSubjectLookup' => [ - 'subjectId' => $actorUserId - ], - 'wsGroupLookups' => [ - ['groupName' => $groupName] - ], - 'subjectAttributeNames' => ['name'] - ] - ]; - - $actionEndpoint = "/groups"; - - try { - $results = $this->http->sendRequest('POST', - $actionEndpoint, - json_encode($usersToShow, JSON_THROW_ON_ERROR)); - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } - - // Parse out relevant records to send front end - if(isset($results['WsGetMembersResults']['results'][0]['resultMetadata']['resultCode']) - && $results['WsGetMembersResults']['results'][0]['resultMetadata']['resultCode'] === GrouperResultCodesEnum::GROUP_NOT_FOUND) { - return [ - [ - 'sourceId' => 'NoAccess', - 'name' => '', - 'id' => '' - ] - ]; - } - - return $results['WsGetMembersResults']['results'][0]['wsSubjects'] ?? []; - } - /** * Used for requests made to Membership endpoint in Grouper WS * @@ -456,7 +405,7 @@ public function getGroupMembers(string $actorUserId, string $groupName): array * @see getOptOutGroups() * @see getOwnedGroups() */ - private function getGrouperUserMemberships(string $userId, string $groupType): array + public function getGrouperUserMemberships(string $userId, string $groupType): array { if (in_array($groupType, [ GrouperGroupTypeEnum::OPTINS, @@ -500,47 +449,76 @@ private function getGrouperUserMemberships(string $userId, string $groupType): a } /** - * Gets all available Optin/OptOut groups in Grouper - * - * Returns Optin/OptOut groups that can be joined/left + * Get members associated to a specific Grouper Group * - * @param string $userId - * @param string $groupType + * @param string $actorUserId + * @param string $groupName * - * @return array Optin groups from Grouper + * @return array Listing of Members belonging to Grouper Group * @throws GrouperLiteWidgetException + * @throws JsonException */ - public function getOptionalGroups(string $userId, string $groupType): array + public function getGroupMembers(string $actorUserId, string $groupName): array { + //Build request logic + $usersToShow = [ + 'WsRestGetMembersRequest' => [ + 'actAsSubjectLookup' => [ + 'subjectId' => $actorUserId + ], + 'wsGroupLookups' => [ + ['groupName' => $groupName] + ], + 'subjectAttributeNames' => ['name'] + ] + ]; + + $actionEndpoint = "/groups"; + try { - $results = $this->getGrouperUserMemberships($userId, $groupType); + $results = $this->http->sendRequest('POST', + $actionEndpoint, + json_encode($usersToShow, JSON_THROW_ON_ERROR)); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - return $results['WsGetMembershipsResults']['wsGroups'] ?? []; + + // Parse out relevant records to send front end + if(isset($results['WsGetMembersResults']['results'][0]['resultMetadata']['resultCode']) + && $results['WsGetMembersResults']['results'][0]['resultMetadata']['resultCode'] === GrouperResultCodesEnum::GROUP_NOT_FOUND) { + return [ + [ + 'sourceId' => 'NoAccess', + 'name' => '', + 'id' => '' + ] + ]; + } + + return $results['WsGetMembersResults']['results'][0]['wsSubjects'] ?? []; } /** - * Gets all groups in Grouper where user is an admin/owner or has update privs + * Gets all available Optin/OptOut groups in Grouper + * + * Returns Optin/OptOut groups that can be joined/left * * @param string $userId + * @param string $groupType * - * @return array Array of groups from Grouper - * @throws Exception + * @return array Optin groups from Grouper + * @throws GrouperLiteWidgetException */ - public function getOwnedGroups(string $userId): array + public function getOptionalGroups(string $userId, string $groupType): array { try { - $resultsAdmin = $this->getGrouperUserMemberships($userId, GrouperGroupTypeEnum::ADMIN); - $resultsUpdate = $this->getGrouperUserMemberships($userId, GrouperGroupTypeEnum::UPDATE); + $results = $this->getGrouperUserMemberships($userId, $groupType); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - - return $this->removeDuplicates($resultsAdmin['WsGetMembershipsResults']['wsGroups'] ?? [], - $resultsUpdate['WsGetMembershipsResults']['wsGroups'] ?? []); + return $results['WsGetMembershipsResults']['wsGroups'] ?? []; } /** @@ -582,7 +560,7 @@ public function isMemberOfGroup(string $groupName, string $userId): bool $groupNameEncoded = $this->urlGrouperEncode($groupName); - $actionEndpoint = "/groups" + $actionEndpoint = '/groups' . "/{$groupNameEncoded}/members/{$userId}"; try { $results = $this->http->sendRequest('GET', $actionEndpoint); @@ -595,37 +573,6 @@ public function isMemberOfGroup(string $groupName, string $userId): bool && $results['WsHasMemberLiteResul']['resultMetadata']['resultCode'] === GrouperResultCodesEnum::IS_MEMBER; } - /** - * Removes duplicates where the user is the owner and the updater of the group. Just one line instead of two. - * - * @param array $arrOne - * @param array $arrTwo - * @return array - */ - public function removeDuplicates(array $arrOne, array $arrTwo) - { - //Determine which array is bigger and use as base - $countOne = count($arrOne); - $countTwo = count($arrTwo); - if ($countOne >= $countTwo) { - $arrL = $arrOne; - $arrS = $arrTwo; - } else { - $arrL = $arrTwo; - $arrS = $arrOne; - } - - foreach ($arrL as $large) { - foreach ($arrS as $key => $val) { - if ($large['uuid'] == $val['uuid']) { - unset($arrS[$key]); - } - } - } - - return array_merge_recursive($arrL, $arrS); - } - /** * Remove a member from a specific Grouper Group * @@ -672,7 +619,11 @@ public function removeGroupMember(string $actAsUserId, string $groupName, string throw $e; } - // Parse out relevant records to send front end + if(isset($results['error']) && $results['error']) { + $cakeExceptionClass = $results['cakeException']; + throw new $cakeExceptionClass($results['message']); + } + return isset($results['WsDeleteMemberResults']['resultMetadata']['resultCode']) && $results['WsDeleteMemberResults']['resultMetadata']['resultCode'] === GrouperResultCodesEnum::SUCCESS; } diff --git a/Model/GrouperGroup.php b/Model/GrouperGroup.php index 3a7c9b9..1a1f8b6 100644 --- a/Model/GrouperGroup.php +++ b/Model/GrouperGroup.php @@ -70,19 +70,24 @@ class GrouperGroup extends GrouperLiteWidgetAppModel * * @since COmanage Registry v4.4.0 */ - public function isUserOwner(string $userId, array $cfg) + public function isUserGroupOwner(string $userId, array $cfg): bool { $this->initApi($cfg); - $args = array(); + + if(empty($userId)) { + return false; + } try { - $ownGroups = $this->grouperAPI->getOwnedGroups($userId); + $resultsAdmin = $this->grouperAPI->getGrouperUserMemberships($userId, GrouperGroupTypeEnum::ADMIN); + $resultsUpdate = $this->grouperAPI->getGrouperUserMemberships($userId, GrouperGroupTypeEnum::UPDATE); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - return count($ownGroups) > 0 ? 'T' : 'F'; + return count($resultsAdmin['WsGetMembershipsResults']['wsGroups'] ?? []) > 0 + || count($resultsUpdate['WsGetMembershipsResults']['wsGroups'] ?? []) > 0; } /** @@ -286,22 +291,31 @@ private function memberOfGroups(string $actorUserId, string $userId, array $cfg) /** * Return all Grouper Groups that the User has a role of owner/admin * - * @param array $conditions Listing of conditions for display of records, including UserId + * @param array $conditions Listing of conditions for display of records, including UserId + * @param array $cfg + * * @return array * @throws GrouperLiteWidgetException - * * @since COmanage Registry v4.4.0 */ - public function ownerGroups(array $conditions, array $cfg) + public function getOwnedGroups(array $conditions, array $cfg): array { + if(empty($conditions['userId'])) { + return false; + } + $this->initApi($cfg); try { - return $this->grouperAPI->getOwnedGroups($conditions['userId']); + $resultsAdmin = $this->grouperAPI->getGrouperUserMemberships($conditions['userId'], GrouperGroupTypeEnum::ADMIN); + $resultsUpdate = $this->grouperAPI->getGrouperUserMemberships($conditions['userId'], GrouperGroupTypeEnum::UPDATE); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } + + return $this->removeDuplicates($resultsAdmin['WsGetMembershipsResults']['wsGroups'] ?? [], + $resultsUpdate['WsGetMembershipsResults']['wsGroups'] ?? []); } /** @@ -383,14 +397,7 @@ public function removeGroupMember(string $userId, { $this->initApi($cfg); - try { - return $this->grouperAPI->removeGroupMember($userId, - $groupName, - $removeUserId); - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } + return $this->grouperAPI->removeGroupMember($userId, $groupName, $removeUserId); } @@ -674,4 +681,36 @@ private function getFriendlyWorkingGroupName(array $groups, string $method) { return $finalWorkingGroups; } + + + /** + * Removes duplicates where the user is the owner and the updater of the group. Just one line instead of two. + * + * @param array $arrOne + * @param array $arrTwo + * @return array + */ + public function removeDuplicates(array $arrOne, array $arrTwo) + { + //Determine which array is bigger and use as base + $countOne = count($arrOne); + $countTwo = count($arrTwo); + if ($countOne >= $countTwo) { + $arrL = $arrOne; + $arrS = $arrTwo; + } else { + $arrL = $arrTwo; + $arrS = $arrOne; + } + + foreach ($arrL as $large) { + foreach ($arrS as $key => $val) { + if ($large['uuid'] == $val['uuid']) { + unset($arrS[$key]); + } + } + } + + return array_merge_recursive($arrL, $arrS); + } } diff --git a/View/GrouperGroups/index.ctp b/View/GrouperGroups/index.ctp index 822086c..d3da24d 100644 --- a/View/GrouperGroups/index.ctp +++ b/View/GrouperGroups/index.ctp @@ -2,7 +2,6 @@ extend('/GrouperGroups/base'); ?> = $this->Html->script('GrouperLiteWidget.vue-router.js') ?> Html->addCrumb(_txt('pl.grouperlite.nav.memberships')); ?> -