Skip to content

Commit d60faa5

Browse files
feat(provisioning_api): OCS endpoints for nested groups and group sub-admins
Additive routes under cloud/groups/{groupId}: - GET/POST/DELETE subgroups - GET/POST/DELETE subadmins/groups All require the Users admin delegation. Cycle and unsupported-backend cases are surfaced as typed HTTP errors. OpenAPI spec regenerated. Signed-off-by: Kiara Grouwstra <cinereal@riseup.net>
1 parent c6da64e commit d60faa5

5 files changed

Lines changed: 2628 additions & 0 deletions

File tree

apps/provisioning_api/appinfo/routes.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
['root' => '/cloud', 'name' => 'Groups#getGroupUsers', 'url' => '/groups/{groupId}/users', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
2222
['root' => '/cloud', 'name' => 'Groups#getGroupUsersDetails', 'url' => '/groups/{groupId}/users/details', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
2323
['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
24+
['root' => '/cloud', 'name' => 'Groups#getSubGroups', 'url' => '/groups/{groupId}/subgroups', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
25+
['root' => '/cloud', 'name' => 'Groups#addSubGroup', 'url' => '/groups/{groupId}/subgroups', 'verb' => 'POST', 'requirements' => ['groupId' => '.+']],
26+
['root' => '/cloud', 'name' => 'Groups#removeSubGroup', 'url' => '/groups/{groupId}/subgroups/{subGroupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+', 'subGroupId' => '.+']],
27+
['root' => '/cloud', 'name' => 'Groups#getGroupSubAdmins', 'url' => '/groups/{groupId}/subadmins/groups', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
28+
['root' => '/cloud', 'name' => 'Groups#addGroupSubAdmin', 'url' => '/groups/{groupId}/subadmins/groups', 'verb' => 'POST', 'requirements' => ['groupId' => '.+']],
29+
['root' => '/cloud', 'name' => 'Groups#removeGroupSubAdmin', 'url' => '/groups/{groupId}/subadmins/groups/{adminGroupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+', 'adminGroupId' => '.+']],
2430
['root' => '/cloud', 'name' => 'Groups#addGroup', 'url' => '/groups', 'verb' => 'POST'],
2531
['root' => '/cloud', 'name' => 'Groups#getGroup', 'url' => '/groups/{groupId}', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
2632
['root' => '/cloud', 'name' => 'Groups#updateGroup', 'url' => '/groups/{groupId}', 'verb' => 'PUT', 'requirements' => ['groupId' => '.+']],

apps/provisioning_api/lib/Controller/GroupsController.php

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,4 +382,166 @@ public function getSubAdminsOfGroup(string $groupId): DataResponse {
382382

383383
return new DataResponse($uids);
384384
}
385+
386+
/**
387+
* Get the direct subgroups of a group (one level deep).
388+
*
389+
* Only edges stored in the nested-group table are returned; transitive
390+
* descendants are not. Use effective-member endpoints if you need the
391+
* full set of users reachable via nesting.
392+
*
393+
* @param string $groupId ID of the parent group
394+
* @return DataResponse<Http::STATUS_OK, list<string>, array{}>
395+
* @throws OCSException
396+
*
397+
* 200: Direct subgroups returned
398+
*/
399+
#[AuthorizedAdminSetting(settings: Users::class)]
400+
public function getSubGroups(string $groupId): DataResponse {
401+
$groupId = urldecode($groupId);
402+
$group = $this->groupManager->get($groupId);
403+
if ($group === null) {
404+
throw new OCSException('Group does not exist', 101);
405+
}
406+
$direct = $this->groupManager->getDirectChildGroupIds($groupId);
407+
return new DataResponse($direct);
408+
}
409+
410+
/**
411+
* Add a subgroup to a group
412+
*
413+
* @param string $groupId ID of the parent group
414+
* @param string $subGroupId ID of the group to add as a subgroup
415+
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
416+
* @throws OCSException
417+
*
418+
* 200: Subgroup added
419+
*/
420+
#[AuthorizedAdminSetting(settings: Users::class)]
421+
#[PasswordConfirmationRequired]
422+
public function addSubGroup(string $groupId, string $subGroupId): DataResponse {
423+
$groupId = urldecode($groupId);
424+
$parent = $this->groupManager->get($groupId);
425+
if ($parent === null) {
426+
throw new OCSException('Parent group does not exist', 101);
427+
}
428+
$child = $this->groupManager->get($subGroupId);
429+
if ($child === null) {
430+
throw new OCSException('Subgroup does not exist', 102);
431+
}
432+
try {
433+
$this->groupManager->addSubGroup($parent, $child);
434+
} catch (\OCP\Group\Exception\CycleDetectedException $e) {
435+
throw new OCSException($e->getMessage(), 103);
436+
} catch (\OCP\Group\Exception\NestedGroupsNotSupportedException $e) {
437+
throw new OCSException('Nested groups are not supported by this backend', 104);
438+
}
439+
return new DataResponse();
440+
}
441+
442+
/**
443+
* Remove a subgroup from a group
444+
*
445+
* @param string $groupId ID of the parent group
446+
* @param string $subGroupId ID of the subgroup to remove
447+
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
448+
* @throws OCSException
449+
*
450+
* 200: Subgroup removed
451+
*/
452+
#[AuthorizedAdminSetting(settings: Users::class)]
453+
#[PasswordConfirmationRequired]
454+
public function removeSubGroup(string $groupId, string $subGroupId): DataResponse {
455+
$groupId = urldecode($groupId);
456+
$subGroupId = urldecode($subGroupId);
457+
$parent = $this->groupManager->get($groupId);
458+
if ($parent === null) {
459+
throw new OCSException('Parent group does not exist', 101);
460+
}
461+
$child = $this->groupManager->get($subGroupId);
462+
if ($child === null) {
463+
throw new OCSException('Subgroup does not exist', 102);
464+
}
465+
try {
466+
$this->groupManager->removeSubGroup($parent, $child);
467+
} catch (\OCP\Group\Exception\NestedGroupsNotSupportedException $e) {
468+
throw new OCSException('Nested groups are not supported by this backend', 104);
469+
}
470+
return new DataResponse();
471+
}
472+
473+
/**
474+
* Get the groups designated as sub-admins of a group
475+
*
476+
* @param string $groupId ID of the group
477+
* @return DataResponse<Http::STATUS_OK, list<string>, array{}>
478+
* @throws OCSException
479+
*
480+
* 200: Admin groups returned
481+
*/
482+
#[AuthorizedAdminSetting(settings: Users::class)]
483+
public function getGroupSubAdmins(string $groupId): DataResponse {
484+
$groupId = urldecode($groupId);
485+
$group = $this->groupManager->get($groupId);
486+
if ($group === null) {
487+
throw new OCSException('Group does not exist', 101);
488+
}
489+
$adminGroups = $this->groupManager->getSubAdmin()->getGroupSubAdminsOfGroup($group);
490+
/** @var list<string> $gids */
491+
$gids = array_map(static fn (IGroup $g): string => $g->getGID(), $adminGroups);
492+
return new DataResponse($gids);
493+
}
494+
495+
/**
496+
* Designate a group as sub-admin of another group
497+
*
498+
* @param string $groupId ID of the group to be administered
499+
* @param string $adminGroupId ID of the group to grant sub-admin rights
500+
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
501+
* @throws OCSException
502+
*
503+
* 200: Group sub-admin created
504+
*/
505+
#[AuthorizedAdminSetting(settings: Users::class)]
506+
#[PasswordConfirmationRequired]
507+
public function addGroupSubAdmin(string $groupId, string $adminGroupId): DataResponse {
508+
$groupId = urldecode($groupId);
509+
$group = $this->groupManager->get($groupId);
510+
if ($group === null) {
511+
throw new OCSException('Group does not exist', 101);
512+
}
513+
$adminGroup = $this->groupManager->get($adminGroupId);
514+
if ($adminGroup === null) {
515+
throw new OCSException('Admin group does not exist', 102);
516+
}
517+
$this->groupManager->getSubAdmin()->createGroupSubAdmin($adminGroup, $group);
518+
return new DataResponse();
519+
}
520+
521+
/**
522+
* Revoke sub-admin rights for a group
523+
*
524+
* @param string $groupId ID of the group
525+
* @param string $adminGroupId ID of the admin group
526+
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
527+
* @throws OCSException
528+
*
529+
* 200: Group sub-admin removed
530+
*/
531+
#[AuthorizedAdminSetting(settings: Users::class)]
532+
#[PasswordConfirmationRequired]
533+
public function removeGroupSubAdmin(string $groupId, string $adminGroupId): DataResponse {
534+
$groupId = urldecode($groupId);
535+
$adminGroupId = urldecode($adminGroupId);
536+
$group = $this->groupManager->get($groupId);
537+
if ($group === null) {
538+
throw new OCSException('Group does not exist', 101);
539+
}
540+
$adminGroup = $this->groupManager->get($adminGroupId);
541+
if ($adminGroup === null) {
542+
throw new OCSException('Admin group does not exist', 102);
543+
}
544+
$this->groupManager->getSubAdmin()->deleteGroupSubAdmin($adminGroup, $group);
545+
return new DataResponse();
546+
}
385547
}

0 commit comments

Comments
 (0)