Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4c3335c
perf(settings): Remove computation of all groups
Pytal Mar 25, 2025
0d20ddf
fix(settings): Fix infinitely loading account management page with pa…
Pytal Mar 25, 2025
56e255e
feat(provisioning_api): Add endpoint for fetching user groups with de…
Pytal Mar 25, 2025
9b303db
perf(settings): Cancel request on new search
Pytal Mar 25, 2025
19e1b54
fix(settings): Allow searching for groups in user row
Pytal Mar 25, 2025
cae40a3
fix(settings): Allow searching for groups in new account dialog
Pytal Mar 25, 2025
4d0f2da
perf(settings): Make scrolling smooth when a large number of groups a…
Pytal Mar 25, 2025
02ca811
refactor(settings): Consolidate group formatting
Pytal Mar 25, 2025
13765c6
chore(settings): Add note on groups sorting
Pytal Mar 25, 2025
0dc0897
fix(settings): Fix loaded groups being undefined
Pytal Mar 25, 2025
b1d3ee8
fix(settings): Prevent selection of invalid groups that are not fully…
Pytal Mar 25, 2025
32c3e18
fix(settings): Fix erroneous hiding of group admin column with pagina…
Pytal Mar 25, 2025
80a2fd2
feat(provisioning_api): Add endpoint for fetching user subadmin group…
Pytal Mar 25, 2025
46098c9
fix(settings): Fix editing groups and subadmin groups of user
Pytal Mar 25, 2025
bdac0d7
fix(settings): Only change usercount if group can be found
Pytal Mar 25, 2025
f5b35fe
fix(settings): Fix group creation when editing users
Pytal Mar 25, 2025
6b28f90
fix(settings): Fix group creation in new account dialog
Pytal Mar 25, 2025
6a36355
fix(settings): Fix duplicated group options when editing account
Pytal Mar 25, 2025
e0cafe5
fix(settings): Fix duplicated group options in new account dialog
Pytal Mar 25, 2025
6ce464b
fix(settings): Natural order groups
Pytal Mar 25, 2025
59885c0
chore(openapi): Update spec
Pytal Mar 25, 2025
42517d9
fix(settings): Preserve system groups on reset
Pytal Mar 25, 2025
4a7fee3
fix(settings): Fix initialization of store
Pytal Mar 25, 2025
0eedb69
fix(settings): Separate subadmin options
Pytal Mar 25, 2025
ada190b
test(settings): Wait until groups list has loaded
Pytal Mar 25, 2025
1223063
test(settings): Correctly find group in select
Pytal Mar 25, 2025
1fc9fb9
test(settings): Fix group items not being found
Pytal Mar 25, 2025
4991271
chore(assets): Recompile assets
nextcloud-command Mar 31, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/provisioning_api/appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,11 @@
['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
['root' => '/cloud', 'name' => 'Users#disableUser', 'url' => '/users/{userId}/disable', 'verb' => 'PUT'],
['root' => '/cloud', 'name' => 'Users#getUsersGroups', 'url' => '/users/{userId}/groups', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#getUsersGroupsDetails', 'url' => '/users/{userId}/groups/details', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#addToGroup', 'url' => '/users/{userId}/groups', 'verb' => 'POST'],
['root' => '/cloud', 'name' => 'Users#removeFromGroup', 'url' => '/users/{userId}/groups', 'verb' => 'DELETE'],
['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroups', 'url' => '/users/{userId}/subadmins', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroupsDetails', 'url' => '/users/{userId}/subadmins/details', 'verb' => 'GET'],
['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'],
['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'],
['root' => '/cloud', 'name' => 'Users#resendWelcomeMessage', 'url' => '/users/{userId}/welcome', 'verb' => 'POST'],
Expand Down
123 changes: 123 additions & 0 deletions apps/provisioning_api/lib/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

use InvalidArgumentException;
use OC\Authentication\Token\RemoteWipe;
use OC\Group\Group;
use OC\KnownUser\KnownUserService;
use OC\User\Backend;
use OCA\Provisioning_API\ResponseDefinitions;
Expand Down Expand Up @@ -52,6 +53,7 @@
use Psr\Log\LoggerInterface;

/**
* @psalm-import-type Provisioning_APIGroupDetails from ResponseDefinitions
* @psalm-import-type Provisioning_APIUserDetails from ResponseDefinitions
*/
class UsersController extends AUserDataOCSController {
Expand Down Expand Up @@ -1402,6 +1404,127 @@ public function getUsersGroups(string $userId): DataResponse {
}
}

/**
* @NoSubAdminRequired
*
* Get a list of groups with details
*
* @param string $userId ID of the user
* @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
* @throws OCSException
*
* 200: Users groups returned
*/
#[NoAdminRequired]
public function getUsersGroupsDetails(string $userId): DataResponse {
$loggedInUser = $this->userSession->getUser();

$targetUser = $this->userManager->get($userId);
if ($targetUser === null) {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}

$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
// Self lookup or admin lookup
$groups = array_map(
function (Group $group) {
return [
'id' => $group->getGID(),
'displayname' => $group->getDisplayName(),
'usercount' => $group->count(),
'disabled' => $group->countDisabled(),
'canAdd' => $group->canAddUser(),
'canRemove' => $group->canRemoveUser(),
];
},
array_values($this->groupManager->getUserGroups($targetUser)),
);
return new DataResponse([
'groups' => $groups,
]);
} else {
$subAdminManager = $this->groupManager->getSubAdmin();

// Looking up someone else
if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
// Return the group that the method caller is subadmin of for the user in question
$gids = array_values(array_intersect(
array_map(
static fn (IGroup $group) => $group->getGID(),
$subAdminManager->getSubAdminsGroups($loggedInUser),
),
$this->groupManager->getUserGroupIds($targetUser)
));
$groups = array_map(
function (string $gid) {
$group = $this->groupManager->get($gid);
return [
'id' => $group->getGID(),
'displayname' => $group->getDisplayName(),
'usercount' => $group->count(),
'disabled' => $group->countDisabled(),
'canAdd' => $group->canAddUser(),
'canRemove' => $group->canRemoveUser(),
];
},
$gids,
);
return new DataResponse([
'groups' => $groups,
]);
} else {
// Not permitted
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}
}
}

/**
* @NoSubAdminRequired
*
* Get a list of the groups the user is a subadmin of, with details
*
* @param string $userId ID of the user
* @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
* @throws OCSException
*
* 200: Users subadmin groups returned
*/
#[NoAdminRequired]
public function getUserSubAdminGroupsDetails(string $userId): DataResponse {
$loggedInUser = $this->userSession->getUser();

$targetUser = $this->userManager->get($userId);
if ($targetUser === null) {
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}

$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
$subAdminManager = $this->groupManager->getSubAdmin();
$groups = array_map(
function (IGroup $group) {
return [
'id' => $group->getGID(),
'displayname' => $group->getDisplayName(),
'usercount' => $group->count(),
'disabled' => $group->countDisabled(),
'canAdd' => $group->canAddUser(),
'canRemove' => $group->canRemoveUser(),
];
},
array_values($subAdminManager->getSubAdminsGroups($targetUser)),
);
return new DataResponse([
'groups' => $groups,
]);
}
throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
}

/**
* Add a user to a group
*
Expand Down
162 changes: 162 additions & 0 deletions apps/provisioning_api/openapi-full.json
Original file line number Diff line number Diff line change
Expand Up @@ -3610,6 +3610,168 @@
}
}
},
"/ocs/v2.php/cloud/users/{userId}/groups/details": {
"get": {
"operationId": "users-get-users-groups-details",
"summary": "Get a list of groups with details",
"tags": [
"users"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "userId",
"in": "path",
"description": "ID of the user",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Users groups returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"groups"
],
"properties": {
"groups": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GroupDetails"
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/cloud/users/{userId}/subadmins/details": {
"get": {
"operationId": "users-get-user-sub-admin-groups-details",
"summary": "Get a list of the groups the user is a subadmin of, with details",
"tags": [
"users"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "userId",
"in": "path",
"description": "ID of the user",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Users subadmin groups returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"groups"
],
"properties": {
"groups": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GroupDetails"
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/cloud/users/{userId}/welcome": {
"post": {
"operationId": "users-resend-welcome-message",
Expand Down
Loading
Loading