Completed
Push — master ( da7a90...f05f79 )
by
unknown
38:32 queued 15s
created
apps/provisioning_api/appinfo/routes.php 1 patch
Indentation   +60 added lines, -60 removed lines patch added patch discarded remove patch
@@ -6,68 +6,68 @@
 block discarded – undo
6 6
  * SPDX-License-Identifier: AGPL-3.0-only
7 7
  */
8 8
 return [
9
-	'ocs' => [
10
-		// Apps
11
-		['root' => '/cloud', 'name' => 'Apps#getApps', 'url' => '/apps', 'verb' => 'GET'],
12
-		['root' => '/cloud', 'name' => 'Apps#getAppInfo', 'url' => '/apps/{app}', 'verb' => 'GET'],
13
-		['root' => '/cloud', 'name' => 'Apps#enable', 'url' => '/apps/{app}', 'verb' => 'POST'],
14
-		['root' => '/cloud', 'name' => 'Apps#disable', 'url' => '/apps/{app}', 'verb' => 'DELETE'],
9
+    'ocs' => [
10
+        // Apps
11
+        ['root' => '/cloud', 'name' => 'Apps#getApps', 'url' => '/apps', 'verb' => 'GET'],
12
+        ['root' => '/cloud', 'name' => 'Apps#getAppInfo', 'url' => '/apps/{app}', 'verb' => 'GET'],
13
+        ['root' => '/cloud', 'name' => 'Apps#enable', 'url' => '/apps/{app}', 'verb' => 'POST'],
14
+        ['root' => '/cloud', 'name' => 'Apps#disable', 'url' => '/apps/{app}', 'verb' => 'DELETE'],
15 15
 
16
-		// Groups
17
-		['root' => '/cloud', 'name' => 'Groups#getGroups', 'url' => '/groups', 'verb' => 'GET'],
18
-		['root' => '/cloud', 'name' => 'Groups#getGroupsDetails', 'url' => '/groups/details', 'verb' => 'GET'],
19
-		['root' => '/cloud', 'name' => 'Groups#getGroupUsers', 'url' => '/groups/{groupId}/users', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
20
-		['root' => '/cloud', 'name' => 'Groups#getGroupUsersDetails', 'url' => '/groups/{groupId}/users/details', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
21
-		['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
22
-		['root' => '/cloud', 'name' => 'Groups#addGroup', 'url' => '/groups', 'verb' => 'POST'],
23
-		['root' => '/cloud', 'name' => 'Groups#getGroup', 'url' => '/groups/{groupId}', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
24
-		['root' => '/cloud', 'name' => 'Groups#updateGroup', 'url' => '/groups/{groupId}', 'verb' => 'PUT', 'requirements' => ['groupId' => '.+']],
25
-		['root' => '/cloud', 'name' => 'Groups#deleteGroup', 'url' => '/groups/{groupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+']],
16
+        // Groups
17
+        ['root' => '/cloud', 'name' => 'Groups#getGroups', 'url' => '/groups', 'verb' => 'GET'],
18
+        ['root' => '/cloud', 'name' => 'Groups#getGroupsDetails', 'url' => '/groups/details', 'verb' => 'GET'],
19
+        ['root' => '/cloud', 'name' => 'Groups#getGroupUsers', 'url' => '/groups/{groupId}/users', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
20
+        ['root' => '/cloud', 'name' => 'Groups#getGroupUsersDetails', 'url' => '/groups/{groupId}/users/details', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
21
+        ['root' => '/cloud', 'name' => 'Groups#getSubAdminsOfGroup', 'url' => '/groups/{groupId}/subadmins', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
22
+        ['root' => '/cloud', 'name' => 'Groups#addGroup', 'url' => '/groups', 'verb' => 'POST'],
23
+        ['root' => '/cloud', 'name' => 'Groups#getGroup', 'url' => '/groups/{groupId}', 'verb' => 'GET', 'requirements' => ['groupId' => '.+']],
24
+        ['root' => '/cloud', 'name' => 'Groups#updateGroup', 'url' => '/groups/{groupId}', 'verb' => 'PUT', 'requirements' => ['groupId' => '.+']],
25
+        ['root' => '/cloud', 'name' => 'Groups#deleteGroup', 'url' => '/groups/{groupId}', 'verb' => 'DELETE', 'requirements' => ['groupId' => '.+']],
26 26
 
27
-		// Users
28
-		['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
29
-		['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
30
-		['root' => '/cloud', 'name' => 'Users#getDisabledUsersDetails', 'url' => '/users/disabled', 'verb' => 'GET'],
31
-		['root' => '/cloud', 'name' => 'Users#getLastLoggedInUsers', 'url' => '/users/recent', 'verb' => 'GET'],
32
-		['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
33
-		['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
34
-		['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
35
-		['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
36
-		['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields', 'verb' => 'GET'],
37
-		['root' => '/cloud', 'name' => 'Users#getEditableFieldsForUser', 'url' => '/user/fields/{userId}', 'verb' => 'GET'],
38
-		['root' => '/cloud', 'name' => 'Users#getEnabledApps', 'url' => '/user/apps', 'verb' => 'GET'],
39
-		['root' => '/cloud', 'name' => 'Users#editUser', 'url' => '/users/{userId}', 'verb' => 'PUT'],
40
-		['root' => '/cloud', 'name' => 'Users#editUserMultiValue', 'url' => '/users/{userId}/{collectionName}', 'verb' => 'PUT', 'requirements' => ['collectionName' => '^(?!enable$|disable$)[a-zA-Z0-9_]*$']],
41
-		['root' => '/cloud', 'name' => 'Users#wipeUserDevices', 'url' => '/users/{userId}/wipe', 'verb' => 'POST'],
42
-		['root' => '/cloud', 'name' => 'Users#deleteUser', 'url' => '/users/{userId}', 'verb' => 'DELETE'],
43
-		['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
44
-		['root' => '/cloud', 'name' => 'Users#disableUser', 'url' => '/users/{userId}/disable', 'verb' => 'PUT'],
45
-		['root' => '/cloud', 'name' => 'Users#getUsersGroups', 'url' => '/users/{userId}/groups', 'verb' => 'GET'],
46
-		['root' => '/cloud', 'name' => 'Users#getUsersGroupsDetails', 'url' => '/users/{userId}/groups/details', 'verb' => 'GET'],
47
-		['root' => '/cloud', 'name' => 'Users#addToGroup', 'url' => '/users/{userId}/groups', 'verb' => 'POST'],
48
-		['root' => '/cloud', 'name' => 'Users#removeFromGroup', 'url' => '/users/{userId}/groups', 'verb' => 'DELETE'],
49
-		['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroups', 'url' => '/users/{userId}/subadmins', 'verb' => 'GET'],
50
-		['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroupsDetails', 'url' => '/users/{userId}/subadmins/details', 'verb' => 'GET'],
51
-		['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'],
52
-		['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'],
53
-		['root' => '/cloud', 'name' => 'Users#resendWelcomeMessage', 'url' => '/users/{userId}/welcome', 'verb' => 'POST'],
27
+        // Users
28
+        ['root' => '/cloud', 'name' => 'Users#getUsers', 'url' => '/users', 'verb' => 'GET'],
29
+        ['root' => '/cloud', 'name' => 'Users#getUsersDetails', 'url' => '/users/details', 'verb' => 'GET'],
30
+        ['root' => '/cloud', 'name' => 'Users#getDisabledUsersDetails', 'url' => '/users/disabled', 'verb' => 'GET'],
31
+        ['root' => '/cloud', 'name' => 'Users#getLastLoggedInUsers', 'url' => '/users/recent', 'verb' => 'GET'],
32
+        ['root' => '/cloud', 'name' => 'Users#searchByPhoneNumbers', 'url' => '/users/search/by-phone', 'verb' => 'POST'],
33
+        ['root' => '/cloud', 'name' => 'Users#addUser', 'url' => '/users', 'verb' => 'POST'],
34
+        ['root' => '/cloud', 'name' => 'Users#getUser', 'url' => '/users/{userId}', 'verb' => 'GET'],
35
+        ['root' => '/cloud', 'name' => 'Users#getCurrentUser', 'url' => '/user', 'verb' => 'GET'],
36
+        ['root' => '/cloud', 'name' => 'Users#getEditableFields', 'url' => '/user/fields', 'verb' => 'GET'],
37
+        ['root' => '/cloud', 'name' => 'Users#getEditableFieldsForUser', 'url' => '/user/fields/{userId}', 'verb' => 'GET'],
38
+        ['root' => '/cloud', 'name' => 'Users#getEnabledApps', 'url' => '/user/apps', 'verb' => 'GET'],
39
+        ['root' => '/cloud', 'name' => 'Users#editUser', 'url' => '/users/{userId}', 'verb' => 'PUT'],
40
+        ['root' => '/cloud', 'name' => 'Users#editUserMultiValue', 'url' => '/users/{userId}/{collectionName}', 'verb' => 'PUT', 'requirements' => ['collectionName' => '^(?!enable$|disable$)[a-zA-Z0-9_]*$']],
41
+        ['root' => '/cloud', 'name' => 'Users#wipeUserDevices', 'url' => '/users/{userId}/wipe', 'verb' => 'POST'],
42
+        ['root' => '/cloud', 'name' => 'Users#deleteUser', 'url' => '/users/{userId}', 'verb' => 'DELETE'],
43
+        ['root' => '/cloud', 'name' => 'Users#enableUser', 'url' => '/users/{userId}/enable', 'verb' => 'PUT'],
44
+        ['root' => '/cloud', 'name' => 'Users#disableUser', 'url' => '/users/{userId}/disable', 'verb' => 'PUT'],
45
+        ['root' => '/cloud', 'name' => 'Users#getUsersGroups', 'url' => '/users/{userId}/groups', 'verb' => 'GET'],
46
+        ['root' => '/cloud', 'name' => 'Users#getUsersGroupsDetails', 'url' => '/users/{userId}/groups/details', 'verb' => 'GET'],
47
+        ['root' => '/cloud', 'name' => 'Users#addToGroup', 'url' => '/users/{userId}/groups', 'verb' => 'POST'],
48
+        ['root' => '/cloud', 'name' => 'Users#removeFromGroup', 'url' => '/users/{userId}/groups', 'verb' => 'DELETE'],
49
+        ['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroups', 'url' => '/users/{userId}/subadmins', 'verb' => 'GET'],
50
+        ['root' => '/cloud', 'name' => 'Users#getUserSubAdminGroupsDetails', 'url' => '/users/{userId}/subadmins/details', 'verb' => 'GET'],
51
+        ['root' => '/cloud', 'name' => 'Users#addSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'POST'],
52
+        ['root' => '/cloud', 'name' => 'Users#removeSubAdmin', 'url' => '/users/{userId}/subadmins', 'verb' => 'DELETE'],
53
+        ['root' => '/cloud', 'name' => 'Users#resendWelcomeMessage', 'url' => '/users/{userId}/welcome', 'verb' => 'POST'],
54 54
 
55
-		// Config
56
-		['name' => 'AppConfig#getApps', 'url' => '/api/v1/config/apps', 'verb' => 'GET'],
57
-		['name' => 'AppConfig#getKeys', 'url' => '/api/v1/config/apps/{app}', 'verb' => 'GET'],
58
-		['name' => 'AppConfig#getValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'GET'],
59
-		['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'],
60
-		['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'],
55
+        // Config
56
+        ['name' => 'AppConfig#getApps', 'url' => '/api/v1/config/apps', 'verb' => 'GET'],
57
+        ['name' => 'AppConfig#getKeys', 'url' => '/api/v1/config/apps/{app}', 'verb' => 'GET'],
58
+        ['name' => 'AppConfig#getValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'GET'],
59
+        ['name' => 'AppConfig#setValue', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'POST'],
60
+        ['name' => 'AppConfig#deleteKey', 'url' => '/api/v1/config/apps/{app}/{key}', 'verb' => 'DELETE'],
61 61
 
62
-		// Preferences
63
-		['name' => 'Preferences#setPreference', 'url' => '/api/v1/config/users/{appId}/{configKey}', 'verb' => 'POST'],
64
-		['name' => 'Preferences#setMultiplePreferences', 'url' => '/api/v1/config/users/{appId}', 'verb' => 'POST'],
65
-		['name' => 'Preferences#deletePreference', 'url' => '/api/v1/config/users/{appId}/{configKey}', 'verb' => 'DELETE'],
66
-		['name' => 'Preferences#deleteMultiplePreference', 'url' => '/api/v1/config/users/{appId}', 'verb' => 'DELETE'],
67
-	],
68
-	'routes' => [
69
-		// Verification
70
-		['name' => 'Verification#showVerifyMail', 'url' => '/mailVerification/{key}/{token}/{userId}', 'verb' => 'GET'],
71
-		['name' => 'Verification#verifyMail', 'url' => '/mailVerification/{key}/{token}/{userId}', 'verb' => 'POST'],
72
-	]
62
+        // Preferences
63
+        ['name' => 'Preferences#setPreference', 'url' => '/api/v1/config/users/{appId}/{configKey}', 'verb' => 'POST'],
64
+        ['name' => 'Preferences#setMultiplePreferences', 'url' => '/api/v1/config/users/{appId}', 'verb' => 'POST'],
65
+        ['name' => 'Preferences#deletePreference', 'url' => '/api/v1/config/users/{appId}/{configKey}', 'verb' => 'DELETE'],
66
+        ['name' => 'Preferences#deleteMultiplePreference', 'url' => '/api/v1/config/users/{appId}', 'verb' => 'DELETE'],
67
+    ],
68
+    'routes' => [
69
+        // Verification
70
+        ['name' => 'Verification#showVerifyMail', 'url' => '/mailVerification/{key}/{token}/{userId}', 'verb' => 'GET'],
71
+        ['name' => 'Verification#verifyMail', 'url' => '/mailVerification/{key}/{token}/{userId}', 'verb' => 'POST'],
72
+    ]
73 73
 ];
Please login to merge, or discard this patch.
apps/provisioning_api/tests/Controller/UsersControllerTest.php 1 patch
Indentation   +4358 added lines, -4358 removed lines patch added patch discarded remove patch
@@ -47,4363 +47,4363 @@
 block discarded – undo
47 47
 use Test\TestCase;
48 48
 
49 49
 class UsersControllerTest extends TestCase {
50
-	protected IUserManager&MockObject $userManager;
51
-	protected IConfig&MockObject $config;
52
-	protected Manager&MockObject $groupManager;
53
-	protected IUserSession&MockObject $userSession;
54
-	protected LoggerInterface&MockObject $logger;
55
-	protected UsersController&MockObject $api;
56
-	protected IAccountManager&MockObject $accountManager;
57
-	protected ISubAdmin&MockObject $subAdminManager;
58
-	protected IURLGenerator&MockObject $urlGenerator;
59
-	protected IRequest&MockObject $request;
60
-	private IFactory&MockObject $l10nFactory;
61
-	private NewUserMailHelper&MockObject $newUserMailHelper;
62
-	private ISecureRandom&MockObject $secureRandom;
63
-	private RemoteWipe&MockObject $remoteWipe;
64
-	private KnownUserService&MockObject $knownUserService;
65
-	private IEventDispatcher&MockObject $eventDispatcher;
66
-	private IRootFolder $rootFolder;
67
-	private IPhoneNumberUtil $phoneNumberUtil;
68
-	private IAppManager $appManager;
69
-
70
-	protected function setUp(): void {
71
-		parent::setUp();
72
-
73
-		$this->userManager = $this->createMock(IUserManager::class);
74
-		$this->config = $this->createMock(IConfig::class);
75
-		$this->groupManager = $this->createMock(Manager::class);
76
-		$this->userSession = $this->createMock(IUserSession::class);
77
-		$this->logger = $this->createMock(LoggerInterface::class);
78
-		$this->request = $this->createMock(IRequest::class);
79
-		$this->accountManager = $this->createMock(IAccountManager::class);
80
-		$this->subAdminManager = $this->createMock(ISubAdmin::class);
81
-		$this->urlGenerator = $this->createMock(IURLGenerator::class);
82
-		$this->l10nFactory = $this->createMock(IFactory::class);
83
-		$this->newUserMailHelper = $this->createMock(NewUserMailHelper::class);
84
-		$this->secureRandom = $this->createMock(ISecureRandom::class);
85
-		$this->remoteWipe = $this->createMock(RemoteWipe::class);
86
-		$this->knownUserService = $this->createMock(KnownUserService::class);
87
-		$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
88
-		$this->phoneNumberUtil = new PhoneNumberUtil();
89
-		$this->appManager = $this->createMock(IAppManager::class);
90
-		$this->rootFolder = $this->createMock(IRootFolder::class);
91
-
92
-		$l10n = $this->createMock(IL10N::class);
93
-		$l10n->method('t')->willReturnCallback(fn (string $txt, array $replacement = []) => sprintf($txt, ...$replacement));
94
-		$this->l10nFactory->method('get')->with('provisioning_api')->willReturn($l10n);
95
-
96
-		$this->api = $this->getMockBuilder(UsersController::class)
97
-			->setConstructorArgs([
98
-				'provisioning_api',
99
-				$this->request,
100
-				$this->userManager,
101
-				$this->config,
102
-				$this->groupManager,
103
-				$this->userSession,
104
-				$this->accountManager,
105
-				$this->subAdminManager,
106
-				$this->l10nFactory,
107
-				$this->rootFolder,
108
-				$this->urlGenerator,
109
-				$this->logger,
110
-				$this->newUserMailHelper,
111
-				$this->secureRandom,
112
-				$this->remoteWipe,
113
-				$this->knownUserService,
114
-				$this->eventDispatcher,
115
-				$this->phoneNumberUtil,
116
-				$this->appManager,
117
-			])
118
-			->onlyMethods(['fillStorageInfo'])
119
-			->getMock();
120
-	}
121
-
122
-	public function testGetUsersAsAdmin(): void {
123
-		$loggedInUser = $this->getMockBuilder(IUser::class)
124
-			->disableOriginalConstructor()
125
-			->getMock();
126
-		$loggedInUser
127
-			->expects($this->once())
128
-			->method('getUID')
129
-			->willReturn('admin');
130
-		$this->userSession
131
-			->expects($this->once())
132
-			->method('getUser')
133
-			->willReturn($loggedInUser);
134
-		$this->groupManager
135
-			->expects($this->once())
136
-			->method('isAdmin')
137
-			->willReturn(true);
138
-		$this->userManager
139
-			->expects($this->once())
140
-			->method('search')
141
-			->with('MyCustomSearch')
142
-			->willReturn(['Admin' => [], 'Foo' => [], 'Bar' => []]);
143
-
144
-		$expected = [
145
-			'users' => [
146
-				'Admin',
147
-				'Foo',
148
-				'Bar',
149
-			],
150
-		];
151
-		$this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
152
-	}
153
-
154
-	public function testGetUsersAsSubAdmin(): void {
155
-		$loggedInUser = $this->getMockBuilder(IUser::class)
156
-			->disableOriginalConstructor()
157
-			->getMock();
158
-		$loggedInUser
159
-			->expects($this->once())
160
-			->method('getUID')
161
-			->willReturn('subadmin');
162
-		$this->userSession
163
-			->expects($this->once())
164
-			->method('getUser')
165
-			->willReturn($loggedInUser);
166
-		$this->groupManager
167
-			->expects($this->once())
168
-			->method('isAdmin')
169
-			->willReturn(false);
170
-		$firstGroup = $this->getMockBuilder('OCP\IGroup')
171
-			->disableOriginalConstructor()
172
-			->getMock();
173
-		$firstGroup
174
-			->expects($this->once())
175
-			->method('getGID')
176
-			->willReturn('FirstGroup');
177
-		$secondGroup = $this->getMockBuilder('OCP\IGroup')
178
-			->disableOriginalConstructor()
179
-			->getMock();
180
-		$secondGroup
181
-			->expects($this->once())
182
-			->method('getGID')
183
-			->willReturn('SecondGroup');
184
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
185
-			->disableOriginalConstructor()->getMock();
186
-		$subAdminManager
187
-			->expects($this->once())
188
-			->method('isSubAdmin')
189
-			->with($loggedInUser)
190
-			->willReturn(true);
191
-		$subAdminManager
192
-			->expects($this->once())
193
-			->method('getSubAdminsGroups')
194
-			->with($loggedInUser)
195
-			->willReturn([$firstGroup, $secondGroup]);
196
-		$this->groupManager
197
-			->expects($this->once())
198
-			->method('getSubAdmin')
199
-			->willReturn($subAdminManager);
200
-		$this->groupManager
201
-			->expects($this->any())
202
-			->method('displayNamesInGroup')
203
-			->will($this->onConsecutiveCalls(['AnotherUserInTheFirstGroup' => []], ['UserInTheSecondGroup' => []]));
204
-
205
-		$expected = [
206
-			'users' => [
207
-				'AnotherUserInTheFirstGroup',
208
-				'UserInTheSecondGroup',
209
-			],
210
-		];
211
-		$this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
212
-	}
213
-
214
-	private function createUserMock(string $uid, bool $enabled): MockObject&IUser {
215
-		$mockUser = $this->getMockBuilder(IUser::class)
216
-			->disableOriginalConstructor()
217
-			->getMock();
218
-		$mockUser
219
-			->method('getUID')
220
-			->willReturn($uid);
221
-		$mockUser
222
-			->method('isEnabled')
223
-			->willReturn($enabled);
224
-		return $mockUser;
225
-	}
226
-
227
-	public function testGetDisabledUsersAsAdmin(): void {
228
-		$loggedInUser = $this->getMockBuilder(IUser::class)
229
-			->disableOriginalConstructor()
230
-			->getMock();
231
-		$loggedInUser
232
-			->expects($this->once())
233
-			->method('getUID')
234
-			->willReturn('admin');
235
-		$this->userSession
236
-			->expects($this->atLeastOnce())
237
-			->method('getUser')
238
-			->willReturn($loggedInUser);
239
-		$this->groupManager
240
-			->expects($this->once())
241
-			->method('isAdmin')
242
-			->willReturn(true);
243
-		$this->userManager
244
-			->expects($this->once())
245
-			->method('getDisabledUsers')
246
-			->with(3, 0, 'MyCustomSearch')
247
-			->willReturn([
248
-				$this->createUserMock('admin', false),
249
-				$this->createUserMock('foo', false),
250
-				$this->createUserMock('bar', false),
251
-			]);
252
-
253
-		$expected = [
254
-			'users' => [
255
-				'admin' => ['id' => 'admin'],
256
-				'foo' => ['id' => 'foo'],
257
-				'bar' => ['id' => 'bar'],
258
-			],
259
-		];
260
-		$this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
261
-	}
262
-
263
-	public function testGetDisabledUsersAsSubAdmin(): void {
264
-		$loggedInUser = $this->getMockBuilder(IUser::class)
265
-			->disableOriginalConstructor()
266
-			->getMock();
267
-		$loggedInUser
268
-			->expects($this->once())
269
-			->method('getUID')
270
-			->willReturn('subadmin');
271
-		$this->userSession
272
-			->expects($this->atLeastOnce())
273
-			->method('getUser')
274
-			->willReturn($loggedInUser);
275
-		$this->groupManager
276
-			->expects($this->once())
277
-			->method('isAdmin')
278
-			->willReturn(false);
279
-		$firstGroup = $this->getMockBuilder('OCP\IGroup')
280
-			->disableOriginalConstructor()
281
-			->getMock();
282
-		$secondGroup = $this->getMockBuilder('OCP\IGroup')
283
-			->disableOriginalConstructor()
284
-			->getMock();
285
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
286
-			->disableOriginalConstructor()->getMock();
287
-		$subAdminManager
288
-			->expects($this->once())
289
-			->method('isSubAdmin')
290
-			->with($loggedInUser)
291
-			->willReturn(true);
292
-		$subAdminManager
293
-			->expects($this->once())
294
-			->method('getSubAdminsGroups')
295
-			->with($loggedInUser)
296
-			->willReturn([$firstGroup, $secondGroup]);
297
-		$this->groupManager
298
-			->expects($this->once())
299
-			->method('getSubAdmin')
300
-			->willReturn($subAdminManager);
301
-		$this->groupManager
302
-			->expects($this->never())
303
-			->method('displayNamesInGroup');
304
-
305
-		$firstGroup
306
-			->expects($this->once())
307
-			->method('searchUsers')
308
-			->with('MyCustomSearch')
309
-			->willReturn([
310
-				$this->createUserMock('user1', false),
311
-				$this->createUserMock('bob', true),
312
-				$this->createUserMock('user2', false),
313
-				$this->createUserMock('alice', true),
314
-			]);
315
-
316
-		$secondGroup
317
-			->expects($this->once())
318
-			->method('searchUsers')
319
-			->with('MyCustomSearch')
320
-			->willReturn([
321
-				$this->createUserMock('user2', false),
322
-				$this->createUserMock('joe', true),
323
-				$this->createUserMock('user3', false),
324
-				$this->createUserMock('jim', true),
325
-				$this->createUserMock('john', true),
326
-			]);
327
-
328
-
329
-		$expected = [
330
-			'users' => [
331
-				'user1' => ['id' => 'user1'],
332
-				'user2' => ['id' => 'user2'],
333
-				'user3' => ['id' => 'user3'],
334
-			],
335
-		];
336
-		$this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
337
-	}
338
-
339
-
340
-	public function testAddUserAlreadyExisting(): void {
341
-		$this->expectException(OCSException::class);
342
-		$this->expectExceptionCode(102);
343
-
344
-		$this->userManager
345
-			->expects($this->once())
346
-			->method('userExists')
347
-			->with('AlreadyExistingUser')
348
-			->willReturn(true);
349
-		$this->logger
350
-			->expects($this->once())
351
-			->method('error')
352
-			->with('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
353
-		$loggedInUser = $this->getMockBuilder(IUser::class)
354
-			->disableOriginalConstructor()
355
-			->getMock();
356
-		$loggedInUser
357
-			->expects($this->exactly(2))
358
-			->method('getUID')
359
-			->willReturn('adminUser');
360
-		$this->userSession
361
-			->expects($this->once())
362
-			->method('getUser')
363
-			->willReturn($loggedInUser);
364
-		$this->groupManager
365
-			->expects($this->once())
366
-			->method('isAdmin')
367
-			->with('adminUser')
368
-			->willReturn(true);
369
-
370
-		$this->api->addUser('AlreadyExistingUser', 'password', '', '', []);
371
-	}
372
-
373
-
374
-	public function testAddUserNonExistingGroup(): void {
375
-		$this->expectException(OCSException::class);
376
-		$this->expectExceptionMessage('Group NonExistingGroup does not exist');
377
-		$this->expectExceptionCode(104);
378
-
379
-		$this->userManager
380
-			->expects($this->once())
381
-			->method('userExists')
382
-			->with('NewUser')
383
-			->willReturn(false);
384
-		$loggedInUser = $this->getMockBuilder(IUser::class)
385
-			->disableOriginalConstructor()
386
-			->getMock();
387
-		$loggedInUser
388
-			->expects($this->exactly(2))
389
-			->method('getUID')
390
-			->willReturn('adminUser');
391
-		$this->userSession
392
-			->expects($this->once())
393
-			->method('getUser')
394
-			->willReturn($loggedInUser);
395
-		$this->groupManager
396
-			->expects($this->once())
397
-			->method('isAdmin')
398
-			->with('adminUser')
399
-			->willReturn(true);
400
-		$this->groupManager
401
-			->expects($this->once())
402
-			->method('groupExists')
403
-			->with('NonExistingGroup')
404
-			->willReturn(false);
405
-
406
-		$this->api->addUser('NewUser', 'pass', '', '', ['NonExistingGroup']);
407
-	}
408
-
409
-
410
-	public function testAddUserExistingGroupNonExistingGroup(): void {
411
-		$this->expectException(OCSException::class);
412
-		$this->expectExceptionMessage('Group NonExistingGroup does not exist');
413
-		$this->expectExceptionCode(104);
414
-
415
-		$this->userManager
416
-			->expects($this->once())
417
-			->method('userExists')
418
-			->with('NewUser')
419
-			->willReturn(false);
420
-		$loggedInUser = $this->getMockBuilder(IUser::class)
421
-			->disableOriginalConstructor()
422
-			->getMock();
423
-		$loggedInUser
424
-			->expects($this->exactly(2))
425
-			->method('getUID')
426
-			->willReturn('adminUser');
427
-		$this->userSession
428
-			->expects($this->once())
429
-			->method('getUser')
430
-			->willReturn($loggedInUser);
431
-		$this->groupManager
432
-			->expects($this->once())
433
-			->method('isAdmin')
434
-			->with('adminUser')
435
-			->willReturn(true);
436
-		$this->groupManager
437
-			->expects($this->exactly(2))
438
-			->method('groupExists')
439
-			->willReturnMap([
440
-				['ExistingGroup', true],
441
-				['NonExistingGroup', false]
442
-			]);
443
-
444
-		$this->api->addUser('NewUser', 'pass', '', '', ['ExistingGroup', 'NonExistingGroup']);
445
-	}
446
-
447
-	public function testAddUserSuccessful(): void {
448
-		$this->userManager
449
-			->expects($this->once())
450
-			->method('userExists')
451
-			->with('NewUser')
452
-			->willReturn(false);
453
-		$this->userManager
454
-			->expects($this->once())
455
-			->method('createUser')
456
-			->with('NewUser', 'PasswordOfTheNewUser');
457
-		$this->logger
458
-			->expects($this->once())
459
-			->method('info')
460
-			->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
461
-		$loggedInUser = $this->getMockBuilder(IUser::class)
462
-			->disableOriginalConstructor()
463
-			->getMock();
464
-		$loggedInUser
465
-			->expects($this->exactly(2))
466
-			->method('getUID')
467
-			->willReturn('adminUser');
468
-		$this->userSession
469
-			->expects($this->once())
470
-			->method('getUser')
471
-			->willReturn($loggedInUser);
472
-		$this->groupManager
473
-			->expects($this->once())
474
-			->method('isAdmin')
475
-			->with('adminUser')
476
-			->willReturn(true);
477
-
478
-		$this->assertTrue(key_exists(
479
-			'id',
480
-			$this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
481
-		));
482
-	}
483
-
484
-	public function testAddUserSuccessfulWithDisplayName(): void {
485
-		/**
486
-		 * @var UserController
487
-		 */
488
-		$api = $this->getMockBuilder(UsersController::class)
489
-			->setConstructorArgs([
490
-				'provisioning_api',
491
-				$this->request,
492
-				$this->userManager,
493
-				$this->config,
494
-				$this->groupManager,
495
-				$this->userSession,
496
-				$this->accountManager,
497
-				$this->subAdminManager,
498
-				$this->l10nFactory,
499
-				$this->rootFolder,
500
-				$this->urlGenerator,
501
-				$this->logger,
502
-				$this->newUserMailHelper,
503
-				$this->secureRandom,
504
-				$this->remoteWipe,
505
-				$this->knownUserService,
506
-				$this->eventDispatcher,
507
-				$this->phoneNumberUtil,
508
-				$this->appManager,
509
-			])
510
-			->onlyMethods(['editUser'])
511
-			->getMock();
512
-
513
-		$this->userManager
514
-			->expects($this->once())
515
-			->method('userExists')
516
-			->with('NewUser')
517
-			->willReturn(false);
518
-		$this->userManager
519
-			->expects($this->once())
520
-			->method('createUser')
521
-			->with('NewUser', 'PasswordOfTheNewUser');
522
-		$this->logger
523
-			->expects($this->once())
524
-			->method('info')
525
-			->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
526
-		$loggedInUser = $this->getMockBuilder(IUser::class)
527
-			->disableOriginalConstructor()
528
-			->getMock();
529
-		$loggedInUser
530
-			->expects($this->any())
531
-			->method('getUID')
532
-			->willReturn('adminUser');
533
-		$this->userSession
534
-			->expects($this->any())
535
-			->method('getUser')
536
-			->willReturn($loggedInUser);
537
-		$this->groupManager
538
-			->expects($this->once())
539
-			->method('isAdmin')
540
-			->with('adminUser')
541
-			->willReturn(true);
542
-		$api
543
-			->expects($this->once())
544
-			->method('editUser')
545
-			->with('NewUser', 'display', 'DisplayNameOfTheNewUser');
546
-
547
-		$this->assertTrue(key_exists(
548
-			'id',
549
-			$api->addUser('NewUser', 'PasswordOfTheNewUser', 'DisplayNameOfTheNewUser')->getData()
550
-		));
551
-	}
552
-
553
-	public function testAddUserSuccessfulGenerateUserID(): void {
554
-		$this->config
555
-			->expects($this->any())
556
-			->method('getAppValue')
557
-			->willReturnCallback(function ($appid, $key, $default) {
558
-				if ($key === 'newUser.generateUserID') {
559
-					return 'yes';
560
-				}
561
-				return null;
562
-			});
563
-		$this->userManager
564
-			->expects($this->any())
565
-			->method('userExists')
566
-			->with($this->anything())
567
-			->willReturn(false);
568
-		$this->userManager
569
-			->expects($this->once())
570
-			->method('createUser')
571
-			->with($this->anything(), 'PasswordOfTheNewUser');
572
-		$this->logger
573
-			->expects($this->once())
574
-			->method('info')
575
-			->with($this->stringStartsWith('Successful addUser call with userid: '), ['app' => 'ocs_api']);
576
-		$loggedInUser = $this->getMockBuilder(IUser::class)
577
-			->disableOriginalConstructor()
578
-			->getMock();
579
-		$loggedInUser
580
-			->expects($this->exactly(2))
581
-			->method('getUID')
582
-			->willReturn('adminUser');
583
-		$this->userSession
584
-			->expects($this->once())
585
-			->method('getUser')
586
-			->willReturn($loggedInUser);
587
-		$this->groupManager
588
-			->expects($this->once())
589
-			->method('isAdmin')
590
-			->with('adminUser')
591
-			->willReturn(true);
592
-		$this->secureRandom->expects($this->any())
593
-			->method('generate')
594
-			->with(10)
595
-			->willReturnCallback(function () {
596
-				return (string)rand(100000000, 999999999);
597
-			});
598
-
599
-		$this->assertTrue(key_exists(
600
-			'id',
601
-			$this->api->addUser('', 'PasswordOfTheNewUser')->getData()
602
-		));
603
-	}
604
-
605
-	public function testAddUserSuccessfulGeneratePassword(): void {
606
-		$this->userManager
607
-			->expects($this->once())
608
-			->method('userExists')
609
-			->with('NewUser')
610
-			->willReturn(false);
611
-		$newUser = $this->createMock(IUser::class);
612
-		$newUser->expects($this->once())
613
-			->method('setEMailAddress');
614
-		$this->userManager
615
-			->expects($this->once())
616
-			->method('createUser')
617
-			->willReturn($newUser);
618
-		$this->logger
619
-			->expects($this->once())
620
-			->method('info')
621
-			->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
622
-		$loggedInUser = $this->getMockBuilder(IUser::class)
623
-			->disableOriginalConstructor()
624
-			->getMock();
625
-		$loggedInUser
626
-			->expects($this->exactly(2))
627
-			->method('getUID')
628
-			->willReturn('adminUser');
629
-		$this->userSession
630
-			->expects($this->once())
631
-			->method('getUser')
632
-			->willReturn($loggedInUser);
633
-		$this->groupManager
634
-			->expects($this->once())
635
-			->method('isAdmin')
636
-			->with('adminUser')
637
-			->willReturn(true);
638
-		$this->eventDispatcher
639
-			->expects($this->once())
640
-			->method('dispatchTyped')
641
-			->with(new GenerateSecurePasswordEvent());
642
-
643
-		$this->assertTrue(key_exists(
644
-			'id',
645
-			$this->api->addUser('NewUser', '', '', 'foo@bar')->getData()
646
-		));
647
-	}
648
-
649
-
650
-	public function testAddUserFailedToGenerateUserID(): void {
651
-		$this->expectException(OCSException::class);
652
-		$this->expectExceptionMessage('Could not create non-existing user ID');
653
-		$this->expectExceptionCode(111);
654
-
655
-		$this->config
656
-			->expects($this->any())
657
-			->method('getAppValue')
658
-			->willReturnCallback(function ($appid, $key, $default) {
659
-				if ($key === 'newUser.generateUserID') {
660
-					return 'yes';
661
-				}
662
-				return null;
663
-			});
664
-		$this->userManager
665
-			->expects($this->any())
666
-			->method('userExists')
667
-			->with($this->anything())
668
-			->willReturn(true);
669
-		$this->userManager
670
-			->expects($this->never())
671
-			->method('createUser');
672
-		$loggedInUser = $this->getMockBuilder(IUser::class)
673
-			->disableOriginalConstructor()
674
-			->getMock();
675
-		$loggedInUser
676
-			->expects($this->exactly(2))
677
-			->method('getUID')
678
-			->willReturn('adminUser');
679
-		$this->userSession
680
-			->expects($this->once())
681
-			->method('getUser')
682
-			->willReturn($loggedInUser);
683
-		$this->groupManager
684
-			->expects($this->once())
685
-			->method('isAdmin')
686
-			->with('adminUser')
687
-			->willReturn(true);
688
-
689
-		$this->api->addUser('', 'PasswordOfTheNewUser')->getData();
690
-	}
691
-
692
-
693
-	public function testAddUserEmailRequired(): void {
694
-		$this->expectException(OCSException::class);
695
-		$this->expectExceptionMessage('Required email address was not provided');
696
-		$this->expectExceptionCode(110);
697
-
698
-		$this->config
699
-			->expects($this->any())
700
-			->method('getAppValue')
701
-			->willReturnCallback(function ($appid, $key, $default) {
702
-				if ($key === 'newUser.requireEmail') {
703
-					return 'yes';
704
-				}
705
-				return null;
706
-			});
707
-		$this->userManager
708
-			->expects($this->once())
709
-			->method('userExists')
710
-			->with('NewUser')
711
-			->willReturn(false);
712
-		$this->userManager
713
-			->expects($this->never())
714
-			->method('createUser');
715
-		$loggedInUser = $this->getMockBuilder(IUser::class)
716
-			->disableOriginalConstructor()
717
-			->getMock();
718
-		$loggedInUser
719
-			->expects($this->exactly(2))
720
-			->method('getUID')
721
-			->willReturn('adminUser');
722
-		$this->userSession
723
-			->expects($this->once())
724
-			->method('getUser')
725
-			->willReturn($loggedInUser);
726
-		$this->groupManager
727
-			->expects($this->once())
728
-			->method('isAdmin')
729
-			->with('adminUser')
730
-			->willReturn(true);
731
-
732
-		$this->assertTrue(key_exists(
733
-			'id',
734
-			$this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
735
-		));
736
-	}
737
-
738
-	public function testAddUserExistingGroup(): void {
739
-		$this->userManager
740
-			->expects($this->once())
741
-			->method('userExists')
742
-			->with('NewUser')
743
-			->willReturn(false);
744
-		$loggedInUser = $this->getMockBuilder(IUser::class)
745
-			->disableOriginalConstructor()
746
-			->getMock();
747
-		$loggedInUser
748
-			->expects($this->exactly(2))
749
-			->method('getUID')
750
-			->willReturn('adminUser');
751
-		$this->userSession
752
-			->expects($this->once())
753
-			->method('getUser')
754
-			->willReturn($loggedInUser);
755
-		$this->groupManager
756
-			->expects($this->once())
757
-			->method('isAdmin')
758
-			->with('adminUser')
759
-			->willReturn(true);
760
-		$this->groupManager
761
-			->expects($this->once())
762
-			->method('groupExists')
763
-			->with('ExistingGroup')
764
-			->willReturn(true);
765
-		$user = $this->getMockBuilder(IUser::class)
766
-			->disableOriginalConstructor()
767
-			->getMock();
768
-		$this->userManager
769
-			->expects($this->once())
770
-			->method('createUser')
771
-			->with('NewUser', 'PasswordOfTheNewUser')
772
-			->willReturn($user);
773
-		$group = $this->getMockBuilder('OCP\IGroup')
774
-			->disableOriginalConstructor()
775
-			->getMock();
776
-		$group
777
-			->expects($this->once())
778
-			->method('addUser')
779
-			->with($user);
780
-		$this->groupManager
781
-			->expects($this->once())
782
-			->method('get')
783
-			->with('ExistingGroup')
784
-			->willReturn($group);
785
-
786
-		$calls = [
787
-			['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
788
-			['Added userid NewUser to group ExistingGroup', ['app' => 'ocs_api']],
789
-		];
790
-		$this->logger
791
-			->expects($this->exactly(2))
792
-			->method('info')
793
-			->willReturnCallback(function () use (&$calls) {
794
-				$expected = array_shift($calls);
795
-				$this->assertEquals($expected, func_get_args());
796
-			});
797
-
798
-		$this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData());
799
-	}
800
-
801
-
802
-	public function testAddUserUnsuccessful(): void {
803
-		$this->expectException(OCSException::class);
804
-		$this->expectExceptionMessage('Bad request');
805
-		$this->expectExceptionCode(101);
806
-
807
-		$exception = new Exception('User backend not found.');
808
-		$this->userManager
809
-			->expects($this->once())
810
-			->method('userExists')
811
-			->with('NewUser')
812
-			->willReturn(false);
813
-		$this->userManager
814
-			->expects($this->once())
815
-			->method('createUser')
816
-			->with('NewUser', 'PasswordOfTheNewUser')
817
-			->will($this->throwException($exception));
818
-		$this->logger
819
-			->expects($this->once())
820
-			->method('error')
821
-			->with(
822
-				'Failed addUser attempt with exception.',
823
-				[
824
-					'app' => 'ocs_api',
825
-					'exception' => $exception
826
-				]
827
-			);
828
-		$loggedInUser = $this->getMockBuilder(IUser::class)
829
-			->disableOriginalConstructor()
830
-			->getMock();
831
-		$loggedInUser
832
-			->expects($this->exactly(2))
833
-			->method('getUID')
834
-			->willReturn('adminUser');
835
-		$this->userSession
836
-			->expects($this->once())
837
-			->method('getUser')
838
-			->willReturn($loggedInUser);
839
-		$this->groupManager
840
-			->expects($this->once())
841
-			->method('isAdmin')
842
-			->with('adminUser')
843
-			->willReturn(true);
844
-
845
-		$this->api->addUser('NewUser', 'PasswordOfTheNewUser');
846
-	}
847
-
848
-
849
-	public function testAddUserAsSubAdminNoGroup(): void {
850
-		$this->expectException(OCSException::class);
851
-		$this->expectExceptionMessage('No group specified (required for sub-admins)');
852
-		$this->expectExceptionCode(106);
853
-
854
-		$loggedInUser = $this->getMockBuilder(IUser::class)
855
-			->disableOriginalConstructor()
856
-			->getMock();
857
-		$loggedInUser
858
-			->expects($this->exactly(2))
859
-			->method('getUID')
860
-			->willReturn('regularUser');
861
-		$this->userSession
862
-			->expects($this->once())
863
-			->method('getUser')
864
-			->willReturn($loggedInUser);
865
-		$this->groupManager
866
-			->expects($this->once())
867
-			->method('isAdmin')
868
-			->with('regularUser')
869
-			->willReturn(false);
870
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
871
-			->disableOriginalConstructor()->getMock();
872
-		$this->groupManager
873
-			->expects($this->once())
874
-			->method('getSubAdmin')
875
-			->with()
876
-			->willReturn($subAdminManager);
877
-
878
-		$this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', []);
879
-	}
880
-
881
-
882
-	public function testAddUserAsSubAdminValidGroupNotSubAdmin(): void {
883
-		$this->expectException(OCSException::class);
884
-		$this->expectExceptionMessage('Insufficient privileges for group ExistingGroup');
885
-		$this->expectExceptionCode(105);
886
-
887
-		$loggedInUser = $this->getMockBuilder(IUser::class)
888
-			->disableOriginalConstructor()
889
-			->getMock();
890
-		$loggedInUser
891
-			->expects($this->exactly(2))
892
-			->method('getUID')
893
-			->willReturn('regularUser');
894
-		$this->userSession
895
-			->expects($this->once())
896
-			->method('getUser')
897
-			->willReturn($loggedInUser);
898
-		$this->groupManager
899
-			->expects($this->once())
900
-			->method('isAdmin')
901
-			->with('regularUser')
902
-			->willReturn(false);
903
-		$existingGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
904
-		$this->groupManager
905
-			->expects($this->once())
906
-			->method('get')
907
-			->with('ExistingGroup')
908
-			->willReturn($existingGroup);
909
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
910
-			->disableOriginalConstructor()->getMock();
911
-		$subAdminManager
912
-			->expects($this->once())
913
-			->method('isSubAdminOfGroup')
914
-			->with($loggedInUser, $existingGroup)
915
-			->willReturn(false);
916
-		$this->groupManager
917
-			->expects($this->once())
918
-			->method('getSubAdmin')
919
-			->with()
920
-			->willReturn($subAdminManager);
921
-		$this->groupManager
922
-			->expects($this->once())
923
-			->method('groupExists')
924
-			->with('ExistingGroup')
925
-			->willReturn(true);
926
-
927
-		$this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData();
928
-	}
929
-
930
-	public function testAddUserAsSubAdminExistingGroups(): void {
931
-		$this->userManager
932
-			->expects($this->once())
933
-			->method('userExists')
934
-			->with('NewUser')
935
-			->willReturn(false);
936
-		$loggedInUser = $this->getMockBuilder(IUser::class)
937
-			->disableOriginalConstructor()
938
-			->getMock();
939
-		$loggedInUser
940
-			->expects($this->exactly(2))
941
-			->method('getUID')
942
-			->willReturn('subAdminUser');
943
-		$this->userSession
944
-			->expects($this->once())
945
-			->method('getUser')
946
-			->willReturn($loggedInUser);
947
-		$this->groupManager
948
-			->expects($this->once())
949
-			->method('isAdmin')
950
-			->with('subAdminUser')
951
-			->willReturn(false);
952
-		$this->groupManager
953
-			->expects($this->exactly(2))
954
-			->method('groupExists')
955
-			->willReturnMap([
956
-				['ExistingGroup1', true],
957
-				['ExistingGroup2', true]
958
-			]);
959
-		$user = $this->getMockBuilder(IUser::class)
960
-			->disableOriginalConstructor()
961
-			->getMock();
962
-		$this->userManager
963
-			->expects($this->once())
964
-			->method('createUser')
965
-			->with('NewUser', 'PasswordOfTheNewUser')
966
-			->willReturn($user);
967
-		$existingGroup1 = $this->getMockBuilder('OCP\IGroup')
968
-			->disableOriginalConstructor()
969
-			->getMock();
970
-		$existingGroup2 = $this->getMockBuilder('OCP\IGroup')
971
-			->disableOriginalConstructor()
972
-			->getMock();
973
-		$existingGroup1
974
-			->expects($this->once())
975
-			->method('addUser')
976
-			->with($user);
977
-		$existingGroup2
978
-			->expects($this->once())
979
-			->method('addUser')
980
-			->with($user);
981
-		$this->groupManager
982
-			->expects($this->exactly(4))
983
-			->method('get')
984
-			->willReturnMap([
985
-				['ExistingGroup1', $existingGroup1],
986
-				['ExistingGroup2', $existingGroup2]
987
-			]);
988
-
989
-		$calls = [
990
-			['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
991
-			['Added userid NewUser to group ExistingGroup1', ['app' => 'ocs_api']],
992
-			['Added userid NewUser to group ExistingGroup2', ['app' => 'ocs_api']],
993
-		];
994
-		$this->logger
995
-			->expects($this->exactly(3))
996
-			->method('info')
997
-			->willReturnCallback(function () use (&$calls) {
998
-				$expected = array_shift($calls);
999
-				$this->assertEquals($expected, func_get_args());
1000
-			});
1001
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1002
-			->disableOriginalConstructor()->getMock();
1003
-		$this->groupManager
1004
-			->expects($this->once())
1005
-			->method('getSubAdmin')
1006
-			->willReturn($subAdminManager);
1007
-		$subAdminManager
1008
-			->expects($this->exactly(2))
1009
-			->method('isSubAdminOfGroup')
1010
-			->willReturnMap([
1011
-				[$loggedInUser, $existingGroup1, true],
1012
-				[$loggedInUser, $existingGroup2, true],
1013
-			]);
1014
-
1015
-		$this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup1', 'ExistingGroup2'])->getData());
1016
-	}
1017
-
1018
-
1019
-	public function testGetUserTargetDoesNotExist(): void {
1020
-		$this->expectException(OCSException::class);
1021
-		$this->expectExceptionMessage('User does not exist');
1022
-		$this->expectExceptionCode(404);
1023
-
1024
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1025
-			->disableOriginalConstructor()
1026
-			->getMock();
1027
-		$this->userSession
1028
-			->method('getUser')
1029
-			->willReturn($loggedInUser);
1030
-		$this->userManager
1031
-			->expects($this->once())
1032
-			->method('get')
1033
-			->with('UserToGet')
1034
-			->willReturn(null);
1035
-
1036
-		$this->api->getUser('UserToGet');
1037
-	}
1038
-
1039
-	public function testGetUserDataAsAdmin(): void {
1040
-		$group0 = $this->createMock(IGroup::class);
1041
-		$group1 = $this->createMock(IGroup::class);
1042
-		$group2 = $this->createMock(IGroup::class);
1043
-		$group3 = $this->createMock(IGroup::class);
1044
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1045
-			->disableOriginalConstructor()
1046
-			->getMock();
1047
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1048
-			->disableOriginalConstructor()
1049
-			->getMock();
1050
-		$loggedInUser
1051
-			->method('getUID')
1052
-			->willReturn('admin');
1053
-		$targetUser = $this->getMockBuilder(IUser::class)
1054
-			->disableOriginalConstructor()
1055
-			->getMock();
1056
-		$targetUser->expects($this->once())
1057
-			->method('getSystemEMailAddress')
1058
-			->willReturn('[email protected]');
1059
-		$this->userSession
1060
-			->method('getUser')
1061
-			->willReturn($loggedInUser);
1062
-		$this->userManager
1063
-			->method('get')
1064
-			->with('UID')
1065
-			->willReturn($targetUser);
1066
-		$this->groupManager
1067
-			->method('isAdmin')
1068
-			->with('admin')
1069
-			->willReturn(true);
1070
-		$this->groupManager
1071
-			->expects($this->any())
1072
-			->method('getUserGroups')
1073
-			->willReturn([$group0, $group1, $group2]);
1074
-		$this->groupManager
1075
-			->expects($this->once())
1076
-			->method('getSubAdmin')
1077
-			->willReturn($subAdminManager);
1078
-		$subAdminManager
1079
-			->expects($this->once())
1080
-			->method('getSubAdminsGroups')
1081
-			->willReturn([$group3]);
1082
-		$group0->expects($this->once())
1083
-			->method('getGID')
1084
-			->willReturn('group0');
1085
-		$group1->expects($this->once())
1086
-			->method('getGID')
1087
-			->willReturn('group1');
1088
-		$group2->expects($this->once())
1089
-			->method('getGID')
1090
-			->willReturn('group2');
1091
-		$group3->expects($this->once())
1092
-			->method('getGID')
1093
-			->willReturn('group3');
1094
-
1095
-		$this->mockAccount($targetUser, [
1096
-			IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1097
-			IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1098
-			IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1099
-			IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1100
-			IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1101
-			IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1102
-			IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1103
-			IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1104
-			IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1105
-			IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1106
-			IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1107
-		]);
1108
-		$this->config
1109
-			->method('getUserValue')
1110
-			->willReturnMap([
1111
-				['UID', 'core', 'enabled', 'true', 'true'],
1112
-			]);
1113
-		$this->api
1114
-			->expects($this->once())
1115
-			->method('fillStorageInfo')
1116
-			->with($targetUser)
1117
-			->willReturn(['DummyValue']);
1118
-
1119
-		$backend = $this->createMock(UserInterface::class);
1120
-		$backend->expects($this->any())
1121
-			->method('implementsActions')
1122
-			->willReturn(true);
1123
-
1124
-		$targetUser
1125
-			->expects($this->once())
1126
-			->method('getDisplayName')
1127
-			->willReturn('Demo User');
1128
-		$targetUser
1129
-			->expects($this->once())
1130
-			->method('getHome')
1131
-			->willReturn('/var/www/newtcloud/data/UID');
1132
-		$targetUser
1133
-			->expects($this->exactly(2))
1134
-			->method('getLastLogin')
1135
-			->willReturn(1521191471);
1136
-		$targetUser
1137
-			->expects($this->once())
1138
-			->method('getFirstLogin')
1139
-			->willReturn(1511191471);
1140
-		$targetUser
1141
-			->expects($this->once())
1142
-			->method('getBackendClassName')
1143
-			->willReturn('Database');
1144
-		$targetUser
1145
-			->expects($this->once())
1146
-			->method('getBackend')
1147
-			->willReturn($backend);
1148
-		$targetUser
1149
-			->method('getUID')
1150
-			->willReturn('UID');
1151
-
1152
-		$this->l10nFactory
1153
-			->expects($this->once())
1154
-			->method('getUserLanguage')
1155
-			->with($targetUser)
1156
-			->willReturn('de');
1157
-
1158
-		$expected = [
1159
-			'id' => 'UID',
1160
-			'enabled' => true,
1161
-			'storageLocation' => '/var/www/newtcloud/data/UID',
1162
-			'firstLoginTimestamp' => 1511191471,
1163
-			'lastLoginTimestamp' => 1521191471,
1164
-			'lastLogin' => 1521191471000,
1165
-			'backend' => 'Database',
1166
-			'subadmin' => ['group3'],
1167
-			'quota' => ['DummyValue'],
1168
-			'email' => '[email protected]',
1169
-			'displayname' => 'Demo User',
1170
-			'display-name' => 'Demo User',
1171
-			'phone' => 'phone',
1172
-			'address' => 'address',
1173
-			'website' => 'website',
1174
-			'twitter' => 'twitter',
1175
-			'fediverse' => 'fediverse',
1176
-			'groups' => ['group0', 'group1', 'group2'],
1177
-			'language' => 'de',
1178
-			'locale' => null,
1179
-			'backendCapabilities' => [
1180
-				'setDisplayName' => true,
1181
-				'setPassword' => true,
1182
-			],
1183
-			'additional_mail' => [],
1184
-			'organisation' => 'organisation',
1185
-			'role' => 'role',
1186
-			'headline' => 'headline',
1187
-			'biography' => 'biography',
1188
-			'profile_enabled' => '1',
1189
-			'notify_email' => null,
1190
-			'manager' => '',
1191
-			'pronouns' => 'they/them',
1192
-		];
1193
-		$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1194
-	}
1195
-
1196
-	public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
1197
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1198
-			->disableOriginalConstructor()
1199
-			->getMock();
1200
-		$loggedInUser
1201
-			->method('getUID')
1202
-			->willReturn('subadmin');
1203
-		$targetUser = $this->getMockBuilder(IUser::class)
1204
-			->disableOriginalConstructor()
1205
-			->getMock();
1206
-		$targetUser
1207
-			->expects($this->once())
1208
-			->method('getSystemEMailAddress')
1209
-			->willReturn('[email protected]');
1210
-		$this->userSession
1211
-			->method('getUser')
1212
-			->willReturn($loggedInUser);
1213
-		$this->userManager
1214
-			->method('get')
1215
-			->with('UID')
1216
-			->willReturn($targetUser);
1217
-		$this->groupManager
1218
-			->method('isAdmin')
1219
-			->with('subadmin')
1220
-			->willReturn(false);
1221
-		$this->groupManager
1222
-			->expects($this->any())
1223
-			->method('getUserGroups')
1224
-			->willReturn([]);
1225
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1226
-			->disableOriginalConstructor()
1227
-			->getMock();
1228
-		$subAdminManager
1229
-			->expects($this->once())
1230
-			->method('isUserAccessible')
1231
-			->with($loggedInUser, $targetUser)
1232
-			->willReturn(true);
1233
-		$subAdminManager
1234
-			->expects($this->once())
1235
-			->method('getSubAdminsGroups')
1236
-			->willReturn([]);
1237
-		$this->groupManager
1238
-			->expects($this->exactly(2))
1239
-			->method('getSubAdmin')
1240
-			->willReturn($subAdminManager);
1241
-		$this->config
1242
-			->method('getUserValue')
1243
-			->willReturnMap([
1244
-				['UID', 'core', 'enabled', 'true', 'true'],
1245
-			]);
1246
-		$this->api
1247
-			->expects($this->once())
1248
-			->method('fillStorageInfo')
1249
-			->with($targetUser)
1250
-			->willReturn(['DummyValue']);
1251
-
1252
-		$backend = $this->createMock(UserInterface::class);
1253
-		$backend->expects($this->any())
1254
-			->method('implementsActions')
1255
-			->willReturn(true);
1256
-
1257
-		$targetUser
1258
-			->expects($this->once())
1259
-			->method('getDisplayName')
1260
-			->willReturn('Demo User');
1261
-		$targetUser
1262
-			->expects($this->never())
1263
-			->method('getHome');
1264
-		$targetUser
1265
-			->expects($this->exactly(2))
1266
-			->method('getLastLogin')
1267
-			->willReturn(1521191471);
1268
-		$targetUser
1269
-			->expects($this->once())
1270
-			->method('getFirstLogin')
1271
-			->willReturn(1511191471);
1272
-		$targetUser
1273
-			->expects($this->once())
1274
-			->method('getBackendClassName')
1275
-			->willReturn('Database');
1276
-		$targetUser
1277
-			->expects($this->once())
1278
-			->method('getBackend')
1279
-			->willReturn($backend);
1280
-		$targetUser
1281
-			->method('getUID')
1282
-			->willReturn('UID');
1283
-
1284
-		$this->mockAccount($targetUser, [
1285
-			IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1286
-			IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1287
-			IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1288
-			IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1289
-			IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1290
-			IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1291
-			IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1292
-			IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1293
-			IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1294
-			IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1295
-			IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1296
-		]);
1297
-
1298
-		$this->l10nFactory
1299
-			->expects($this->once())
1300
-			->method('getUserLanguage')
1301
-			->with($targetUser)
1302
-			->willReturn('da');
1303
-
1304
-		$expected = [
1305
-			'id' => 'UID',
1306
-			'enabled' => true,
1307
-			'firstLoginTimestamp' => 1511191471,
1308
-			'lastLoginTimestamp' => 1521191471,
1309
-			'lastLogin' => 1521191471000,
1310
-			'backend' => 'Database',
1311
-			'subadmin' => [],
1312
-			'quota' => ['DummyValue'],
1313
-			'email' => '[email protected]',
1314
-			'displayname' => 'Demo User',
1315
-			'display-name' => 'Demo User',
1316
-			'phone' => 'phone',
1317
-			'address' => 'address',
1318
-			'website' => 'website',
1319
-			'twitter' => 'twitter',
1320
-			'fediverse' => 'fediverse',
1321
-			'groups' => [],
1322
-			'language' => 'da',
1323
-			'locale' => null,
1324
-			'backendCapabilities' => [
1325
-				'setDisplayName' => true,
1326
-				'setPassword' => true,
1327
-			],
1328
-			'additional_mail' => [],
1329
-			'organisation' => 'organisation',
1330
-			'role' => 'role',
1331
-			'headline' => 'headline',
1332
-			'biography' => 'biography',
1333
-			'profile_enabled' => '1',
1334
-			'notify_email' => null,
1335
-			'manager' => '',
1336
-			'pronouns' => 'they/them',
1337
-		];
1338
-		$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1339
-	}
1340
-
1341
-
1342
-
1343
-	public function testGetUserDataAsSubAdminAndUserIsNotAccessible(): void {
1344
-		$this->expectException(OCSException::class);
1345
-		$this->expectExceptionCode(998);
1346
-
1347
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1348
-			->disableOriginalConstructor()
1349
-			->getMock();
1350
-		$loggedInUser
1351
-			->expects($this->exactly(4))
1352
-			->method('getUID')
1353
-			->willReturn('subadmin');
1354
-		$targetUser = $this->getMockBuilder(IUser::class)
1355
-			->disableOriginalConstructor()
1356
-			->getMock();
1357
-		$this->userSession
1358
-			->method('getUser')
1359
-			->willReturn($loggedInUser);
1360
-		$this->userManager
1361
-			->expects($this->once())
1362
-			->method('get')
1363
-			->with('UserToGet')
1364
-			->willReturn($targetUser);
1365
-		$this->groupManager
1366
-			->expects($this->once())
1367
-			->method('isAdmin')
1368
-			->with('subadmin')
1369
-			->willReturn(false);
1370
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1371
-			->disableOriginalConstructor()
1372
-			->getMock();
1373
-		$subAdminManager
1374
-			->expects($this->once())
1375
-			->method('isUserAccessible')
1376
-			->with($loggedInUser, $targetUser)
1377
-			->willReturn(false);
1378
-		$this->groupManager
1379
-			->expects($this->once())
1380
-			->method('getSubAdmin')
1381
-			->willReturn($subAdminManager);
1382
-
1383
-		$this->invokePrivate($this->api, 'getUser', ['UserToGet']);
1384
-	}
1385
-
1386
-	public function testGetUserDataAsSubAdminSelfLookup(): void {
1387
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1388
-			->disableOriginalConstructor()
1389
-			->getMock();
1390
-		$loggedInUser
1391
-			->method('getUID')
1392
-			->willReturn('UID');
1393
-		$targetUser = $this->getMockBuilder(IUser::class)
1394
-			->disableOriginalConstructor()
1395
-			->getMock();
1396
-		$this->userSession
1397
-			->method('getUser')
1398
-			->willReturn($loggedInUser);
1399
-		$this->userManager
1400
-			->method('get')
1401
-			->with('UID')
1402
-			->willReturn($targetUser);
1403
-		$this->groupManager
1404
-			->method('isAdmin')
1405
-			->with('UID')
1406
-			->willReturn(false);
1407
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1408
-			->disableOriginalConstructor()
1409
-			->getMock();
1410
-		$subAdminManager
1411
-			->expects($this->once())
1412
-			->method('isUserAccessible')
1413
-			->with($loggedInUser, $targetUser)
1414
-			->willReturn(false);
1415
-		$subAdminManager
1416
-			->expects($this->once())
1417
-			->method('getSubAdminsGroups')
1418
-			->willReturn([]);
1419
-		$this->groupManager
1420
-			->expects($this->exactly(2))
1421
-			->method('getSubAdmin')
1422
-			->willReturn($subAdminManager);
1423
-		$this->groupManager
1424
-			->expects($this->any())
1425
-			->method('getUserGroups')
1426
-			->willReturn([]);
1427
-		$this->api
1428
-			->expects($this->once())
1429
-			->method('fillStorageInfo')
1430
-			->with($targetUser)
1431
-			->willReturn(['DummyValue']);
1432
-
1433
-		$backend = $this->createMock(UserInterface::class);
1434
-		$backend->expects($this->atLeastOnce())
1435
-			->method('implementsActions')
1436
-			->willReturn(false);
1437
-
1438
-		$targetUser
1439
-			->expects($this->once())
1440
-			->method('getDisplayName')
1441
-			->willReturn('Subadmin User');
1442
-		$targetUser
1443
-			->expects($this->once())
1444
-			->method('getSystemEMailAddress')
1445
-			->willReturn('[email protected]');
1446
-		$targetUser
1447
-			->method('getUID')
1448
-			->willReturn('UID');
1449
-		$targetUser
1450
-			->expects($this->never())
1451
-			->method('getHome');
1452
-		$targetUser
1453
-			->expects($this->exactly(2))
1454
-			->method('getLastLogin')
1455
-			->willReturn(1521191471);
1456
-		$targetUser
1457
-			->expects($this->once())
1458
-			->method('getFirstLogin')
1459
-			->willReturn(1511191471);
1460
-		$targetUser
1461
-			->expects($this->once())
1462
-			->method('getBackendClassName')
1463
-			->willReturn('Database');
1464
-		$targetUser
1465
-			->expects($this->once())
1466
-			->method('getBackend')
1467
-			->willReturn($backend);
1468
-		$this->mockAccount($targetUser, [
1469
-			IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1470
-			IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1471
-			IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1472
-			IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1473
-			IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1474
-			IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1475
-			IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1476
-			IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1477
-			IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1478
-			IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1479
-			IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1480
-		]);
1481
-
1482
-		$this->l10nFactory
1483
-			->expects($this->once())
1484
-			->method('getUserLanguage')
1485
-			->with($targetUser)
1486
-			->willReturn('ru');
1487
-
1488
-		$expected = [
1489
-			'id' => 'UID',
1490
-			'firstLoginTimestamp' => 1511191471,
1491
-			'lastLoginTimestamp' => 1521191471,
1492
-			'lastLogin' => 1521191471000,
1493
-			'backend' => 'Database',
1494
-			'subadmin' => [],
1495
-			'quota' => ['DummyValue'],
1496
-			'email' => '[email protected]',
1497
-			'displayname' => 'Subadmin User',
1498
-			'display-name' => 'Subadmin User',
1499
-			'phone' => 'phone',
1500
-			'address' => 'address',
1501
-			'website' => 'website',
1502
-			'twitter' => 'twitter',
1503
-			'fediverse' => 'fediverse',
1504
-			'groups' => [],
1505
-			'language' => 'ru',
1506
-			'locale' => null,
1507
-			'backendCapabilities' => [
1508
-				'setDisplayName' => false,
1509
-				'setPassword' => false,
1510
-			],
1511
-			'additional_mail' => [],
1512
-			'organisation' => 'organisation',
1513
-			'role' => 'role',
1514
-			'headline' => 'headline',
1515
-			'biography' => 'biography',
1516
-			'profile_enabled' => '1',
1517
-			'notify_email' => null,
1518
-			'manager' => '',
1519
-			'pronouns' => 'they/them',
1520
-		];
1521
-		$this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1522
-	}
1523
-
1524
-	public static function dataSearchByPhoneNumbers(): array {
1525
-		return [
1526
-			'Invalid country' => ['Not a country code', ['12345' => ['NaN']], 400, null, null, []],
1527
-			'No number to search' => ['DE', ['12345' => ['NaN']], 200, null, null, []],
1528
-			'Valid number but no match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1529
-			'Invalid number' => ['FR', ['12345' => ['0711 / 25 24 28-90']], 200, null, null, []],
1530
-			'Invalid and valid number' => ['DE', ['12345' => ['NaN', '0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1531
-			'Valid and invalid number' => ['DE', ['12345' => ['0711 / 25 24 28-90', 'NaN']], 200, ['+4971125242890'], [], []],
1532
-			'Valid number and a match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['12345' => 'admin@localhost']],
1533
-			'Same number twice, later hits' => ['DE', ['12345' => ['0711 / 25 24 28-90'], '23456' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['23456' => 'admin@localhost']],
1534
-		];
1535
-	}
1536
-
1537
-	/**
1538
-	 * @dataProvider dataSearchByPhoneNumbers
1539
-	 */
1540
-	public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected): void {
1541
-		$knownTo = 'knownTo';
1542
-		$user = $this->createMock(IUser::class);
1543
-		$user->method('getUID')
1544
-			->willReturn($knownTo);
1545
-		$this->userSession->method('getUser')
1546
-			->willReturn($user);
1547
-
1548
-		if ($searchUsers === null) {
1549
-			$this->accountManager->expects($this->never())
1550
-				->method('searchUsers');
1551
-		} else {
1552
-			$this->accountManager->expects($this->once())
1553
-				->method('searchUsers')
1554
-				->with(IAccountManager::PROPERTY_PHONE, $searchUsers)
1555
-				->willReturn($userMatches);
1556
-
1557
-			$this->knownUserService->expects($this->once())
1558
-				->method('deleteKnownTo')
1559
-				->with($knownTo);
1560
-
1561
-			$this->knownUserService->expects($this->exactly(count($expected)))
1562
-				->method('storeIsKnownToUser')
1563
-				->with($knownTo, $this->anything());
1564
-		}
1565
-
1566
-		$this->urlGenerator->method('getAbsoluteURL')
1567
-			->with('/')
1568
-			->willReturn('https://localhost/');
1569
-
1570
-		$response = $this->api->searchByPhoneNumbers($location, $search);
1571
-
1572
-		self::assertEquals($status, $response->getStatus());
1573
-		self::assertEquals($expected, $response->getData());
1574
-	}
1575
-
1576
-	public function testEditUserRegularUserSelfEditChangeDisplayName(): void {
1577
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1578
-			->disableOriginalConstructor()
1579
-			->getMock();
1580
-		$loggedInUser
1581
-			->expects($this->any())
1582
-			->method('getUID')
1583
-			->willReturn('UID');
1584
-		$targetUser = $this->getMockBuilder(IUser::class)
1585
-			->disableOriginalConstructor()
1586
-			->getMock();
1587
-		$this->userSession
1588
-			->expects($this->once())
1589
-			->method('getUser')
1590
-			->willReturn($loggedInUser);
1591
-		$this->userManager
1592
-			->expects($this->once())
1593
-			->method('get')
1594
-			->with('UserToEdit')
1595
-			->willReturn($targetUser);
1596
-		$targetUser
1597
-			->expects($this->once())
1598
-			->method('getBackend')
1599
-			->willReturn($this->createMock(ISetDisplayNameBackend::class));
1600
-		$targetUser
1601
-			->expects($this->once())
1602
-			->method('setDisplayName')
1603
-			->with('NewDisplayName')
1604
-			->willReturn(true);
1605
-		$targetUser
1606
-			->expects($this->any())
1607
-			->method('getUID')
1608
-			->willReturn('UID');
1609
-
1610
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'display', 'NewDisplayName')->getData());
1611
-	}
1612
-
1613
-	public function testEditUserRegularUserSelfEditChangeEmailValid(): void {
1614
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1615
-			->disableOriginalConstructor()
1616
-			->getMock();
1617
-		$loggedInUser
1618
-			->expects($this->any())
1619
-			->method('getUID')
1620
-			->willReturn('UID');
1621
-		$targetUser = $this->getMockBuilder(IUser::class)
1622
-			->disableOriginalConstructor()
1623
-			->getMock();
1624
-		$this->userSession
1625
-			->expects($this->once())
1626
-			->method('getUser')
1627
-			->willReturn($loggedInUser);
1628
-		$this->userManager
1629
-			->expects($this->once())
1630
-			->method('get')
1631
-			->with('UserToEdit')
1632
-			->willReturn($targetUser);
1633
-		$targetUser
1634
-			->expects($this->once())
1635
-			->method('setEMailAddress')
1636
-			->with('[email protected]');
1637
-		$targetUser
1638
-			->expects($this->any())
1639
-			->method('getUID')
1640
-			->willReturn('UID');
1641
-
1642
-		$backend = $this->createMock(UserInterface::class);
1643
-		$targetUser
1644
-			->expects($this->any())
1645
-			->method('getBackend')
1646
-			->willReturn($backend);
1647
-
1648
-		$this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1649
-
1650
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'email', '[email protected]')->getData());
1651
-	}
1652
-
1653
-	public function testEditUserRegularUserSelfEditAddAdditionalEmailValid(): void {
1654
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1655
-			->disableOriginalConstructor()
1656
-			->getMock();
1657
-		$loggedInUser
1658
-			->expects($this->any())
1659
-			->method('getUID')
1660
-			->willReturn('UID');
1661
-		$targetUser = $this->getMockBuilder(IUser::class)
1662
-			->disableOriginalConstructor()
1663
-			->getMock();
1664
-		$this->userSession
1665
-			->expects($this->once())
1666
-			->method('getUser')
1667
-			->willReturn($loggedInUser);
1668
-		$this->userManager
1669
-			->expects($this->once())
1670
-			->method('get')
1671
-			->with('UserToEdit')
1672
-			->willReturn($targetUser);
1673
-		$targetUser
1674
-			->expects($this->any())
1675
-			->method('getUID')
1676
-			->willReturn('UID');
1677
-
1678
-		$backend = $this->createMock(UserInterface::class);
1679
-		$targetUser
1680
-			->expects($this->any())
1681
-			->method('getBackend')
1682
-			->willReturn($backend);
1683
-
1684
-		$userAccount = $this->createMock(IAccount::class);
1685
-
1686
-		$this->accountManager
1687
-			->expects($this->once())
1688
-			->method('getAccount')
1689
-			->with($targetUser)
1690
-			->willReturn($userAccount);
1691
-		$this->accountManager
1692
-			->expects($this->once())
1693
-			->method('updateAccount')
1694
-			->with($userAccount);
1695
-
1696
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData());
1697
-	}
1698
-
1699
-	public function testEditUserRegularUserSelfEditAddAdditionalEmailMainAddress(): void {
1700
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1701
-			->disableOriginalConstructor()
1702
-			->getMock();
1703
-		$loggedInUser
1704
-			->expects($this->any())
1705
-			->method('getUID')
1706
-			->willReturn('UID');
1707
-		$targetUser = $this->getMockBuilder(IUser::class)
1708
-			->disableOriginalConstructor()
1709
-			->getMock();
1710
-		$this->userSession
1711
-			->expects($this->once())
1712
-			->method('getUser')
1713
-			->willReturn($loggedInUser);
1714
-		$this->userManager
1715
-			->expects($this->once())
1716
-			->method('get')
1717
-			->with('UserToEdit')
1718
-			->willReturn($targetUser);
1719
-		$targetUser
1720
-			->expects($this->any())
1721
-			->method('getUID')
1722
-			->willReturn('UID');
1723
-
1724
-		$backend = $this->createMock(UserInterface::class);
1725
-		$targetUser
1726
-			->expects($this->any())
1727
-			->method('getBackend')
1728
-			->willReturn($backend);
1729
-		$targetUser
1730
-			->expects($this->any())
1731
-			->method('getSystemEMailAddress')
1732
-			->willReturn('[email protected]');
1733
-
1734
-		$userAccount = $this->createMock(IAccount::class);
1735
-
1736
-		$this->accountManager
1737
-			->expects($this->never())
1738
-			->method('getAccount')
1739
-			->with($targetUser)
1740
-			->willReturn($userAccount);
1741
-		$this->accountManager
1742
-			->expects($this->never())
1743
-			->method('updateAccount')
1744
-			->with($userAccount);
1745
-
1746
-		$this->expectException(OCSException::class);
1747
-		$this->expectExceptionCode(101);
1748
-		$this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1749
-	}
1750
-
1751
-	public function testEditUserRegularUserSelfEditAddAdditionalEmailDuplicate(): void {
1752
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1753
-			->disableOriginalConstructor()
1754
-			->getMock();
1755
-		$loggedInUser
1756
-			->expects($this->any())
1757
-			->method('getUID')
1758
-			->willReturn('UID');
1759
-		$targetUser = $this->getMockBuilder(IUser::class)
1760
-			->disableOriginalConstructor()
1761
-			->getMock();
1762
-		$this->userSession
1763
-			->expects($this->once())
1764
-			->method('getUser')
1765
-			->willReturn($loggedInUser);
1766
-		$this->userManager
1767
-			->expects($this->once())
1768
-			->method('get')
1769
-			->with('UserToEdit')
1770
-			->willReturn($targetUser);
1771
-		$targetUser
1772
-			->expects($this->any())
1773
-			->method('getUID')
1774
-			->willReturn('UID');
1775
-
1776
-		$backend = $this->createMock(UserInterface::class);
1777
-		$targetUser
1778
-			->expects($this->any())
1779
-			->method('getBackend')
1780
-			->willReturn($backend);
1781
-
1782
-		$property = $this->createMock(IAccountProperty::class);
1783
-		$property->method('getValue')
1784
-			->willReturn('[email protected]');
1785
-		$collection = $this->createMock(IAccountPropertyCollection::class);
1786
-		$collection->method('getPropertyByValue')
1787
-			->with('[email protected]')
1788
-			->willReturn($property);
1789
-
1790
-		$userAccount = $this->createMock(IAccount::class);
1791
-		$userAccount->method('getPropertyCollection')
1792
-			->with(IAccountManager::COLLECTION_EMAIL)
1793
-			->willReturn($collection);
1794
-
1795
-		$this->accountManager
1796
-			->expects($this->once())
1797
-			->method('getAccount')
1798
-			->with($targetUser)
1799
-			->willReturn($userAccount);
1800
-		$this->accountManager
1801
-			->expects($this->never())
1802
-			->method('updateAccount')
1803
-			->with($userAccount);
1804
-
1805
-		$this->expectException(OCSException::class);
1806
-		$this->expectExceptionCode(101);
1807
-		$this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1808
-	}
1809
-
1810
-	public function testEditUserRegularUserSelfEditChangeEmailInvalid(): void {
1811
-		$this->expectException(OCSException::class);
1812
-		$this->expectExceptionCode(101);
1813
-
1814
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1815
-			->disableOriginalConstructor()
1816
-			->getMock();
1817
-		$loggedInUser
1818
-			->expects($this->any())
1819
-			->method('getUID')
1820
-			->willReturn('UID');
1821
-		$targetUser = $this->getMockBuilder(IUser::class)
1822
-			->disableOriginalConstructor()
1823
-			->getMock();
1824
-		$this->userSession
1825
-			->expects($this->once())
1826
-			->method('getUser')
1827
-			->willReturn($loggedInUser);
1828
-		$this->userManager
1829
-			->expects($this->once())
1830
-			->method('get')
1831
-			->with('UserToEdit')
1832
-			->willReturn($targetUser);
1833
-		$targetUser
1834
-			->expects($this->any())
1835
-			->method('getUID')
1836
-			->willReturn('UID');
1837
-
1838
-		$backend = $this->createMock(UserInterface::class);
1839
-		$targetUser
1840
-			->expects($this->any())
1841
-			->method('getBackend')
1842
-			->willReturn($backend);
1843
-
1844
-		$this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1845
-
1846
-		$this->api->editUser('UserToEdit', 'email', 'demo.org');
1847
-	}
1848
-
1849
-	public static function selfEditChangePropertyProvider(): array {
1850
-		return [
1851
-			[IAccountManager::PROPERTY_TWITTER, '@oldtwitter', '@newtwitter'],
1852
-			[IAccountManager::PROPERTY_FEDIVERSE, '@[email protected]', '@[email protected]'],
1853
-			[IAccountManager::PROPERTY_PHONE, '1234', '12345'],
1854
-			[IAccountManager::PROPERTY_ADDRESS, 'Something street 2', 'Another street 3'],
1855
-			[IAccountManager::PROPERTY_WEBSITE, 'https://examplesite1', 'https://examplesite2'],
1856
-			[IAccountManager::PROPERTY_ORGANISATION, 'Organisation A', 'Organisation B'],
1857
-			[IAccountManager::PROPERTY_ROLE, 'Human', 'Alien'],
1858
-			[IAccountManager::PROPERTY_HEADLINE, 'Hi', 'Hello'],
1859
-			[IAccountManager::PROPERTY_BIOGRAPHY, 'A biography', 'Another biography'],
1860
-			[IAccountManager::PROPERTY_PROFILE_ENABLED, '1', '0'],
1861
-			[IAccountManager::PROPERTY_PRONOUNS, 'they/them', 'he/him'],
1862
-		];
1863
-	}
1864
-
1865
-	/**
1866
-	 * @dataProvider selfEditChangePropertyProvider
1867
-	 */
1868
-	public function testEditUserRegularUserSelfEditChangeProperty($propertyName, $oldValue, $newValue): void {
1869
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1870
-			->disableOriginalConstructor()
1871
-			->getMock();
1872
-		$loggedInUser
1873
-			->expects($this->any())
1874
-			->method('getUID')
1875
-			->willReturn('UID');
1876
-		$this->userSession
1877
-			->expects($this->once())
1878
-			->method('getUser')
1879
-			->willReturn($loggedInUser);
1880
-		$this->userManager
1881
-			->expects($this->once())
1882
-			->method('get')
1883
-			->with('UserToEdit')
1884
-			->willReturn($loggedInUser);
1885
-
1886
-		$backend = $this->createMock(UserInterface::class);
1887
-		$loggedInUser
1888
-			->expects($this->any())
1889
-			->method('getBackend')
1890
-			->willReturn($backend);
1891
-
1892
-		$propertyMock = $this->createMock(IAccountProperty::class);
1893
-		$propertyMock->expects($this->any())
1894
-			->method('getName')
1895
-			->willReturn($propertyName);
1896
-		$propertyMock->expects($this->any())
1897
-			->method('getValue')
1898
-			->willReturn($oldValue);
1899
-		$propertyMock->expects($this->once())
1900
-			->method('setValue')
1901
-			->with($newValue)
1902
-			->willReturnSelf();
1903
-		$propertyMock->expects($this->any())
1904
-			->method('getScope')
1905
-			->willReturn(IAccountManager::SCOPE_LOCAL);
1906
-
1907
-		$accountMock = $this->createMock(IAccount::class);
1908
-		$accountMock->expects($this->any())
1909
-			->method('getProperty')
1910
-			->with($propertyName)
1911
-			->willReturn($propertyMock);
1912
-
1913
-		$this->accountManager->expects($this->atLeastOnce())
1914
-			->method('getAccount')
1915
-			->with($loggedInUser)
1916
-			->willReturn($accountMock);
1917
-		$this->accountManager->expects($this->once())
1918
-			->method('updateAccount')
1919
-			->with($accountMock);
1920
-
1921
-		$this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName, $newValue)->getData());
1922
-	}
1923
-
1924
-	public function selfEditChangePropertyScopeProvider() {
1925
-		return [
1926
-			[IAccountManager::PROPERTY_AVATAR, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1927
-			[IAccountManager::PROPERTY_DISPLAYNAME, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1928
-			[IAccountManager::PROPERTY_EMAIL, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1929
-			[IAccountManager::PROPERTY_TWITTER, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1930
-			[IAccountManager::PROPERTY_FEDIVERSE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1931
-			[IAccountManager::PROPERTY_PHONE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1932
-			[IAccountManager::PROPERTY_ADDRESS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1933
-			[IAccountManager::PROPERTY_WEBSITE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1934
-			[IAccountManager::PROPERTY_ORGANISATION, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1935
-			[IAccountManager::PROPERTY_ROLE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1936
-			[IAccountManager::PROPERTY_HEADLINE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1937
-			[IAccountManager::PROPERTY_BIOGRAPHY, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1938
-			[IAccountManager::PROPERTY_PROFILE_ENABLED, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1939
-			[IAccountManager::PROPERTY_PRONOUNS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1940
-		];
1941
-	}
1942
-
1943
-	/**
1944
-	 * @dataProvider selfEditChangePropertyProvider
1945
-	 */
1946
-	public function testEditUserRegularUserSelfEditChangePropertyScope($propertyName, $oldScope, $newScope): void {
1947
-		$loggedInUser = $this->getMockBuilder(IUser::class)
1948
-			->disableOriginalConstructor()
1949
-			->getMock();
1950
-		$loggedInUser
1951
-			->expects($this->any())
1952
-			->method('getUID')
1953
-			->willReturn('UID');
1954
-		$this->userSession
1955
-			->expects($this->once())
1956
-			->method('getUser')
1957
-			->willReturn($loggedInUser);
1958
-		$this->userManager
1959
-			->expects($this->once())
1960
-			->method('get')
1961
-			->with('UserToEdit')
1962
-			->willReturn($loggedInUser);
1963
-
1964
-		$backend = $this->createMock(UserInterface::class);
1965
-		$loggedInUser
1966
-			->expects($this->any())
1967
-			->method('getBackend')
1968
-			->willReturn($backend);
1969
-
1970
-		$propertyMock = $this->createMock(IAccountProperty::class);
1971
-		$propertyMock->expects($this->any())
1972
-			->method('getName')
1973
-			->willReturn($propertyName);
1974
-		$propertyMock->expects($this->any())
1975
-			->method('getValue')
1976
-			->willReturn('somevalue');
1977
-		$propertyMock->expects($this->any())
1978
-			->method('getScope')
1979
-			->willReturn($oldScope);
1980
-		$propertyMock->expects($this->atLeastOnce())
1981
-			->method('setScope')
1982
-			->with($newScope)
1983
-			->willReturnSelf();
1984
-
1985
-		$accountMock = $this->createMock(IAccount::class);
1986
-		$accountMock->expects($this->any())
1987
-			->method('getProperty')
1988
-			->with($propertyName)
1989
-			->willReturn($propertyMock);
1990
-
1991
-		$this->accountManager->expects($this->atLeastOnce())
1992
-			->method('getAccount')
1993
-			->with($loggedInUser)
1994
-			->willReturn($accountMock);
1995
-		$this->accountManager->expects($this->once())
1996
-			->method('updateAccount')
1997
-			->with($accountMock);
1998
-
1999
-		$this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName . 'Scope', $newScope)->getData());
2000
-	}
2001
-
2002
-	public function testEditUserRegularUserSelfEditChangePassword(): void {
2003
-		$loggedInUser = $this->getMockBuilder(IUser::class)
2004
-			->disableOriginalConstructor()
2005
-			->getMock();
2006
-		$loggedInUser
2007
-			->expects($this->any())
2008
-			->method('getUID')
2009
-			->willReturn('UID');
2010
-		$targetUser = $this->getMockBuilder(IUser::class)
2011
-			->disableOriginalConstructor()
2012
-			->getMock();
2013
-		$this->userSession
2014
-			->expects($this->once())
2015
-			->method('getUser')
2016
-			->willReturn($loggedInUser);
2017
-		$this->userManager
2018
-			->expects($this->once())
2019
-			->method('get')
2020
-			->with('UserToEdit')
2021
-			->willReturn($targetUser);
2022
-		$targetUser
2023
-			->expects($this->once())
2024
-			->method('canChangePassword')
2025
-			->willReturn(true);
2026
-		$targetUser
2027
-			->expects($this->once())
2028
-			->method('setPassword')
2029
-			->with('NewPassword');
2030
-		$targetUser
2031
-			->expects($this->any())
2032
-			->method('getUID')
2033
-			->willReturn('UID');
2034
-
2035
-		$backend = $this->createMock(UserInterface::class);
2036
-		$targetUser
2037
-			->expects($this->any())
2038
-			->method('getBackend')
2039
-			->willReturn($backend);
2040
-
2041
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'password', 'NewPassword')->getData());
2042
-	}
2043
-
2044
-
2045
-
2046
-	public function testEditUserRegularUserSelfEditChangeQuota(): void {
2047
-		$this->expectException(OCSException::class);
2048
-		$this->expectExceptionCode(113);
2049
-
2050
-		$loggedInUser = $this->getMockBuilder(IUser::class)
2051
-			->disableOriginalConstructor()
2052
-			->getMock();
2053
-		$loggedInUser
2054
-			->expects($this->any())
2055
-			->method('getUID')
2056
-			->willReturn('UID');
2057
-		$targetUser = $this->getMockBuilder(IUser::class)
2058
-			->disableOriginalConstructor()
2059
-			->getMock();
2060
-		$this->userSession
2061
-			->expects($this->once())
2062
-			->method('getUser')
2063
-			->willReturn($loggedInUser);
2064
-		$this->userManager
2065
-			->expects($this->once())
2066
-			->method('get')
2067
-			->with('UserToEdit')
2068
-			->willReturn($targetUser);
2069
-		$targetUser
2070
-			->expects($this->any())
2071
-			->method('getUID')
2072
-			->willReturn('UID');
2073
-
2074
-		$backend = $this->createMock(UserInterface::class);
2075
-		$targetUser
2076
-			->expects($this->any())
2077
-			->method('getBackend')
2078
-			->willReturn($backend);
2079
-
2080
-		$this->api->editUser('UserToEdit', 'quota', 'NewQuota');
2081
-	}
2082
-
2083
-	public function testEditUserAdminUserSelfEditChangeValidQuota(): void {
2084
-		$this->config
2085
-			->expects($this->once())
2086
-			->method('getAppValue')
2087
-			->willReturnCallback(function ($appid, $key, $default) {
2088
-				if ($key === 'max_quota') {
2089
-					return '-1';
2090
-				}
2091
-				return null;
2092
-			});
2093
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2094
-		$loggedInUser
2095
-			->expects($this->any())
2096
-			->method('getUID')
2097
-			->willReturn('UID');
2098
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2099
-		$targetUser->expects($this->once())
2100
-			->method('setQuota')
2101
-			->with('2.9 MB');
2102
-		$this->userSession
2103
-			->expects($this->once())
2104
-			->method('getUser')
2105
-			->willReturn($loggedInUser);
2106
-		$this->userManager
2107
-			->expects($this->once())
2108
-			->method('get')
2109
-			->with('UserToEdit')
2110
-			->willReturn($targetUser);
2111
-		$this->groupManager
2112
-			->expects($this->exactly(3))
2113
-			->method('isAdmin')
2114
-			->with('UID')
2115
-			->willReturn(true);
2116
-		$targetUser
2117
-			->expects($this->any())
2118
-			->method('getUID')
2119
-			->willReturn('UID');
2120
-
2121
-		$backend = $this->createMock(UserInterface::class);
2122
-		$targetUser
2123
-			->expects($this->any())
2124
-			->method('getBackend')
2125
-			->willReturn($backend);
2126
-
2127
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2128
-	}
2129
-
2130
-
2131
-
2132
-	public function testEditUserAdminUserSelfEditChangeInvalidQuota(): void {
2133
-		$this->expectException(OCSException::class);
2134
-		$this->expectExceptionMessage('Invalid quota value: ABC');
2135
-		$this->expectExceptionCode(101);
2136
-
2137
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2138
-		$loggedInUser
2139
-			->expects($this->any())
2140
-			->method('getUID')
2141
-			->willReturn('UID');
2142
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2143
-		$this->userSession
2144
-			->expects($this->once())
2145
-			->method('getUser')
2146
-			->willReturn($loggedInUser);
2147
-		$this->userManager
2148
-			->expects($this->once())
2149
-			->method('get')
2150
-			->with('UserToEdit')
2151
-			->willReturn($targetUser);
2152
-		$this->groupManager
2153
-			->expects($this->exactly(3))
2154
-			->method('isAdmin')
2155
-			->with('UID')
2156
-			->willReturn(true);
2157
-		$targetUser
2158
-			->expects($this->any())
2159
-			->method('getUID')
2160
-			->willReturn('UID');
2161
-
2162
-		$backend = $this->createMock(UserInterface::class);
2163
-		$targetUser
2164
-			->expects($this->any())
2165
-			->method('getBackend')
2166
-			->willReturn($backend);
2167
-
2168
-		$this->api->editUser('UserToEdit', 'quota', 'ABC');
2169
-	}
2170
-
2171
-	public function testEditUserAdminUserEditChangeValidQuota(): void {
2172
-		$this->config
2173
-			->expects($this->once())
2174
-			->method('getAppValue')
2175
-			->willReturnCallback(function ($appid, $key, $default) {
2176
-				if ($key === 'max_quota') {
2177
-					return '-1';
2178
-				}
2179
-				return null;
2180
-			});
2181
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2182
-		$loggedInUser
2183
-			->expects($this->any())
2184
-			->method('getUID')
2185
-			->willReturn('admin');
2186
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2187
-		$targetUser->expects($this->once())
2188
-			->method('setQuota')
2189
-			->with('2.9 MB');
2190
-		$this->userSession
2191
-			->expects($this->once())
2192
-			->method('getUser')
2193
-			->willReturn($loggedInUser);
2194
-		$this->userManager
2195
-			->expects($this->once())
2196
-			->method('get')
2197
-			->with('UserToEdit')
2198
-			->willReturn($targetUser);
2199
-		$this->groupManager
2200
-			->expects($this->once())
2201
-			->method('isAdmin')
2202
-			->with('admin')
2203
-			->willReturn(true);
2204
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2205
-			->disableOriginalConstructor()
2206
-			->getMock();
2207
-		$this->groupManager
2208
-			->expects($this->once())
2209
-			->method('getSubAdmin')
2210
-			->willReturn($subAdminManager);
2211
-		$targetUser
2212
-			->expects($this->any())
2213
-			->method('getUID')
2214
-			->willReturn('UID');
2215
-
2216
-		$backend = $this->createMock(UserInterface::class);
2217
-		$targetUser
2218
-			->expects($this->any())
2219
-			->method('getBackend')
2220
-			->willReturn($backend);
2221
-
2222
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2223
-	}
2224
-
2225
-	public function testEditUserSelfEditChangeLanguage(): void {
2226
-		$this->l10nFactory->expects($this->once())
2227
-			->method('findAvailableLanguages')
2228
-			->willReturn(['en', 'de', 'sv']);
2229
-		$this->config->expects($this->any())
2230
-			->method('getSystemValue')
2231
-			->willReturnMap([
2232
-				['allow_user_to_change_display_name', true, true],
2233
-				['force_language', false, false],
2234
-			]);
2235
-
2236
-		$loggedInUser = $this->createMock(IUser::class);
2237
-		$loggedInUser
2238
-			->expects($this->any())
2239
-			->method('getUID')
2240
-			->willReturn('UserToEdit');
2241
-		$targetUser = $this->createMock(IUser::class);
2242
-		$this->config->expects($this->once())
2243
-			->method('setUserValue')
2244
-			->with('UserToEdit', 'core', 'lang', 'de');
2245
-		$this->userSession
2246
-			->expects($this->once())
2247
-			->method('getUser')
2248
-			->willReturn($loggedInUser);
2249
-		$this->userManager
2250
-			->expects($this->once())
2251
-			->method('get')
2252
-			->with('UserToEdit')
2253
-			->willReturn($targetUser);
2254
-		$this->groupManager
2255
-			->expects($this->atLeastOnce())
2256
-			->method('isAdmin')
2257
-			->with('UserToEdit')
2258
-			->willReturn(false);
2259
-		$targetUser
2260
-			->expects($this->any())
2261
-			->method('getUID')
2262
-			->willReturn('UserToEdit');
2263
-
2264
-		$backend = $this->createMock(UserInterface::class);
2265
-		$targetUser
2266
-			->expects($this->any())
2267
-			->method('getBackend')
2268
-			->willReturn($backend);
2269
-
2270
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2271
-	}
2272
-
2273
-	public static function dataEditUserSelfEditChangeLanguageButForced(): array {
2274
-		return [
2275
-			['de'],
2276
-			[true],
2277
-		];
2278
-	}
2279
-
2280
-	/**
2281
-	 * @dataProvider dataEditUserSelfEditChangeLanguageButForced
2282
-	 */
2283
-	public function testEditUserSelfEditChangeLanguageButForced($forced): void {
2284
-		$this->expectException(OCSException::class);
2285
-
2286
-		$this->config->expects($this->any())
2287
-			->method('getSystemValue')
2288
-			->willReturnMap([
2289
-				['allow_user_to_change_display_name', true, true],
2290
-				['force_language', false, $forced],
2291
-			]);
2292
-
2293
-		$loggedInUser = $this->createMock(IUser::class);
2294
-		$loggedInUser
2295
-			->expects($this->any())
2296
-			->method('getUID')
2297
-			->willReturn('UserToEdit');
2298
-		$targetUser = $this->createMock(IUser::class);
2299
-		$this->config->expects($this->never())
2300
-			->method('setUserValue');
2301
-		$this->userSession
2302
-			->expects($this->once())
2303
-			->method('getUser')
2304
-			->willReturn($loggedInUser);
2305
-		$this->userManager
2306
-			->expects($this->once())
2307
-			->method('get')
2308
-			->with('UserToEdit')
2309
-			->willReturn($targetUser);
2310
-		$this->groupManager
2311
-			->expects($this->atLeastOnce())
2312
-			->method('isAdmin')
2313
-			->with('UserToEdit')
2314
-			->willReturn(false);
2315
-		$targetUser
2316
-			->expects($this->any())
2317
-			->method('getUID')
2318
-			->willReturn('UserToEdit');
2319
-
2320
-		$backend = $this->createMock(UserInterface::class);
2321
-		$targetUser
2322
-			->expects($this->any())
2323
-			->method('getBackend')
2324
-			->willReturn($backend);
2325
-
2326
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2327
-	}
2328
-
2329
-	public function testEditUserAdminEditChangeLanguage(): void {
2330
-		$this->l10nFactory->expects($this->once())
2331
-			->method('findAvailableLanguages')
2332
-			->willReturn(['en', 'de', 'sv']);
2333
-
2334
-		$loggedInUser = $this->createMock(IUser::class);
2335
-		$loggedInUser
2336
-			->expects($this->any())
2337
-			->method('getUID')
2338
-			->willReturn('admin');
2339
-		$targetUser = $this->createMock(IUser::class);
2340
-		$this->config->expects($this->once())
2341
-			->method('setUserValue')
2342
-			->with('UserToEdit', 'core', 'lang', 'de');
2343
-		$this->userSession
2344
-			->expects($this->once())
2345
-			->method('getUser')
2346
-			->willReturn($loggedInUser);
2347
-		$this->userManager
2348
-			->expects($this->once())
2349
-			->method('get')
2350
-			->with('UserToEdit')
2351
-			->willReturn($targetUser);
2352
-		$this->groupManager
2353
-			->expects($this->once())
2354
-			->method('isAdmin')
2355
-			->with('admin')
2356
-			->willReturn(true);
2357
-		$subAdminManager = $this->createMock(SubAdmin::class);
2358
-		$this->groupManager
2359
-			->expects($this->once())
2360
-			->method('getSubAdmin')
2361
-			->willReturn($subAdminManager);
2362
-		$targetUser
2363
-			->expects($this->any())
2364
-			->method('getUID')
2365
-			->willReturn('UserToEdit');
2366
-
2367
-		$backend = $this->createMock(UserInterface::class);
2368
-		$targetUser
2369
-			->expects($this->any())
2370
-			->method('getBackend')
2371
-			->willReturn($backend);
2372
-
2373
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2374
-	}
2375
-
2376
-	/**
2377
-	 * @dataProvider dataEditUserSelfEditChangeLanguageButForced
2378
-	 */
2379
-	public function testEditUserAdminEditChangeLanguageInvalidLanguage(): void {
2380
-		$this->expectException(OCSException::class);
2381
-
2382
-
2383
-		$this->l10nFactory->expects($this->once())
2384
-			->method('findAvailableLanguages')
2385
-			->willReturn(['en', 'de', 'sv']);
2386
-
2387
-		$loggedInUser = $this->createMock(IUser::class);
2388
-		$loggedInUser
2389
-			->expects($this->any())
2390
-			->method('getUID')
2391
-			->willReturn('admin');
2392
-		$targetUser = $this->createMock(IUser::class);
2393
-		$this->config->expects($this->never())
2394
-			->method('setUserValue');
2395
-		$this->userSession
2396
-			->expects($this->once())
2397
-			->method('getUser')
2398
-			->willReturn($loggedInUser);
2399
-		$this->userManager
2400
-			->expects($this->once())
2401
-			->method('get')
2402
-			->with('UserToEdit')
2403
-			->willReturn($targetUser);
2404
-		$this->groupManager
2405
-			->expects($this->once())
2406
-			->method('isAdmin')
2407
-			->with('admin')
2408
-			->willReturn(true);
2409
-		$subAdminManager = $this->createMock(SubAdmin::class);
2410
-		$this->groupManager
2411
-			->expects($this->once())
2412
-			->method('getSubAdmin')
2413
-			->willReturn($subAdminManager);
2414
-		$targetUser
2415
-			->expects($this->any())
2416
-			->method('getUID')
2417
-			->willReturn('UserToEdit');
2418
-
2419
-		$backend = $this->createMock(UserInterface::class);
2420
-		$targetUser
2421
-			->expects($this->any())
2422
-			->method('getBackend')
2423
-			->willReturn($backend);
2424
-
2425
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'ru')->getData());
2426
-	}
2427
-
2428
-	public function testEditUserSubadminUserAccessible(): void {
2429
-		$this->config
2430
-			->expects($this->once())
2431
-			->method('getAppValue')
2432
-			->willReturnCallback(function ($appid, $key, $default) {
2433
-				if ($key === 'max_quota') {
2434
-					return '-1';
2435
-				}
2436
-				return null;
2437
-			});
2438
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2439
-		$loggedInUser
2440
-			->expects($this->any())
2441
-			->method('getUID')
2442
-			->willReturn('subadmin');
2443
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2444
-		$targetUser->expects($this->once())
2445
-			->method('setQuota')
2446
-			->with('2.9 MB');
2447
-		$this->userSession
2448
-			->expects($this->once())
2449
-			->method('getUser')
2450
-			->willReturn($loggedInUser);
2451
-		$this->userManager
2452
-			->expects($this->once())
2453
-			->method('get')
2454
-			->with('UserToEdit')
2455
-			->willReturn($targetUser);
2456
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2457
-			->disableOriginalConstructor()
2458
-			->getMock();
2459
-		$subAdminManager
2460
-			->expects($this->once())
2461
-			->method('isUserAccessible')
2462
-			->with($loggedInUser, $targetUser)
2463
-			->willReturn(true);
2464
-		$this->groupManager
2465
-			->expects($this->once())
2466
-			->method('getSubAdmin')
2467
-			->willReturn($subAdminManager);
2468
-		$targetUser
2469
-			->expects($this->any())
2470
-			->method('getUID')
2471
-			->willReturn('UID');
2472
-
2473
-		$backend = $this->createMock(UserInterface::class);
2474
-		$targetUser
2475
-			->expects($this->any())
2476
-			->method('getBackend')
2477
-			->willReturn($backend);
2478
-
2479
-		$this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2480
-	}
2481
-
2482
-
2483
-	public function testEditUserSubadminUserInaccessible(): void {
2484
-		$this->expectException(OCSException::class);
2485
-		$this->expectExceptionCode(998);
2486
-
2487
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2488
-		$loggedInUser
2489
-			->expects($this->any())
2490
-			->method('getUID')
2491
-			->willReturn('subadmin');
2492
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2493
-		$this->userSession
2494
-			->expects($this->once())
2495
-			->method('getUser')
2496
-			->willReturn($loggedInUser);
2497
-		$this->userManager
2498
-			->expects($this->once())
2499
-			->method('get')
2500
-			->with('UserToEdit')
2501
-			->willReturn($targetUser);
2502
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2503
-			->disableOriginalConstructor()
2504
-			->getMock();
2505
-		$subAdminManager
2506
-			->expects($this->once())
2507
-			->method('isUserAccessible')
2508
-			->with($loggedInUser, $targetUser)
2509
-			->willReturn(false);
2510
-		$this->groupManager
2511
-			->expects($this->once())
2512
-			->method('getSubAdmin')
2513
-			->willReturn($subAdminManager);
2514
-		$targetUser
2515
-			->expects($this->any())
2516
-			->method('getUID')
2517
-			->willReturn('UID');
2518
-
2519
-		$this->api->editUser('UserToEdit', 'quota', 'value');
2520
-	}
2521
-
2522
-
2523
-	public function testDeleteUserNotExistingUser(): void {
2524
-		$this->expectException(OCSException::class);
2525
-		$this->expectExceptionCode(998);
2526
-
2527
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2528
-		$loggedInUser
2529
-			->expects($this->any())
2530
-			->method('getUID')
2531
-			->willReturn('UserToEdit');
2532
-		$this->userSession
2533
-			->expects($this->once())
2534
-			->method('getUser')
2535
-			->willReturn($loggedInUser);
2536
-		$this->userManager
2537
-			->expects($this->once())
2538
-			->method('get')
2539
-			->with('UserToDelete')
2540
-			->willReturn(null);
2541
-
2542
-		$this->api->deleteUser('UserToDelete');
2543
-	}
2544
-
2545
-
2546
-	public function testDeleteUserSelf(): void {
2547
-		$this->expectException(OCSException::class);
2548
-		$this->expectExceptionCode(101);
2549
-
2550
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2551
-		$loggedInUser
2552
-			->expects($this->any())
2553
-			->method('getUID')
2554
-			->willReturn('UID');
2555
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2556
-		$targetUser
2557
-			->expects($this->once())
2558
-			->method('getUID')
2559
-			->willReturn('UID');
2560
-		$this->userSession
2561
-			->expects($this->once())
2562
-			->method('getUser')
2563
-			->willReturn($loggedInUser);
2564
-		$this->userManager
2565
-			->expects($this->once())
2566
-			->method('get')
2567
-			->with('UserToDelete')
2568
-			->willReturn($targetUser);
2569
-
2570
-		$this->api->deleteUser('UserToDelete');
2571
-	}
2572
-
2573
-	public function testDeleteSuccessfulUserAsAdmin(): void {
2574
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2575
-		$loggedInUser
2576
-			->expects($this->any())
2577
-			->method('getUID')
2578
-			->willReturn('admin');
2579
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2580
-		$targetUser
2581
-			->expects($this->once())
2582
-			->method('getUID')
2583
-			->willReturn('UID');
2584
-		$this->userSession
2585
-			->expects($this->once())
2586
-			->method('getUser')
2587
-			->willReturn($loggedInUser);
2588
-		$this->userManager
2589
-			->expects($this->once())
2590
-			->method('get')
2591
-			->with('UserToDelete')
2592
-			->willReturn($targetUser);
2593
-		$this->groupManager
2594
-			->expects($this->once())
2595
-			->method('isAdmin')
2596
-			->with('admin')
2597
-			->willReturn(true);
2598
-		$targetUser
2599
-			->expects($this->once())
2600
-			->method('delete')
2601
-			->willReturn(true);
2602
-
2603
-		$this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2604
-	}
2605
-
2606
-
2607
-	public function testDeleteUnsuccessfulUserAsAdmin(): void {
2608
-		$this->expectException(OCSException::class);
2609
-		$this->expectExceptionCode(101);
2610
-
2611
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2612
-		$loggedInUser
2613
-			->expects($this->any())
2614
-			->method('getUID')
2615
-			->willReturn('admin');
2616
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2617
-		$targetUser
2618
-			->expects($this->once())
2619
-			->method('getUID')
2620
-			->willReturn('UID');
2621
-		$this->userSession
2622
-			->expects($this->once())
2623
-			->method('getUser')
2624
-			->willReturn($loggedInUser);
2625
-		$this->userManager
2626
-			->expects($this->once())
2627
-			->method('get')
2628
-			->with('UserToDelete')
2629
-			->willReturn($targetUser);
2630
-		$this->groupManager
2631
-			->expects($this->once())
2632
-			->method('isAdmin')
2633
-			->with('admin')
2634
-			->willReturn(true);
2635
-		$targetUser
2636
-			->expects($this->once())
2637
-			->method('delete')
2638
-			->willReturn(false);
2639
-
2640
-		$this->api->deleteUser('UserToDelete');
2641
-	}
2642
-
2643
-	public function testDeleteSuccessfulUserAsSubadmin(): void {
2644
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2645
-		$loggedInUser
2646
-			->expects($this->any())
2647
-			->method('getUID')
2648
-			->willReturn('subadmin');
2649
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2650
-		$targetUser
2651
-			->expects($this->once())
2652
-			->method('getUID')
2653
-			->willReturn('UID');
2654
-		$this->userSession
2655
-			->expects($this->once())
2656
-			->method('getUser')
2657
-			->willReturn($loggedInUser);
2658
-		$this->userManager
2659
-			->expects($this->once())
2660
-			->method('get')
2661
-			->with('UserToDelete')
2662
-			->willReturn($targetUser);
2663
-		$this->groupManager
2664
-			->expects($this->once())
2665
-			->method('isAdmin')
2666
-			->with('subadmin')
2667
-			->willReturn(false);
2668
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2669
-			->disableOriginalConstructor()->getMock();
2670
-		$subAdminManager
2671
-			->expects($this->once())
2672
-			->method('isUserAccessible')
2673
-			->with($loggedInUser, $targetUser)
2674
-			->willReturn(true);
2675
-		$this->groupManager
2676
-			->expects($this->once())
2677
-			->method('getSubAdmin')
2678
-			->willReturn($subAdminManager);
2679
-		$targetUser
2680
-			->expects($this->once())
2681
-			->method('delete')
2682
-			->willReturn(true);
2683
-
2684
-		$this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2685
-	}
2686
-
2687
-
2688
-	public function testDeleteUnsuccessfulUserAsSubadmin(): void {
2689
-		$this->expectException(OCSException::class);
2690
-		$this->expectExceptionCode(101);
2691
-
2692
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2693
-		$loggedInUser
2694
-			->expects($this->any())
2695
-			->method('getUID')
2696
-			->willReturn('subadmin');
2697
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2698
-		$targetUser
2699
-			->expects($this->once())
2700
-			->method('getUID')
2701
-			->willReturn('UID');
2702
-		$this->userSession
2703
-			->expects($this->once())
2704
-			->method('getUser')
2705
-			->willReturn($loggedInUser);
2706
-		$this->userManager
2707
-			->expects($this->once())
2708
-			->method('get')
2709
-			->with('UserToDelete')
2710
-			->willReturn($targetUser);
2711
-		$this->groupManager
2712
-			->expects($this->once())
2713
-			->method('isAdmin')
2714
-			->with('subadmin')
2715
-			->willReturn(false);
2716
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2717
-			->disableOriginalConstructor()->getMock();
2718
-		$subAdminManager
2719
-			->expects($this->once())
2720
-			->method('isUserAccessible')
2721
-			->with($loggedInUser, $targetUser)
2722
-			->willReturn(true);
2723
-		$this->groupManager
2724
-			->expects($this->once())
2725
-			->method('getSubAdmin')
2726
-			->willReturn($subAdminManager);
2727
-		$targetUser
2728
-			->expects($this->once())
2729
-			->method('delete')
2730
-			->willReturn(false);
2731
-
2732
-		$this->api->deleteUser('UserToDelete');
2733
-	}
2734
-
2735
-
2736
-	public function testDeleteUserAsSubAdminAndUserIsNotAccessible(): void {
2737
-		$this->expectException(OCSException::class);
2738
-		$this->expectExceptionCode(998);
2739
-
2740
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2741
-		$loggedInUser
2742
-			->expects($this->any())
2743
-			->method('getUID')
2744
-			->willReturn('subadmin');
2745
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2746
-		$targetUser
2747
-			->expects($this->once())
2748
-			->method('getUID')
2749
-			->willReturn('UID');
2750
-		$this->userSession
2751
-			->expects($this->once())
2752
-			->method('getUser')
2753
-			->willReturn($loggedInUser);
2754
-		$this->userManager
2755
-			->expects($this->once())
2756
-			->method('get')
2757
-			->with('UserToDelete')
2758
-			->willReturn($targetUser);
2759
-		$this->groupManager
2760
-			->expects($this->once())
2761
-			->method('isAdmin')
2762
-			->with('subadmin')
2763
-			->willReturn(false);
2764
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2765
-			->disableOriginalConstructor()->getMock();
2766
-		$subAdminManager
2767
-			->expects($this->once())
2768
-			->method('isUserAccessible')
2769
-			->with($loggedInUser, $targetUser)
2770
-			->willReturn(false);
2771
-		$this->groupManager
2772
-			->expects($this->once())
2773
-			->method('getSubAdmin')
2774
-			->willReturn($subAdminManager);
2775
-
2776
-		$this->api->deleteUser('UserToDelete');
2777
-	}
2778
-
2779
-
2780
-	public function testGetUsersGroupsTargetUserNotExisting(): void {
2781
-		$this->expectException(OCSException::class);
2782
-		$this->expectExceptionCode(998);
2783
-
2784
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2785
-		$this->userSession
2786
-			->expects($this->once())
2787
-			->method('getUser')
2788
-			->willReturn($loggedInUser);
2789
-
2790
-		$this->api->getUsersGroups('UserToLookup');
2791
-	}
2792
-
2793
-	public function testGetUsersGroupsSelfTargetted(): void {
2794
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2795
-		$loggedInUser
2796
-			->expects($this->exactly(3))
2797
-			->method('getUID')
2798
-			->willReturn('UserToLookup');
2799
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2800
-		$targetUser
2801
-			->expects($this->once())
2802
-			->method('getUID')
2803
-			->willReturn('UserToLookup');
2804
-		$this->userSession
2805
-			->expects($this->once())
2806
-			->method('getUser')
2807
-			->willReturn($loggedInUser);
2808
-		$this->userManager
2809
-			->expects($this->once())
2810
-			->method('get')
2811
-			->with('UserToLookup')
2812
-			->willReturn($targetUser);
2813
-		$this->groupManager
2814
-			->expects($this->once())
2815
-			->method('getUserGroupIds')
2816
-			->with($targetUser)
2817
-			->willReturn(['DummyValue']);
2818
-
2819
-		$this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2820
-	}
2821
-
2822
-	public function testGetUsersGroupsForAdminUser(): void {
2823
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2824
-		$loggedInUser
2825
-			->expects($this->exactly(3))
2826
-			->method('getUID')
2827
-			->willReturn('admin');
2828
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2829
-		$targetUser
2830
-			->expects($this->once())
2831
-			->method('getUID')
2832
-			->willReturn('UserToLookup');
2833
-		$this->userSession
2834
-			->expects($this->once())
2835
-			->method('getUser')
2836
-			->willReturn($loggedInUser);
2837
-		$this->userManager
2838
-			->expects($this->once())
2839
-			->method('get')
2840
-			->with('UserToLookup')
2841
-			->willReturn($targetUser);
2842
-		$this->groupManager
2843
-			->expects($this->once())
2844
-			->method('getUserGroupIds')
2845
-			->with($targetUser)
2846
-			->willReturn(['DummyValue']);
2847
-		$this->groupManager
2848
-			->expects($this->once())
2849
-			->method('isAdmin')
2850
-			->with('admin')
2851
-			->willReturn(true);
2852
-
2853
-		$this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2854
-	}
2855
-
2856
-	public function testGetUsersGroupsForSubAdminUserAndUserIsAccessible(): void {
2857
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2858
-		$loggedInUser
2859
-			->expects($this->exactly(3))
2860
-			->method('getUID')
2861
-			->willReturn('subadmin');
2862
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2863
-		$targetUser
2864
-			->expects($this->once())
2865
-			->method('getUID')
2866
-			->willReturn('UserToLookup');
2867
-		$this->userSession
2868
-			->expects($this->once())
2869
-			->method('getUser')
2870
-			->willReturn($loggedInUser);
2871
-		$this->userManager
2872
-			->expects($this->once())
2873
-			->method('get')
2874
-			->with('UserToLookup')
2875
-			->willReturn($targetUser);
2876
-		$this->groupManager
2877
-			->expects($this->once())
2878
-			->method('isAdmin')
2879
-			->with('subadmin')
2880
-			->willReturn(false);
2881
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2882
-			->disableOriginalConstructor()->getMock();
2883
-		$subAdminManager
2884
-			->expects($this->once())
2885
-			->method('isUserAccessible')
2886
-			->with($loggedInUser, $targetUser)
2887
-			->willReturn(true);
2888
-		$this->groupManager
2889
-			->expects($this->once())
2890
-			->method('getSubAdmin')
2891
-			->willReturn($subAdminManager);
2892
-		$group1 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2893
-		$group1
2894
-			->expects($this->any())
2895
-			->method('getGID')
2896
-			->willReturn('Group1');
2897
-		$group2 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2898
-		$group2
2899
-			->expects($this->any())
2900
-			->method('getGID')
2901
-			->willReturn('Group2');
2902
-		$subAdminManager
2903
-			->expects($this->once())
2904
-			->method('getSubAdminsGroups')
2905
-			->with($loggedInUser)
2906
-			->willReturn([$group1, $group2]);
2907
-		$this->groupManager
2908
-			->expects($this->any())
2909
-			->method('getUserGroupIds')
2910
-			->with($targetUser)
2911
-			->willReturn(['Group1']);
2912
-
2913
-		$this->assertEquals(['groups' => ['Group1']], $this->api->getUsersGroups('UserToLookup')->getData());
2914
-	}
2915
-
2916
-
2917
-	public function testGetUsersGroupsForSubAdminUserAndUserIsInaccessible(): void {
2918
-		$this->expectException(OCSException::class);
2919
-		$this->expectExceptionCode(998);
2920
-
2921
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2922
-		$loggedInUser
2923
-			->expects($this->exactly(3))
2924
-			->method('getUID')
2925
-			->willReturn('subadmin');
2926
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2927
-		$targetUser
2928
-			->expects($this->once())
2929
-			->method('getUID')
2930
-			->willReturn('UserToLookup');
2931
-		$this->userSession
2932
-			->expects($this->once())
2933
-			->method('getUser')
2934
-			->willReturn($loggedInUser);
2935
-		$this->userManager
2936
-			->expects($this->once())
2937
-			->method('get')
2938
-			->with('UserToLookup')
2939
-			->willReturn($targetUser);
2940
-		$this->groupManager
2941
-			->expects($this->once())
2942
-			->method('isAdmin')
2943
-			->with('subadmin')
2944
-			->willReturn(false);
2945
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2946
-			->disableOriginalConstructor()->getMock();
2947
-		$subAdminManager
2948
-			->expects($this->once())
2949
-			->method('isUserAccessible')
2950
-			->with($loggedInUser, $targetUser)
2951
-			->willReturn(false);
2952
-		$this->groupManager
2953
-			->expects($this->once())
2954
-			->method('getSubAdmin')
2955
-			->willReturn($subAdminManager);
2956
-		$this->groupManager
2957
-			->expects($this->any())
2958
-			->method('getUserGroupIds')
2959
-			->with($targetUser)
2960
-			->willReturn(['Group1']);
2961
-
2962
-		$this->api->getUsersGroups('UserToLookup');
2963
-	}
2964
-
2965
-
2966
-	public function testAddToGroupWithTargetGroupNotExisting(): void {
2967
-		$this->expectException(OCSException::class);
2968
-		$this->expectExceptionCode(102);
2969
-
2970
-		$this->groupManager->expects($this->once())
2971
-			->method('get')
2972
-			->with('GroupToAddTo')
2973
-			->willReturn(null);
2974
-
2975
-		$this->api->addToGroup('TargetUser', 'GroupToAddTo');
2976
-	}
2977
-
2978
-
2979
-	public function testAddToGroupWithNoGroupSpecified(): void {
2980
-		$this->expectException(OCSException::class);
2981
-		$this->expectExceptionCode(101);
2982
-
2983
-		$this->api->addToGroup('TargetUser');
2984
-	}
2985
-
2986
-
2987
-	public function testAddToGroupWithTargetUserNotExisting(): void {
2988
-		$this->expectException(OCSException::class);
2989
-		$this->expectExceptionCode(103);
2990
-
2991
-		$targetGroup = $this->createMock(IGroup::class);
2992
-		$this->groupManager->expects($this->once())
2993
-			->method('get')
2994
-			->with('GroupToAddTo')
2995
-			->willReturn($targetGroup);
2996
-
2997
-		$this->api->addToGroup('TargetUser', 'GroupToAddTo');
2998
-	}
2999
-
3000
-
3001
-	public function testAddToGroupNoSubadmin(): void {
3002
-		$this->expectException(OCSException::class);
3003
-		$this->expectExceptionCode(104);
3004
-
3005
-		$targetUser = $this->createMock(IUser::class);
3006
-		$loggedInUser = $this->createMock(IUser::class);
3007
-		$loggedInUser->expects($this->exactly(2))
3008
-			->method('getUID')
3009
-			->willReturn('subadmin');
3010
-
3011
-		$targetGroup = $this->createMock(IGroup::class);
3012
-		$targetGroup->expects($this->never())
3013
-			->method('addUser')
3014
-			->with($targetUser);
3015
-
3016
-		$this->groupManager->expects($this->once())
3017
-			->method('get')
3018
-			->with('GroupToAddTo')
3019
-			->willReturn($targetGroup);
3020
-
3021
-
3022
-		$subAdminManager = $this->createMock(SubAdmin::class);
3023
-		$subAdminManager->expects($this->once())
3024
-			->method('isSubAdminOfGroup')
3025
-			->with($loggedInUser, $targetGroup)
3026
-			->willReturn(false);
3027
-
3028
-		$this->groupManager->expects($this->once())
3029
-			->method('getSubAdmin')
3030
-			->willReturn($subAdminManager);
3031
-		$this->groupManager->expects($this->once())
3032
-			->method('isAdmin')
3033
-			->with('subadmin')
3034
-			->willReturn(false);
3035
-
3036
-		$this->userManager->expects($this->once())
3037
-			->method('get')
3038
-			->with('TargetUser')
3039
-			->willReturn($targetUser);
3040
-
3041
-		$this->userSession->expects($this->once())
3042
-			->method('getUser')
3043
-			->willReturn($loggedInUser);
3044
-
3045
-		$this->api->addToGroup('TargetUser', 'GroupToAddTo');
3046
-	}
3047
-
3048
-	public function testAddToGroupSuccessAsSubadmin(): void {
3049
-		$targetUser = $this->createMock(IUser::class);
3050
-		$loggedInUser = $this->createMock(IUser::class);
3051
-		$loggedInUser->expects($this->exactly(2))
3052
-			->method('getUID')
3053
-			->willReturn('subadmin');
3054
-
3055
-		$targetGroup = $this->createMock(IGroup::class);
3056
-		$targetGroup->expects($this->once())
3057
-			->method('addUser')
3058
-			->with($targetUser);
3059
-
3060
-		$this->groupManager->expects($this->once())
3061
-			->method('get')
3062
-			->with('GroupToAddTo')
3063
-			->willReturn($targetGroup);
50
+    protected IUserManager&MockObject $userManager;
51
+    protected IConfig&MockObject $config;
52
+    protected Manager&MockObject $groupManager;
53
+    protected IUserSession&MockObject $userSession;
54
+    protected LoggerInterface&MockObject $logger;
55
+    protected UsersController&MockObject $api;
56
+    protected IAccountManager&MockObject $accountManager;
57
+    protected ISubAdmin&MockObject $subAdminManager;
58
+    protected IURLGenerator&MockObject $urlGenerator;
59
+    protected IRequest&MockObject $request;
60
+    private IFactory&MockObject $l10nFactory;
61
+    private NewUserMailHelper&MockObject $newUserMailHelper;
62
+    private ISecureRandom&MockObject $secureRandom;
63
+    private RemoteWipe&MockObject $remoteWipe;
64
+    private KnownUserService&MockObject $knownUserService;
65
+    private IEventDispatcher&MockObject $eventDispatcher;
66
+    private IRootFolder $rootFolder;
67
+    private IPhoneNumberUtil $phoneNumberUtil;
68
+    private IAppManager $appManager;
69
+
70
+    protected function setUp(): void {
71
+        parent::setUp();
72
+
73
+        $this->userManager = $this->createMock(IUserManager::class);
74
+        $this->config = $this->createMock(IConfig::class);
75
+        $this->groupManager = $this->createMock(Manager::class);
76
+        $this->userSession = $this->createMock(IUserSession::class);
77
+        $this->logger = $this->createMock(LoggerInterface::class);
78
+        $this->request = $this->createMock(IRequest::class);
79
+        $this->accountManager = $this->createMock(IAccountManager::class);
80
+        $this->subAdminManager = $this->createMock(ISubAdmin::class);
81
+        $this->urlGenerator = $this->createMock(IURLGenerator::class);
82
+        $this->l10nFactory = $this->createMock(IFactory::class);
83
+        $this->newUserMailHelper = $this->createMock(NewUserMailHelper::class);
84
+        $this->secureRandom = $this->createMock(ISecureRandom::class);
85
+        $this->remoteWipe = $this->createMock(RemoteWipe::class);
86
+        $this->knownUserService = $this->createMock(KnownUserService::class);
87
+        $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
88
+        $this->phoneNumberUtil = new PhoneNumberUtil();
89
+        $this->appManager = $this->createMock(IAppManager::class);
90
+        $this->rootFolder = $this->createMock(IRootFolder::class);
91
+
92
+        $l10n = $this->createMock(IL10N::class);
93
+        $l10n->method('t')->willReturnCallback(fn (string $txt, array $replacement = []) => sprintf($txt, ...$replacement));
94
+        $this->l10nFactory->method('get')->with('provisioning_api')->willReturn($l10n);
95
+
96
+        $this->api = $this->getMockBuilder(UsersController::class)
97
+            ->setConstructorArgs([
98
+                'provisioning_api',
99
+                $this->request,
100
+                $this->userManager,
101
+                $this->config,
102
+                $this->groupManager,
103
+                $this->userSession,
104
+                $this->accountManager,
105
+                $this->subAdminManager,
106
+                $this->l10nFactory,
107
+                $this->rootFolder,
108
+                $this->urlGenerator,
109
+                $this->logger,
110
+                $this->newUserMailHelper,
111
+                $this->secureRandom,
112
+                $this->remoteWipe,
113
+                $this->knownUserService,
114
+                $this->eventDispatcher,
115
+                $this->phoneNumberUtil,
116
+                $this->appManager,
117
+            ])
118
+            ->onlyMethods(['fillStorageInfo'])
119
+            ->getMock();
120
+    }
121
+
122
+    public function testGetUsersAsAdmin(): void {
123
+        $loggedInUser = $this->getMockBuilder(IUser::class)
124
+            ->disableOriginalConstructor()
125
+            ->getMock();
126
+        $loggedInUser
127
+            ->expects($this->once())
128
+            ->method('getUID')
129
+            ->willReturn('admin');
130
+        $this->userSession
131
+            ->expects($this->once())
132
+            ->method('getUser')
133
+            ->willReturn($loggedInUser);
134
+        $this->groupManager
135
+            ->expects($this->once())
136
+            ->method('isAdmin')
137
+            ->willReturn(true);
138
+        $this->userManager
139
+            ->expects($this->once())
140
+            ->method('search')
141
+            ->with('MyCustomSearch')
142
+            ->willReturn(['Admin' => [], 'Foo' => [], 'Bar' => []]);
143
+
144
+        $expected = [
145
+            'users' => [
146
+                'Admin',
147
+                'Foo',
148
+                'Bar',
149
+            ],
150
+        ];
151
+        $this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
152
+    }
153
+
154
+    public function testGetUsersAsSubAdmin(): void {
155
+        $loggedInUser = $this->getMockBuilder(IUser::class)
156
+            ->disableOriginalConstructor()
157
+            ->getMock();
158
+        $loggedInUser
159
+            ->expects($this->once())
160
+            ->method('getUID')
161
+            ->willReturn('subadmin');
162
+        $this->userSession
163
+            ->expects($this->once())
164
+            ->method('getUser')
165
+            ->willReturn($loggedInUser);
166
+        $this->groupManager
167
+            ->expects($this->once())
168
+            ->method('isAdmin')
169
+            ->willReturn(false);
170
+        $firstGroup = $this->getMockBuilder('OCP\IGroup')
171
+            ->disableOriginalConstructor()
172
+            ->getMock();
173
+        $firstGroup
174
+            ->expects($this->once())
175
+            ->method('getGID')
176
+            ->willReturn('FirstGroup');
177
+        $secondGroup = $this->getMockBuilder('OCP\IGroup')
178
+            ->disableOriginalConstructor()
179
+            ->getMock();
180
+        $secondGroup
181
+            ->expects($this->once())
182
+            ->method('getGID')
183
+            ->willReturn('SecondGroup');
184
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
185
+            ->disableOriginalConstructor()->getMock();
186
+        $subAdminManager
187
+            ->expects($this->once())
188
+            ->method('isSubAdmin')
189
+            ->with($loggedInUser)
190
+            ->willReturn(true);
191
+        $subAdminManager
192
+            ->expects($this->once())
193
+            ->method('getSubAdminsGroups')
194
+            ->with($loggedInUser)
195
+            ->willReturn([$firstGroup, $secondGroup]);
196
+        $this->groupManager
197
+            ->expects($this->once())
198
+            ->method('getSubAdmin')
199
+            ->willReturn($subAdminManager);
200
+        $this->groupManager
201
+            ->expects($this->any())
202
+            ->method('displayNamesInGroup')
203
+            ->will($this->onConsecutiveCalls(['AnotherUserInTheFirstGroup' => []], ['UserInTheSecondGroup' => []]));
204
+
205
+        $expected = [
206
+            'users' => [
207
+                'AnotherUserInTheFirstGroup',
208
+                'UserInTheSecondGroup',
209
+            ],
210
+        ];
211
+        $this->assertEquals($expected, $this->api->getUsers('MyCustomSearch')->getData());
212
+    }
213
+
214
+    private function createUserMock(string $uid, bool $enabled): MockObject&IUser {
215
+        $mockUser = $this->getMockBuilder(IUser::class)
216
+            ->disableOriginalConstructor()
217
+            ->getMock();
218
+        $mockUser
219
+            ->method('getUID')
220
+            ->willReturn($uid);
221
+        $mockUser
222
+            ->method('isEnabled')
223
+            ->willReturn($enabled);
224
+        return $mockUser;
225
+    }
226
+
227
+    public function testGetDisabledUsersAsAdmin(): void {
228
+        $loggedInUser = $this->getMockBuilder(IUser::class)
229
+            ->disableOriginalConstructor()
230
+            ->getMock();
231
+        $loggedInUser
232
+            ->expects($this->once())
233
+            ->method('getUID')
234
+            ->willReturn('admin');
235
+        $this->userSession
236
+            ->expects($this->atLeastOnce())
237
+            ->method('getUser')
238
+            ->willReturn($loggedInUser);
239
+        $this->groupManager
240
+            ->expects($this->once())
241
+            ->method('isAdmin')
242
+            ->willReturn(true);
243
+        $this->userManager
244
+            ->expects($this->once())
245
+            ->method('getDisabledUsers')
246
+            ->with(3, 0, 'MyCustomSearch')
247
+            ->willReturn([
248
+                $this->createUserMock('admin', false),
249
+                $this->createUserMock('foo', false),
250
+                $this->createUserMock('bar', false),
251
+            ]);
252
+
253
+        $expected = [
254
+            'users' => [
255
+                'admin' => ['id' => 'admin'],
256
+                'foo' => ['id' => 'foo'],
257
+                'bar' => ['id' => 'bar'],
258
+            ],
259
+        ];
260
+        $this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
261
+    }
262
+
263
+    public function testGetDisabledUsersAsSubAdmin(): void {
264
+        $loggedInUser = $this->getMockBuilder(IUser::class)
265
+            ->disableOriginalConstructor()
266
+            ->getMock();
267
+        $loggedInUser
268
+            ->expects($this->once())
269
+            ->method('getUID')
270
+            ->willReturn('subadmin');
271
+        $this->userSession
272
+            ->expects($this->atLeastOnce())
273
+            ->method('getUser')
274
+            ->willReturn($loggedInUser);
275
+        $this->groupManager
276
+            ->expects($this->once())
277
+            ->method('isAdmin')
278
+            ->willReturn(false);
279
+        $firstGroup = $this->getMockBuilder('OCP\IGroup')
280
+            ->disableOriginalConstructor()
281
+            ->getMock();
282
+        $secondGroup = $this->getMockBuilder('OCP\IGroup')
283
+            ->disableOriginalConstructor()
284
+            ->getMock();
285
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
286
+            ->disableOriginalConstructor()->getMock();
287
+        $subAdminManager
288
+            ->expects($this->once())
289
+            ->method('isSubAdmin')
290
+            ->with($loggedInUser)
291
+            ->willReturn(true);
292
+        $subAdminManager
293
+            ->expects($this->once())
294
+            ->method('getSubAdminsGroups')
295
+            ->with($loggedInUser)
296
+            ->willReturn([$firstGroup, $secondGroup]);
297
+        $this->groupManager
298
+            ->expects($this->once())
299
+            ->method('getSubAdmin')
300
+            ->willReturn($subAdminManager);
301
+        $this->groupManager
302
+            ->expects($this->never())
303
+            ->method('displayNamesInGroup');
304
+
305
+        $firstGroup
306
+            ->expects($this->once())
307
+            ->method('searchUsers')
308
+            ->with('MyCustomSearch')
309
+            ->willReturn([
310
+                $this->createUserMock('user1', false),
311
+                $this->createUserMock('bob', true),
312
+                $this->createUserMock('user2', false),
313
+                $this->createUserMock('alice', true),
314
+            ]);
315
+
316
+        $secondGroup
317
+            ->expects($this->once())
318
+            ->method('searchUsers')
319
+            ->with('MyCustomSearch')
320
+            ->willReturn([
321
+                $this->createUserMock('user2', false),
322
+                $this->createUserMock('joe', true),
323
+                $this->createUserMock('user3', false),
324
+                $this->createUserMock('jim', true),
325
+                $this->createUserMock('john', true),
326
+            ]);
327
+
328
+
329
+        $expected = [
330
+            'users' => [
331
+                'user1' => ['id' => 'user1'],
332
+                'user2' => ['id' => 'user2'],
333
+                'user3' => ['id' => 'user3'],
334
+            ],
335
+        ];
336
+        $this->assertEquals($expected, $this->api->getDisabledUsersDetails('MyCustomSearch', 3)->getData());
337
+    }
338
+
339
+
340
+    public function testAddUserAlreadyExisting(): void {
341
+        $this->expectException(OCSException::class);
342
+        $this->expectExceptionCode(102);
343
+
344
+        $this->userManager
345
+            ->expects($this->once())
346
+            ->method('userExists')
347
+            ->with('AlreadyExistingUser')
348
+            ->willReturn(true);
349
+        $this->logger
350
+            ->expects($this->once())
351
+            ->method('error')
352
+            ->with('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
353
+        $loggedInUser = $this->getMockBuilder(IUser::class)
354
+            ->disableOriginalConstructor()
355
+            ->getMock();
356
+        $loggedInUser
357
+            ->expects($this->exactly(2))
358
+            ->method('getUID')
359
+            ->willReturn('adminUser');
360
+        $this->userSession
361
+            ->expects($this->once())
362
+            ->method('getUser')
363
+            ->willReturn($loggedInUser);
364
+        $this->groupManager
365
+            ->expects($this->once())
366
+            ->method('isAdmin')
367
+            ->with('adminUser')
368
+            ->willReturn(true);
369
+
370
+        $this->api->addUser('AlreadyExistingUser', 'password', '', '', []);
371
+    }
372
+
373
+
374
+    public function testAddUserNonExistingGroup(): void {
375
+        $this->expectException(OCSException::class);
376
+        $this->expectExceptionMessage('Group NonExistingGroup does not exist');
377
+        $this->expectExceptionCode(104);
378
+
379
+        $this->userManager
380
+            ->expects($this->once())
381
+            ->method('userExists')
382
+            ->with('NewUser')
383
+            ->willReturn(false);
384
+        $loggedInUser = $this->getMockBuilder(IUser::class)
385
+            ->disableOriginalConstructor()
386
+            ->getMock();
387
+        $loggedInUser
388
+            ->expects($this->exactly(2))
389
+            ->method('getUID')
390
+            ->willReturn('adminUser');
391
+        $this->userSession
392
+            ->expects($this->once())
393
+            ->method('getUser')
394
+            ->willReturn($loggedInUser);
395
+        $this->groupManager
396
+            ->expects($this->once())
397
+            ->method('isAdmin')
398
+            ->with('adminUser')
399
+            ->willReturn(true);
400
+        $this->groupManager
401
+            ->expects($this->once())
402
+            ->method('groupExists')
403
+            ->with('NonExistingGroup')
404
+            ->willReturn(false);
405
+
406
+        $this->api->addUser('NewUser', 'pass', '', '', ['NonExistingGroup']);
407
+    }
408
+
409
+
410
+    public function testAddUserExistingGroupNonExistingGroup(): void {
411
+        $this->expectException(OCSException::class);
412
+        $this->expectExceptionMessage('Group NonExistingGroup does not exist');
413
+        $this->expectExceptionCode(104);
414
+
415
+        $this->userManager
416
+            ->expects($this->once())
417
+            ->method('userExists')
418
+            ->with('NewUser')
419
+            ->willReturn(false);
420
+        $loggedInUser = $this->getMockBuilder(IUser::class)
421
+            ->disableOriginalConstructor()
422
+            ->getMock();
423
+        $loggedInUser
424
+            ->expects($this->exactly(2))
425
+            ->method('getUID')
426
+            ->willReturn('adminUser');
427
+        $this->userSession
428
+            ->expects($this->once())
429
+            ->method('getUser')
430
+            ->willReturn($loggedInUser);
431
+        $this->groupManager
432
+            ->expects($this->once())
433
+            ->method('isAdmin')
434
+            ->with('adminUser')
435
+            ->willReturn(true);
436
+        $this->groupManager
437
+            ->expects($this->exactly(2))
438
+            ->method('groupExists')
439
+            ->willReturnMap([
440
+                ['ExistingGroup', true],
441
+                ['NonExistingGroup', false]
442
+            ]);
443
+
444
+        $this->api->addUser('NewUser', 'pass', '', '', ['ExistingGroup', 'NonExistingGroup']);
445
+    }
446
+
447
+    public function testAddUserSuccessful(): void {
448
+        $this->userManager
449
+            ->expects($this->once())
450
+            ->method('userExists')
451
+            ->with('NewUser')
452
+            ->willReturn(false);
453
+        $this->userManager
454
+            ->expects($this->once())
455
+            ->method('createUser')
456
+            ->with('NewUser', 'PasswordOfTheNewUser');
457
+        $this->logger
458
+            ->expects($this->once())
459
+            ->method('info')
460
+            ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
461
+        $loggedInUser = $this->getMockBuilder(IUser::class)
462
+            ->disableOriginalConstructor()
463
+            ->getMock();
464
+        $loggedInUser
465
+            ->expects($this->exactly(2))
466
+            ->method('getUID')
467
+            ->willReturn('adminUser');
468
+        $this->userSession
469
+            ->expects($this->once())
470
+            ->method('getUser')
471
+            ->willReturn($loggedInUser);
472
+        $this->groupManager
473
+            ->expects($this->once())
474
+            ->method('isAdmin')
475
+            ->with('adminUser')
476
+            ->willReturn(true);
477
+
478
+        $this->assertTrue(key_exists(
479
+            'id',
480
+            $this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
481
+        ));
482
+    }
483
+
484
+    public function testAddUserSuccessfulWithDisplayName(): void {
485
+        /**
486
+         * @var UserController
487
+         */
488
+        $api = $this->getMockBuilder(UsersController::class)
489
+            ->setConstructorArgs([
490
+                'provisioning_api',
491
+                $this->request,
492
+                $this->userManager,
493
+                $this->config,
494
+                $this->groupManager,
495
+                $this->userSession,
496
+                $this->accountManager,
497
+                $this->subAdminManager,
498
+                $this->l10nFactory,
499
+                $this->rootFolder,
500
+                $this->urlGenerator,
501
+                $this->logger,
502
+                $this->newUserMailHelper,
503
+                $this->secureRandom,
504
+                $this->remoteWipe,
505
+                $this->knownUserService,
506
+                $this->eventDispatcher,
507
+                $this->phoneNumberUtil,
508
+                $this->appManager,
509
+            ])
510
+            ->onlyMethods(['editUser'])
511
+            ->getMock();
512
+
513
+        $this->userManager
514
+            ->expects($this->once())
515
+            ->method('userExists')
516
+            ->with('NewUser')
517
+            ->willReturn(false);
518
+        $this->userManager
519
+            ->expects($this->once())
520
+            ->method('createUser')
521
+            ->with('NewUser', 'PasswordOfTheNewUser');
522
+        $this->logger
523
+            ->expects($this->once())
524
+            ->method('info')
525
+            ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
526
+        $loggedInUser = $this->getMockBuilder(IUser::class)
527
+            ->disableOriginalConstructor()
528
+            ->getMock();
529
+        $loggedInUser
530
+            ->expects($this->any())
531
+            ->method('getUID')
532
+            ->willReturn('adminUser');
533
+        $this->userSession
534
+            ->expects($this->any())
535
+            ->method('getUser')
536
+            ->willReturn($loggedInUser);
537
+        $this->groupManager
538
+            ->expects($this->once())
539
+            ->method('isAdmin')
540
+            ->with('adminUser')
541
+            ->willReturn(true);
542
+        $api
543
+            ->expects($this->once())
544
+            ->method('editUser')
545
+            ->with('NewUser', 'display', 'DisplayNameOfTheNewUser');
546
+
547
+        $this->assertTrue(key_exists(
548
+            'id',
549
+            $api->addUser('NewUser', 'PasswordOfTheNewUser', 'DisplayNameOfTheNewUser')->getData()
550
+        ));
551
+    }
552
+
553
+    public function testAddUserSuccessfulGenerateUserID(): void {
554
+        $this->config
555
+            ->expects($this->any())
556
+            ->method('getAppValue')
557
+            ->willReturnCallback(function ($appid, $key, $default) {
558
+                if ($key === 'newUser.generateUserID') {
559
+                    return 'yes';
560
+                }
561
+                return null;
562
+            });
563
+        $this->userManager
564
+            ->expects($this->any())
565
+            ->method('userExists')
566
+            ->with($this->anything())
567
+            ->willReturn(false);
568
+        $this->userManager
569
+            ->expects($this->once())
570
+            ->method('createUser')
571
+            ->with($this->anything(), 'PasswordOfTheNewUser');
572
+        $this->logger
573
+            ->expects($this->once())
574
+            ->method('info')
575
+            ->with($this->stringStartsWith('Successful addUser call with userid: '), ['app' => 'ocs_api']);
576
+        $loggedInUser = $this->getMockBuilder(IUser::class)
577
+            ->disableOriginalConstructor()
578
+            ->getMock();
579
+        $loggedInUser
580
+            ->expects($this->exactly(2))
581
+            ->method('getUID')
582
+            ->willReturn('adminUser');
583
+        $this->userSession
584
+            ->expects($this->once())
585
+            ->method('getUser')
586
+            ->willReturn($loggedInUser);
587
+        $this->groupManager
588
+            ->expects($this->once())
589
+            ->method('isAdmin')
590
+            ->with('adminUser')
591
+            ->willReturn(true);
592
+        $this->secureRandom->expects($this->any())
593
+            ->method('generate')
594
+            ->with(10)
595
+            ->willReturnCallback(function () {
596
+                return (string)rand(100000000, 999999999);
597
+            });
598
+
599
+        $this->assertTrue(key_exists(
600
+            'id',
601
+            $this->api->addUser('', 'PasswordOfTheNewUser')->getData()
602
+        ));
603
+    }
604
+
605
+    public function testAddUserSuccessfulGeneratePassword(): void {
606
+        $this->userManager
607
+            ->expects($this->once())
608
+            ->method('userExists')
609
+            ->with('NewUser')
610
+            ->willReturn(false);
611
+        $newUser = $this->createMock(IUser::class);
612
+        $newUser->expects($this->once())
613
+            ->method('setEMailAddress');
614
+        $this->userManager
615
+            ->expects($this->once())
616
+            ->method('createUser')
617
+            ->willReturn($newUser);
618
+        $this->logger
619
+            ->expects($this->once())
620
+            ->method('info')
621
+            ->with('Successful addUser call with userid: NewUser', ['app' => 'ocs_api']);
622
+        $loggedInUser = $this->getMockBuilder(IUser::class)
623
+            ->disableOriginalConstructor()
624
+            ->getMock();
625
+        $loggedInUser
626
+            ->expects($this->exactly(2))
627
+            ->method('getUID')
628
+            ->willReturn('adminUser');
629
+        $this->userSession
630
+            ->expects($this->once())
631
+            ->method('getUser')
632
+            ->willReturn($loggedInUser);
633
+        $this->groupManager
634
+            ->expects($this->once())
635
+            ->method('isAdmin')
636
+            ->with('adminUser')
637
+            ->willReturn(true);
638
+        $this->eventDispatcher
639
+            ->expects($this->once())
640
+            ->method('dispatchTyped')
641
+            ->with(new GenerateSecurePasswordEvent());
642
+
643
+        $this->assertTrue(key_exists(
644
+            'id',
645
+            $this->api->addUser('NewUser', '', '', 'foo@bar')->getData()
646
+        ));
647
+    }
648
+
649
+
650
+    public function testAddUserFailedToGenerateUserID(): void {
651
+        $this->expectException(OCSException::class);
652
+        $this->expectExceptionMessage('Could not create non-existing user ID');
653
+        $this->expectExceptionCode(111);
654
+
655
+        $this->config
656
+            ->expects($this->any())
657
+            ->method('getAppValue')
658
+            ->willReturnCallback(function ($appid, $key, $default) {
659
+                if ($key === 'newUser.generateUserID') {
660
+                    return 'yes';
661
+                }
662
+                return null;
663
+            });
664
+        $this->userManager
665
+            ->expects($this->any())
666
+            ->method('userExists')
667
+            ->with($this->anything())
668
+            ->willReturn(true);
669
+        $this->userManager
670
+            ->expects($this->never())
671
+            ->method('createUser');
672
+        $loggedInUser = $this->getMockBuilder(IUser::class)
673
+            ->disableOriginalConstructor()
674
+            ->getMock();
675
+        $loggedInUser
676
+            ->expects($this->exactly(2))
677
+            ->method('getUID')
678
+            ->willReturn('adminUser');
679
+        $this->userSession
680
+            ->expects($this->once())
681
+            ->method('getUser')
682
+            ->willReturn($loggedInUser);
683
+        $this->groupManager
684
+            ->expects($this->once())
685
+            ->method('isAdmin')
686
+            ->with('adminUser')
687
+            ->willReturn(true);
688
+
689
+        $this->api->addUser('', 'PasswordOfTheNewUser')->getData();
690
+    }
691
+
692
+
693
+    public function testAddUserEmailRequired(): void {
694
+        $this->expectException(OCSException::class);
695
+        $this->expectExceptionMessage('Required email address was not provided');
696
+        $this->expectExceptionCode(110);
697
+
698
+        $this->config
699
+            ->expects($this->any())
700
+            ->method('getAppValue')
701
+            ->willReturnCallback(function ($appid, $key, $default) {
702
+                if ($key === 'newUser.requireEmail') {
703
+                    return 'yes';
704
+                }
705
+                return null;
706
+            });
707
+        $this->userManager
708
+            ->expects($this->once())
709
+            ->method('userExists')
710
+            ->with('NewUser')
711
+            ->willReturn(false);
712
+        $this->userManager
713
+            ->expects($this->never())
714
+            ->method('createUser');
715
+        $loggedInUser = $this->getMockBuilder(IUser::class)
716
+            ->disableOriginalConstructor()
717
+            ->getMock();
718
+        $loggedInUser
719
+            ->expects($this->exactly(2))
720
+            ->method('getUID')
721
+            ->willReturn('adminUser');
722
+        $this->userSession
723
+            ->expects($this->once())
724
+            ->method('getUser')
725
+            ->willReturn($loggedInUser);
726
+        $this->groupManager
727
+            ->expects($this->once())
728
+            ->method('isAdmin')
729
+            ->with('adminUser')
730
+            ->willReturn(true);
731
+
732
+        $this->assertTrue(key_exists(
733
+            'id',
734
+            $this->api->addUser('NewUser', 'PasswordOfTheNewUser')->getData()
735
+        ));
736
+    }
737
+
738
+    public function testAddUserExistingGroup(): void {
739
+        $this->userManager
740
+            ->expects($this->once())
741
+            ->method('userExists')
742
+            ->with('NewUser')
743
+            ->willReturn(false);
744
+        $loggedInUser = $this->getMockBuilder(IUser::class)
745
+            ->disableOriginalConstructor()
746
+            ->getMock();
747
+        $loggedInUser
748
+            ->expects($this->exactly(2))
749
+            ->method('getUID')
750
+            ->willReturn('adminUser');
751
+        $this->userSession
752
+            ->expects($this->once())
753
+            ->method('getUser')
754
+            ->willReturn($loggedInUser);
755
+        $this->groupManager
756
+            ->expects($this->once())
757
+            ->method('isAdmin')
758
+            ->with('adminUser')
759
+            ->willReturn(true);
760
+        $this->groupManager
761
+            ->expects($this->once())
762
+            ->method('groupExists')
763
+            ->with('ExistingGroup')
764
+            ->willReturn(true);
765
+        $user = $this->getMockBuilder(IUser::class)
766
+            ->disableOriginalConstructor()
767
+            ->getMock();
768
+        $this->userManager
769
+            ->expects($this->once())
770
+            ->method('createUser')
771
+            ->with('NewUser', 'PasswordOfTheNewUser')
772
+            ->willReturn($user);
773
+        $group = $this->getMockBuilder('OCP\IGroup')
774
+            ->disableOriginalConstructor()
775
+            ->getMock();
776
+        $group
777
+            ->expects($this->once())
778
+            ->method('addUser')
779
+            ->with($user);
780
+        $this->groupManager
781
+            ->expects($this->once())
782
+            ->method('get')
783
+            ->with('ExistingGroup')
784
+            ->willReturn($group);
785
+
786
+        $calls = [
787
+            ['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
788
+            ['Added userid NewUser to group ExistingGroup', ['app' => 'ocs_api']],
789
+        ];
790
+        $this->logger
791
+            ->expects($this->exactly(2))
792
+            ->method('info')
793
+            ->willReturnCallback(function () use (&$calls) {
794
+                $expected = array_shift($calls);
795
+                $this->assertEquals($expected, func_get_args());
796
+            });
797
+
798
+        $this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData());
799
+    }
800
+
801
+
802
+    public function testAddUserUnsuccessful(): void {
803
+        $this->expectException(OCSException::class);
804
+        $this->expectExceptionMessage('Bad request');
805
+        $this->expectExceptionCode(101);
806
+
807
+        $exception = new Exception('User backend not found.');
808
+        $this->userManager
809
+            ->expects($this->once())
810
+            ->method('userExists')
811
+            ->with('NewUser')
812
+            ->willReturn(false);
813
+        $this->userManager
814
+            ->expects($this->once())
815
+            ->method('createUser')
816
+            ->with('NewUser', 'PasswordOfTheNewUser')
817
+            ->will($this->throwException($exception));
818
+        $this->logger
819
+            ->expects($this->once())
820
+            ->method('error')
821
+            ->with(
822
+                'Failed addUser attempt with exception.',
823
+                [
824
+                    'app' => 'ocs_api',
825
+                    'exception' => $exception
826
+                ]
827
+            );
828
+        $loggedInUser = $this->getMockBuilder(IUser::class)
829
+            ->disableOriginalConstructor()
830
+            ->getMock();
831
+        $loggedInUser
832
+            ->expects($this->exactly(2))
833
+            ->method('getUID')
834
+            ->willReturn('adminUser');
835
+        $this->userSession
836
+            ->expects($this->once())
837
+            ->method('getUser')
838
+            ->willReturn($loggedInUser);
839
+        $this->groupManager
840
+            ->expects($this->once())
841
+            ->method('isAdmin')
842
+            ->with('adminUser')
843
+            ->willReturn(true);
844
+
845
+        $this->api->addUser('NewUser', 'PasswordOfTheNewUser');
846
+    }
847
+
848
+
849
+    public function testAddUserAsSubAdminNoGroup(): void {
850
+        $this->expectException(OCSException::class);
851
+        $this->expectExceptionMessage('No group specified (required for sub-admins)');
852
+        $this->expectExceptionCode(106);
853
+
854
+        $loggedInUser = $this->getMockBuilder(IUser::class)
855
+            ->disableOriginalConstructor()
856
+            ->getMock();
857
+        $loggedInUser
858
+            ->expects($this->exactly(2))
859
+            ->method('getUID')
860
+            ->willReturn('regularUser');
861
+        $this->userSession
862
+            ->expects($this->once())
863
+            ->method('getUser')
864
+            ->willReturn($loggedInUser);
865
+        $this->groupManager
866
+            ->expects($this->once())
867
+            ->method('isAdmin')
868
+            ->with('regularUser')
869
+            ->willReturn(false);
870
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
871
+            ->disableOriginalConstructor()->getMock();
872
+        $this->groupManager
873
+            ->expects($this->once())
874
+            ->method('getSubAdmin')
875
+            ->with()
876
+            ->willReturn($subAdminManager);
877
+
878
+        $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', []);
879
+    }
880
+
881
+
882
+    public function testAddUserAsSubAdminValidGroupNotSubAdmin(): void {
883
+        $this->expectException(OCSException::class);
884
+        $this->expectExceptionMessage('Insufficient privileges for group ExistingGroup');
885
+        $this->expectExceptionCode(105);
886
+
887
+        $loggedInUser = $this->getMockBuilder(IUser::class)
888
+            ->disableOriginalConstructor()
889
+            ->getMock();
890
+        $loggedInUser
891
+            ->expects($this->exactly(2))
892
+            ->method('getUID')
893
+            ->willReturn('regularUser');
894
+        $this->userSession
895
+            ->expects($this->once())
896
+            ->method('getUser')
897
+            ->willReturn($loggedInUser);
898
+        $this->groupManager
899
+            ->expects($this->once())
900
+            ->method('isAdmin')
901
+            ->with('regularUser')
902
+            ->willReturn(false);
903
+        $existingGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
904
+        $this->groupManager
905
+            ->expects($this->once())
906
+            ->method('get')
907
+            ->with('ExistingGroup')
908
+            ->willReturn($existingGroup);
909
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
910
+            ->disableOriginalConstructor()->getMock();
911
+        $subAdminManager
912
+            ->expects($this->once())
913
+            ->method('isSubAdminOfGroup')
914
+            ->with($loggedInUser, $existingGroup)
915
+            ->willReturn(false);
916
+        $this->groupManager
917
+            ->expects($this->once())
918
+            ->method('getSubAdmin')
919
+            ->with()
920
+            ->willReturn($subAdminManager);
921
+        $this->groupManager
922
+            ->expects($this->once())
923
+            ->method('groupExists')
924
+            ->with('ExistingGroup')
925
+            ->willReturn(true);
926
+
927
+        $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup'])->getData();
928
+    }
929
+
930
+    public function testAddUserAsSubAdminExistingGroups(): void {
931
+        $this->userManager
932
+            ->expects($this->once())
933
+            ->method('userExists')
934
+            ->with('NewUser')
935
+            ->willReturn(false);
936
+        $loggedInUser = $this->getMockBuilder(IUser::class)
937
+            ->disableOriginalConstructor()
938
+            ->getMock();
939
+        $loggedInUser
940
+            ->expects($this->exactly(2))
941
+            ->method('getUID')
942
+            ->willReturn('subAdminUser');
943
+        $this->userSession
944
+            ->expects($this->once())
945
+            ->method('getUser')
946
+            ->willReturn($loggedInUser);
947
+        $this->groupManager
948
+            ->expects($this->once())
949
+            ->method('isAdmin')
950
+            ->with('subAdminUser')
951
+            ->willReturn(false);
952
+        $this->groupManager
953
+            ->expects($this->exactly(2))
954
+            ->method('groupExists')
955
+            ->willReturnMap([
956
+                ['ExistingGroup1', true],
957
+                ['ExistingGroup2', true]
958
+            ]);
959
+        $user = $this->getMockBuilder(IUser::class)
960
+            ->disableOriginalConstructor()
961
+            ->getMock();
962
+        $this->userManager
963
+            ->expects($this->once())
964
+            ->method('createUser')
965
+            ->with('NewUser', 'PasswordOfTheNewUser')
966
+            ->willReturn($user);
967
+        $existingGroup1 = $this->getMockBuilder('OCP\IGroup')
968
+            ->disableOriginalConstructor()
969
+            ->getMock();
970
+        $existingGroup2 = $this->getMockBuilder('OCP\IGroup')
971
+            ->disableOriginalConstructor()
972
+            ->getMock();
973
+        $existingGroup1
974
+            ->expects($this->once())
975
+            ->method('addUser')
976
+            ->with($user);
977
+        $existingGroup2
978
+            ->expects($this->once())
979
+            ->method('addUser')
980
+            ->with($user);
981
+        $this->groupManager
982
+            ->expects($this->exactly(4))
983
+            ->method('get')
984
+            ->willReturnMap([
985
+                ['ExistingGroup1', $existingGroup1],
986
+                ['ExistingGroup2', $existingGroup2]
987
+            ]);
988
+
989
+        $calls = [
990
+            ['Successful addUser call with userid: NewUser', ['app' => 'ocs_api']],
991
+            ['Added userid NewUser to group ExistingGroup1', ['app' => 'ocs_api']],
992
+            ['Added userid NewUser to group ExistingGroup2', ['app' => 'ocs_api']],
993
+        ];
994
+        $this->logger
995
+            ->expects($this->exactly(3))
996
+            ->method('info')
997
+            ->willReturnCallback(function () use (&$calls) {
998
+                $expected = array_shift($calls);
999
+                $this->assertEquals($expected, func_get_args());
1000
+            });
1001
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1002
+            ->disableOriginalConstructor()->getMock();
1003
+        $this->groupManager
1004
+            ->expects($this->once())
1005
+            ->method('getSubAdmin')
1006
+            ->willReturn($subAdminManager);
1007
+        $subAdminManager
1008
+            ->expects($this->exactly(2))
1009
+            ->method('isSubAdminOfGroup')
1010
+            ->willReturnMap([
1011
+                [$loggedInUser, $existingGroup1, true],
1012
+                [$loggedInUser, $existingGroup2, true],
1013
+            ]);
1014
+
1015
+        $this->assertArrayHasKey('id', $this->api->addUser('NewUser', 'PasswordOfTheNewUser', '', '', ['ExistingGroup1', 'ExistingGroup2'])->getData());
1016
+    }
1017
+
1018
+
1019
+    public function testGetUserTargetDoesNotExist(): void {
1020
+        $this->expectException(OCSException::class);
1021
+        $this->expectExceptionMessage('User does not exist');
1022
+        $this->expectExceptionCode(404);
1023
+
1024
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1025
+            ->disableOriginalConstructor()
1026
+            ->getMock();
1027
+        $this->userSession
1028
+            ->method('getUser')
1029
+            ->willReturn($loggedInUser);
1030
+        $this->userManager
1031
+            ->expects($this->once())
1032
+            ->method('get')
1033
+            ->with('UserToGet')
1034
+            ->willReturn(null);
1035
+
1036
+        $this->api->getUser('UserToGet');
1037
+    }
1038
+
1039
+    public function testGetUserDataAsAdmin(): void {
1040
+        $group0 = $this->createMock(IGroup::class);
1041
+        $group1 = $this->createMock(IGroup::class);
1042
+        $group2 = $this->createMock(IGroup::class);
1043
+        $group3 = $this->createMock(IGroup::class);
1044
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1045
+            ->disableOriginalConstructor()
1046
+            ->getMock();
1047
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1048
+            ->disableOriginalConstructor()
1049
+            ->getMock();
1050
+        $loggedInUser
1051
+            ->method('getUID')
1052
+            ->willReturn('admin');
1053
+        $targetUser = $this->getMockBuilder(IUser::class)
1054
+            ->disableOriginalConstructor()
1055
+            ->getMock();
1056
+        $targetUser->expects($this->once())
1057
+            ->method('getSystemEMailAddress')
1058
+            ->willReturn('[email protected]');
1059
+        $this->userSession
1060
+            ->method('getUser')
1061
+            ->willReturn($loggedInUser);
1062
+        $this->userManager
1063
+            ->method('get')
1064
+            ->with('UID')
1065
+            ->willReturn($targetUser);
1066
+        $this->groupManager
1067
+            ->method('isAdmin')
1068
+            ->with('admin')
1069
+            ->willReturn(true);
1070
+        $this->groupManager
1071
+            ->expects($this->any())
1072
+            ->method('getUserGroups')
1073
+            ->willReturn([$group0, $group1, $group2]);
1074
+        $this->groupManager
1075
+            ->expects($this->once())
1076
+            ->method('getSubAdmin')
1077
+            ->willReturn($subAdminManager);
1078
+        $subAdminManager
1079
+            ->expects($this->once())
1080
+            ->method('getSubAdminsGroups')
1081
+            ->willReturn([$group3]);
1082
+        $group0->expects($this->once())
1083
+            ->method('getGID')
1084
+            ->willReturn('group0');
1085
+        $group1->expects($this->once())
1086
+            ->method('getGID')
1087
+            ->willReturn('group1');
1088
+        $group2->expects($this->once())
1089
+            ->method('getGID')
1090
+            ->willReturn('group2');
1091
+        $group3->expects($this->once())
1092
+            ->method('getGID')
1093
+            ->willReturn('group3');
1094
+
1095
+        $this->mockAccount($targetUser, [
1096
+            IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1097
+            IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1098
+            IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1099
+            IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1100
+            IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1101
+            IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1102
+            IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1103
+            IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1104
+            IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1105
+            IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1106
+            IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1107
+        ]);
1108
+        $this->config
1109
+            ->method('getUserValue')
1110
+            ->willReturnMap([
1111
+                ['UID', 'core', 'enabled', 'true', 'true'],
1112
+            ]);
1113
+        $this->api
1114
+            ->expects($this->once())
1115
+            ->method('fillStorageInfo')
1116
+            ->with($targetUser)
1117
+            ->willReturn(['DummyValue']);
1118
+
1119
+        $backend = $this->createMock(UserInterface::class);
1120
+        $backend->expects($this->any())
1121
+            ->method('implementsActions')
1122
+            ->willReturn(true);
1123
+
1124
+        $targetUser
1125
+            ->expects($this->once())
1126
+            ->method('getDisplayName')
1127
+            ->willReturn('Demo User');
1128
+        $targetUser
1129
+            ->expects($this->once())
1130
+            ->method('getHome')
1131
+            ->willReturn('/var/www/newtcloud/data/UID');
1132
+        $targetUser
1133
+            ->expects($this->exactly(2))
1134
+            ->method('getLastLogin')
1135
+            ->willReturn(1521191471);
1136
+        $targetUser
1137
+            ->expects($this->once())
1138
+            ->method('getFirstLogin')
1139
+            ->willReturn(1511191471);
1140
+        $targetUser
1141
+            ->expects($this->once())
1142
+            ->method('getBackendClassName')
1143
+            ->willReturn('Database');
1144
+        $targetUser
1145
+            ->expects($this->once())
1146
+            ->method('getBackend')
1147
+            ->willReturn($backend);
1148
+        $targetUser
1149
+            ->method('getUID')
1150
+            ->willReturn('UID');
1151
+
1152
+        $this->l10nFactory
1153
+            ->expects($this->once())
1154
+            ->method('getUserLanguage')
1155
+            ->with($targetUser)
1156
+            ->willReturn('de');
1157
+
1158
+        $expected = [
1159
+            'id' => 'UID',
1160
+            'enabled' => true,
1161
+            'storageLocation' => '/var/www/newtcloud/data/UID',
1162
+            'firstLoginTimestamp' => 1511191471,
1163
+            'lastLoginTimestamp' => 1521191471,
1164
+            'lastLogin' => 1521191471000,
1165
+            'backend' => 'Database',
1166
+            'subadmin' => ['group3'],
1167
+            'quota' => ['DummyValue'],
1168
+            'email' => '[email protected]',
1169
+            'displayname' => 'Demo User',
1170
+            'display-name' => 'Demo User',
1171
+            'phone' => 'phone',
1172
+            'address' => 'address',
1173
+            'website' => 'website',
1174
+            'twitter' => 'twitter',
1175
+            'fediverse' => 'fediverse',
1176
+            'groups' => ['group0', 'group1', 'group2'],
1177
+            'language' => 'de',
1178
+            'locale' => null,
1179
+            'backendCapabilities' => [
1180
+                'setDisplayName' => true,
1181
+                'setPassword' => true,
1182
+            ],
1183
+            'additional_mail' => [],
1184
+            'organisation' => 'organisation',
1185
+            'role' => 'role',
1186
+            'headline' => 'headline',
1187
+            'biography' => 'biography',
1188
+            'profile_enabled' => '1',
1189
+            'notify_email' => null,
1190
+            'manager' => '',
1191
+            'pronouns' => 'they/them',
1192
+        ];
1193
+        $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1194
+    }
1195
+
1196
+    public function testGetUserDataAsSubAdminAndUserIsAccessible(): void {
1197
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1198
+            ->disableOriginalConstructor()
1199
+            ->getMock();
1200
+        $loggedInUser
1201
+            ->method('getUID')
1202
+            ->willReturn('subadmin');
1203
+        $targetUser = $this->getMockBuilder(IUser::class)
1204
+            ->disableOriginalConstructor()
1205
+            ->getMock();
1206
+        $targetUser
1207
+            ->expects($this->once())
1208
+            ->method('getSystemEMailAddress')
1209
+            ->willReturn('[email protected]');
1210
+        $this->userSession
1211
+            ->method('getUser')
1212
+            ->willReturn($loggedInUser);
1213
+        $this->userManager
1214
+            ->method('get')
1215
+            ->with('UID')
1216
+            ->willReturn($targetUser);
1217
+        $this->groupManager
1218
+            ->method('isAdmin')
1219
+            ->with('subadmin')
1220
+            ->willReturn(false);
1221
+        $this->groupManager
1222
+            ->expects($this->any())
1223
+            ->method('getUserGroups')
1224
+            ->willReturn([]);
1225
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1226
+            ->disableOriginalConstructor()
1227
+            ->getMock();
1228
+        $subAdminManager
1229
+            ->expects($this->once())
1230
+            ->method('isUserAccessible')
1231
+            ->with($loggedInUser, $targetUser)
1232
+            ->willReturn(true);
1233
+        $subAdminManager
1234
+            ->expects($this->once())
1235
+            ->method('getSubAdminsGroups')
1236
+            ->willReturn([]);
1237
+        $this->groupManager
1238
+            ->expects($this->exactly(2))
1239
+            ->method('getSubAdmin')
1240
+            ->willReturn($subAdminManager);
1241
+        $this->config
1242
+            ->method('getUserValue')
1243
+            ->willReturnMap([
1244
+                ['UID', 'core', 'enabled', 'true', 'true'],
1245
+            ]);
1246
+        $this->api
1247
+            ->expects($this->once())
1248
+            ->method('fillStorageInfo')
1249
+            ->with($targetUser)
1250
+            ->willReturn(['DummyValue']);
1251
+
1252
+        $backend = $this->createMock(UserInterface::class);
1253
+        $backend->expects($this->any())
1254
+            ->method('implementsActions')
1255
+            ->willReturn(true);
1256
+
1257
+        $targetUser
1258
+            ->expects($this->once())
1259
+            ->method('getDisplayName')
1260
+            ->willReturn('Demo User');
1261
+        $targetUser
1262
+            ->expects($this->never())
1263
+            ->method('getHome');
1264
+        $targetUser
1265
+            ->expects($this->exactly(2))
1266
+            ->method('getLastLogin')
1267
+            ->willReturn(1521191471);
1268
+        $targetUser
1269
+            ->expects($this->once())
1270
+            ->method('getFirstLogin')
1271
+            ->willReturn(1511191471);
1272
+        $targetUser
1273
+            ->expects($this->once())
1274
+            ->method('getBackendClassName')
1275
+            ->willReturn('Database');
1276
+        $targetUser
1277
+            ->expects($this->once())
1278
+            ->method('getBackend')
1279
+            ->willReturn($backend);
1280
+        $targetUser
1281
+            ->method('getUID')
1282
+            ->willReturn('UID');
1283
+
1284
+        $this->mockAccount($targetUser, [
1285
+            IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1286
+            IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1287
+            IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1288
+            IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1289
+            IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1290
+            IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1291
+            IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1292
+            IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1293
+            IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1294
+            IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1295
+            IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1296
+        ]);
1297
+
1298
+        $this->l10nFactory
1299
+            ->expects($this->once())
1300
+            ->method('getUserLanguage')
1301
+            ->with($targetUser)
1302
+            ->willReturn('da');
1303
+
1304
+        $expected = [
1305
+            'id' => 'UID',
1306
+            'enabled' => true,
1307
+            'firstLoginTimestamp' => 1511191471,
1308
+            'lastLoginTimestamp' => 1521191471,
1309
+            'lastLogin' => 1521191471000,
1310
+            'backend' => 'Database',
1311
+            'subadmin' => [],
1312
+            'quota' => ['DummyValue'],
1313
+            'email' => '[email protected]',
1314
+            'displayname' => 'Demo User',
1315
+            'display-name' => 'Demo User',
1316
+            'phone' => 'phone',
1317
+            'address' => 'address',
1318
+            'website' => 'website',
1319
+            'twitter' => 'twitter',
1320
+            'fediverse' => 'fediverse',
1321
+            'groups' => [],
1322
+            'language' => 'da',
1323
+            'locale' => null,
1324
+            'backendCapabilities' => [
1325
+                'setDisplayName' => true,
1326
+                'setPassword' => true,
1327
+            ],
1328
+            'additional_mail' => [],
1329
+            'organisation' => 'organisation',
1330
+            'role' => 'role',
1331
+            'headline' => 'headline',
1332
+            'biography' => 'biography',
1333
+            'profile_enabled' => '1',
1334
+            'notify_email' => null,
1335
+            'manager' => '',
1336
+            'pronouns' => 'they/them',
1337
+        ];
1338
+        $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1339
+    }
1340
+
1341
+
1342
+
1343
+    public function testGetUserDataAsSubAdminAndUserIsNotAccessible(): void {
1344
+        $this->expectException(OCSException::class);
1345
+        $this->expectExceptionCode(998);
1346
+
1347
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1348
+            ->disableOriginalConstructor()
1349
+            ->getMock();
1350
+        $loggedInUser
1351
+            ->expects($this->exactly(4))
1352
+            ->method('getUID')
1353
+            ->willReturn('subadmin');
1354
+        $targetUser = $this->getMockBuilder(IUser::class)
1355
+            ->disableOriginalConstructor()
1356
+            ->getMock();
1357
+        $this->userSession
1358
+            ->method('getUser')
1359
+            ->willReturn($loggedInUser);
1360
+        $this->userManager
1361
+            ->expects($this->once())
1362
+            ->method('get')
1363
+            ->with('UserToGet')
1364
+            ->willReturn($targetUser);
1365
+        $this->groupManager
1366
+            ->expects($this->once())
1367
+            ->method('isAdmin')
1368
+            ->with('subadmin')
1369
+            ->willReturn(false);
1370
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1371
+            ->disableOriginalConstructor()
1372
+            ->getMock();
1373
+        $subAdminManager
1374
+            ->expects($this->once())
1375
+            ->method('isUserAccessible')
1376
+            ->with($loggedInUser, $targetUser)
1377
+            ->willReturn(false);
1378
+        $this->groupManager
1379
+            ->expects($this->once())
1380
+            ->method('getSubAdmin')
1381
+            ->willReturn($subAdminManager);
1382
+
1383
+        $this->invokePrivate($this->api, 'getUser', ['UserToGet']);
1384
+    }
1385
+
1386
+    public function testGetUserDataAsSubAdminSelfLookup(): void {
1387
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1388
+            ->disableOriginalConstructor()
1389
+            ->getMock();
1390
+        $loggedInUser
1391
+            ->method('getUID')
1392
+            ->willReturn('UID');
1393
+        $targetUser = $this->getMockBuilder(IUser::class)
1394
+            ->disableOriginalConstructor()
1395
+            ->getMock();
1396
+        $this->userSession
1397
+            ->method('getUser')
1398
+            ->willReturn($loggedInUser);
1399
+        $this->userManager
1400
+            ->method('get')
1401
+            ->with('UID')
1402
+            ->willReturn($targetUser);
1403
+        $this->groupManager
1404
+            ->method('isAdmin')
1405
+            ->with('UID')
1406
+            ->willReturn(false);
1407
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
1408
+            ->disableOriginalConstructor()
1409
+            ->getMock();
1410
+        $subAdminManager
1411
+            ->expects($this->once())
1412
+            ->method('isUserAccessible')
1413
+            ->with($loggedInUser, $targetUser)
1414
+            ->willReturn(false);
1415
+        $subAdminManager
1416
+            ->expects($this->once())
1417
+            ->method('getSubAdminsGroups')
1418
+            ->willReturn([]);
1419
+        $this->groupManager
1420
+            ->expects($this->exactly(2))
1421
+            ->method('getSubAdmin')
1422
+            ->willReturn($subAdminManager);
1423
+        $this->groupManager
1424
+            ->expects($this->any())
1425
+            ->method('getUserGroups')
1426
+            ->willReturn([]);
1427
+        $this->api
1428
+            ->expects($this->once())
1429
+            ->method('fillStorageInfo')
1430
+            ->with($targetUser)
1431
+            ->willReturn(['DummyValue']);
1432
+
1433
+        $backend = $this->createMock(UserInterface::class);
1434
+        $backend->expects($this->atLeastOnce())
1435
+            ->method('implementsActions')
1436
+            ->willReturn(false);
1437
+
1438
+        $targetUser
1439
+            ->expects($this->once())
1440
+            ->method('getDisplayName')
1441
+            ->willReturn('Subadmin User');
1442
+        $targetUser
1443
+            ->expects($this->once())
1444
+            ->method('getSystemEMailAddress')
1445
+            ->willReturn('[email protected]');
1446
+        $targetUser
1447
+            ->method('getUID')
1448
+            ->willReturn('UID');
1449
+        $targetUser
1450
+            ->expects($this->never())
1451
+            ->method('getHome');
1452
+        $targetUser
1453
+            ->expects($this->exactly(2))
1454
+            ->method('getLastLogin')
1455
+            ->willReturn(1521191471);
1456
+        $targetUser
1457
+            ->expects($this->once())
1458
+            ->method('getFirstLogin')
1459
+            ->willReturn(1511191471);
1460
+        $targetUser
1461
+            ->expects($this->once())
1462
+            ->method('getBackendClassName')
1463
+            ->willReturn('Database');
1464
+        $targetUser
1465
+            ->expects($this->once())
1466
+            ->method('getBackend')
1467
+            ->willReturn($backend);
1468
+        $this->mockAccount($targetUser, [
1469
+            IAccountManager::PROPERTY_ADDRESS => ['value' => 'address'],
1470
+            IAccountManager::PROPERTY_PHONE => ['value' => 'phone'],
1471
+            IAccountManager::PROPERTY_TWITTER => ['value' => 'twitter'],
1472
+            IAccountManager::PROPERTY_FEDIVERSE => ['value' => 'fediverse'],
1473
+            IAccountManager::PROPERTY_WEBSITE => ['value' => 'website'],
1474
+            IAccountManager::PROPERTY_ORGANISATION => ['value' => 'organisation'],
1475
+            IAccountManager::PROPERTY_ROLE => ['value' => 'role'],
1476
+            IAccountManager::PROPERTY_HEADLINE => ['value' => 'headline'],
1477
+            IAccountManager::PROPERTY_BIOGRAPHY => ['value' => 'biography'],
1478
+            IAccountManager::PROPERTY_PROFILE_ENABLED => ['value' => '1'],
1479
+            IAccountManager::PROPERTY_PRONOUNS => ['value' => 'they/them'],
1480
+        ]);
1481
+
1482
+        $this->l10nFactory
1483
+            ->expects($this->once())
1484
+            ->method('getUserLanguage')
1485
+            ->with($targetUser)
1486
+            ->willReturn('ru');
1487
+
1488
+        $expected = [
1489
+            'id' => 'UID',
1490
+            'firstLoginTimestamp' => 1511191471,
1491
+            'lastLoginTimestamp' => 1521191471,
1492
+            'lastLogin' => 1521191471000,
1493
+            'backend' => 'Database',
1494
+            'subadmin' => [],
1495
+            'quota' => ['DummyValue'],
1496
+            'email' => '[email protected]',
1497
+            'displayname' => 'Subadmin User',
1498
+            'display-name' => 'Subadmin User',
1499
+            'phone' => 'phone',
1500
+            'address' => 'address',
1501
+            'website' => 'website',
1502
+            'twitter' => 'twitter',
1503
+            'fediverse' => 'fediverse',
1504
+            'groups' => [],
1505
+            'language' => 'ru',
1506
+            'locale' => null,
1507
+            'backendCapabilities' => [
1508
+                'setDisplayName' => false,
1509
+                'setPassword' => false,
1510
+            ],
1511
+            'additional_mail' => [],
1512
+            'organisation' => 'organisation',
1513
+            'role' => 'role',
1514
+            'headline' => 'headline',
1515
+            'biography' => 'biography',
1516
+            'profile_enabled' => '1',
1517
+            'notify_email' => null,
1518
+            'manager' => '',
1519
+            'pronouns' => 'they/them',
1520
+        ];
1521
+        $this->assertEquals($expected, $this->invokePrivate($this->api, 'getUserData', ['UID']));
1522
+    }
1523
+
1524
+    public static function dataSearchByPhoneNumbers(): array {
1525
+        return [
1526
+            'Invalid country' => ['Not a country code', ['12345' => ['NaN']], 400, null, null, []],
1527
+            'No number to search' => ['DE', ['12345' => ['NaN']], 200, null, null, []],
1528
+            'Valid number but no match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1529
+            'Invalid number' => ['FR', ['12345' => ['0711 / 25 24 28-90']], 200, null, null, []],
1530
+            'Invalid and valid number' => ['DE', ['12345' => ['NaN', '0711 / 25 24 28-90']], 200, ['+4971125242890'], [], []],
1531
+            'Valid and invalid number' => ['DE', ['12345' => ['0711 / 25 24 28-90', 'NaN']], 200, ['+4971125242890'], [], []],
1532
+            'Valid number and a match' => ['DE', ['12345' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['12345' => 'admin@localhost']],
1533
+            'Same number twice, later hits' => ['DE', ['12345' => ['0711 / 25 24 28-90'], '23456' => ['0711 / 25 24 28-90']], 200, ['+4971125242890'], ['+4971125242890' => 'admin'], ['23456' => 'admin@localhost']],
1534
+        ];
1535
+    }
1536
+
1537
+    /**
1538
+     * @dataProvider dataSearchByPhoneNumbers
1539
+     */
1540
+    public function testSearchByPhoneNumbers(string $location, array $search, int $status, ?array $searchUsers, ?array $userMatches, array $expected): void {
1541
+        $knownTo = 'knownTo';
1542
+        $user = $this->createMock(IUser::class);
1543
+        $user->method('getUID')
1544
+            ->willReturn($knownTo);
1545
+        $this->userSession->method('getUser')
1546
+            ->willReturn($user);
1547
+
1548
+        if ($searchUsers === null) {
1549
+            $this->accountManager->expects($this->never())
1550
+                ->method('searchUsers');
1551
+        } else {
1552
+            $this->accountManager->expects($this->once())
1553
+                ->method('searchUsers')
1554
+                ->with(IAccountManager::PROPERTY_PHONE, $searchUsers)
1555
+                ->willReturn($userMatches);
1556
+
1557
+            $this->knownUserService->expects($this->once())
1558
+                ->method('deleteKnownTo')
1559
+                ->with($knownTo);
1560
+
1561
+            $this->knownUserService->expects($this->exactly(count($expected)))
1562
+                ->method('storeIsKnownToUser')
1563
+                ->with($knownTo, $this->anything());
1564
+        }
1565
+
1566
+        $this->urlGenerator->method('getAbsoluteURL')
1567
+            ->with('/')
1568
+            ->willReturn('https://localhost/');
1569
+
1570
+        $response = $this->api->searchByPhoneNumbers($location, $search);
1571
+
1572
+        self::assertEquals($status, $response->getStatus());
1573
+        self::assertEquals($expected, $response->getData());
1574
+    }
1575
+
1576
+    public function testEditUserRegularUserSelfEditChangeDisplayName(): void {
1577
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1578
+            ->disableOriginalConstructor()
1579
+            ->getMock();
1580
+        $loggedInUser
1581
+            ->expects($this->any())
1582
+            ->method('getUID')
1583
+            ->willReturn('UID');
1584
+        $targetUser = $this->getMockBuilder(IUser::class)
1585
+            ->disableOriginalConstructor()
1586
+            ->getMock();
1587
+        $this->userSession
1588
+            ->expects($this->once())
1589
+            ->method('getUser')
1590
+            ->willReturn($loggedInUser);
1591
+        $this->userManager
1592
+            ->expects($this->once())
1593
+            ->method('get')
1594
+            ->with('UserToEdit')
1595
+            ->willReturn($targetUser);
1596
+        $targetUser
1597
+            ->expects($this->once())
1598
+            ->method('getBackend')
1599
+            ->willReturn($this->createMock(ISetDisplayNameBackend::class));
1600
+        $targetUser
1601
+            ->expects($this->once())
1602
+            ->method('setDisplayName')
1603
+            ->with('NewDisplayName')
1604
+            ->willReturn(true);
1605
+        $targetUser
1606
+            ->expects($this->any())
1607
+            ->method('getUID')
1608
+            ->willReturn('UID');
1609
+
1610
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'display', 'NewDisplayName')->getData());
1611
+    }
1612
+
1613
+    public function testEditUserRegularUserSelfEditChangeEmailValid(): void {
1614
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1615
+            ->disableOriginalConstructor()
1616
+            ->getMock();
1617
+        $loggedInUser
1618
+            ->expects($this->any())
1619
+            ->method('getUID')
1620
+            ->willReturn('UID');
1621
+        $targetUser = $this->getMockBuilder(IUser::class)
1622
+            ->disableOriginalConstructor()
1623
+            ->getMock();
1624
+        $this->userSession
1625
+            ->expects($this->once())
1626
+            ->method('getUser')
1627
+            ->willReturn($loggedInUser);
1628
+        $this->userManager
1629
+            ->expects($this->once())
1630
+            ->method('get')
1631
+            ->with('UserToEdit')
1632
+            ->willReturn($targetUser);
1633
+        $targetUser
1634
+            ->expects($this->once())
1635
+            ->method('setEMailAddress')
1636
+            ->with('[email protected]');
1637
+        $targetUser
1638
+            ->expects($this->any())
1639
+            ->method('getUID')
1640
+            ->willReturn('UID');
1641
+
1642
+        $backend = $this->createMock(UserInterface::class);
1643
+        $targetUser
1644
+            ->expects($this->any())
1645
+            ->method('getBackend')
1646
+            ->willReturn($backend);
1647
+
1648
+        $this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1649
+
1650
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'email', '[email protected]')->getData());
1651
+    }
1652
+
1653
+    public function testEditUserRegularUserSelfEditAddAdditionalEmailValid(): void {
1654
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1655
+            ->disableOriginalConstructor()
1656
+            ->getMock();
1657
+        $loggedInUser
1658
+            ->expects($this->any())
1659
+            ->method('getUID')
1660
+            ->willReturn('UID');
1661
+        $targetUser = $this->getMockBuilder(IUser::class)
1662
+            ->disableOriginalConstructor()
1663
+            ->getMock();
1664
+        $this->userSession
1665
+            ->expects($this->once())
1666
+            ->method('getUser')
1667
+            ->willReturn($loggedInUser);
1668
+        $this->userManager
1669
+            ->expects($this->once())
1670
+            ->method('get')
1671
+            ->with('UserToEdit')
1672
+            ->willReturn($targetUser);
1673
+        $targetUser
1674
+            ->expects($this->any())
1675
+            ->method('getUID')
1676
+            ->willReturn('UID');
1677
+
1678
+        $backend = $this->createMock(UserInterface::class);
1679
+        $targetUser
1680
+            ->expects($this->any())
1681
+            ->method('getBackend')
1682
+            ->willReturn($backend);
1683
+
1684
+        $userAccount = $this->createMock(IAccount::class);
1685
+
1686
+        $this->accountManager
1687
+            ->expects($this->once())
1688
+            ->method('getAccount')
1689
+            ->with($targetUser)
1690
+            ->willReturn($userAccount);
1691
+        $this->accountManager
1692
+            ->expects($this->once())
1693
+            ->method('updateAccount')
1694
+            ->with($userAccount);
1695
+
1696
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData());
1697
+    }
1698
+
1699
+    public function testEditUserRegularUserSelfEditAddAdditionalEmailMainAddress(): void {
1700
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1701
+            ->disableOriginalConstructor()
1702
+            ->getMock();
1703
+        $loggedInUser
1704
+            ->expects($this->any())
1705
+            ->method('getUID')
1706
+            ->willReturn('UID');
1707
+        $targetUser = $this->getMockBuilder(IUser::class)
1708
+            ->disableOriginalConstructor()
1709
+            ->getMock();
1710
+        $this->userSession
1711
+            ->expects($this->once())
1712
+            ->method('getUser')
1713
+            ->willReturn($loggedInUser);
1714
+        $this->userManager
1715
+            ->expects($this->once())
1716
+            ->method('get')
1717
+            ->with('UserToEdit')
1718
+            ->willReturn($targetUser);
1719
+        $targetUser
1720
+            ->expects($this->any())
1721
+            ->method('getUID')
1722
+            ->willReturn('UID');
1723
+
1724
+        $backend = $this->createMock(UserInterface::class);
1725
+        $targetUser
1726
+            ->expects($this->any())
1727
+            ->method('getBackend')
1728
+            ->willReturn($backend);
1729
+        $targetUser
1730
+            ->expects($this->any())
1731
+            ->method('getSystemEMailAddress')
1732
+            ->willReturn('[email protected]');
1733
+
1734
+        $userAccount = $this->createMock(IAccount::class);
1735
+
1736
+        $this->accountManager
1737
+            ->expects($this->never())
1738
+            ->method('getAccount')
1739
+            ->with($targetUser)
1740
+            ->willReturn($userAccount);
1741
+        $this->accountManager
1742
+            ->expects($this->never())
1743
+            ->method('updateAccount')
1744
+            ->with($userAccount);
1745
+
1746
+        $this->expectException(OCSException::class);
1747
+        $this->expectExceptionCode(101);
1748
+        $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1749
+    }
1750
+
1751
+    public function testEditUserRegularUserSelfEditAddAdditionalEmailDuplicate(): void {
1752
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1753
+            ->disableOriginalConstructor()
1754
+            ->getMock();
1755
+        $loggedInUser
1756
+            ->expects($this->any())
1757
+            ->method('getUID')
1758
+            ->willReturn('UID');
1759
+        $targetUser = $this->getMockBuilder(IUser::class)
1760
+            ->disableOriginalConstructor()
1761
+            ->getMock();
1762
+        $this->userSession
1763
+            ->expects($this->once())
1764
+            ->method('getUser')
1765
+            ->willReturn($loggedInUser);
1766
+        $this->userManager
1767
+            ->expects($this->once())
1768
+            ->method('get')
1769
+            ->with('UserToEdit')
1770
+            ->willReturn($targetUser);
1771
+        $targetUser
1772
+            ->expects($this->any())
1773
+            ->method('getUID')
1774
+            ->willReturn('UID');
1775
+
1776
+        $backend = $this->createMock(UserInterface::class);
1777
+        $targetUser
1778
+            ->expects($this->any())
1779
+            ->method('getBackend')
1780
+            ->willReturn($backend);
1781
+
1782
+        $property = $this->createMock(IAccountProperty::class);
1783
+        $property->method('getValue')
1784
+            ->willReturn('[email protected]');
1785
+        $collection = $this->createMock(IAccountPropertyCollection::class);
1786
+        $collection->method('getPropertyByValue')
1787
+            ->with('[email protected]')
1788
+            ->willReturn($property);
1789
+
1790
+        $userAccount = $this->createMock(IAccount::class);
1791
+        $userAccount->method('getPropertyCollection')
1792
+            ->with(IAccountManager::COLLECTION_EMAIL)
1793
+            ->willReturn($collection);
1794
+
1795
+        $this->accountManager
1796
+            ->expects($this->once())
1797
+            ->method('getAccount')
1798
+            ->with($targetUser)
1799
+            ->willReturn($userAccount);
1800
+        $this->accountManager
1801
+            ->expects($this->never())
1802
+            ->method('updateAccount')
1803
+            ->with($userAccount);
1804
+
1805
+        $this->expectException(OCSException::class);
1806
+        $this->expectExceptionCode(101);
1807
+        $this->api->editUser('UserToEdit', 'additional_mail', '[email protected]')->getData();
1808
+    }
1809
+
1810
+    public function testEditUserRegularUserSelfEditChangeEmailInvalid(): void {
1811
+        $this->expectException(OCSException::class);
1812
+        $this->expectExceptionCode(101);
1813
+
1814
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1815
+            ->disableOriginalConstructor()
1816
+            ->getMock();
1817
+        $loggedInUser
1818
+            ->expects($this->any())
1819
+            ->method('getUID')
1820
+            ->willReturn('UID');
1821
+        $targetUser = $this->getMockBuilder(IUser::class)
1822
+            ->disableOriginalConstructor()
1823
+            ->getMock();
1824
+        $this->userSession
1825
+            ->expects($this->once())
1826
+            ->method('getUser')
1827
+            ->willReturn($loggedInUser);
1828
+        $this->userManager
1829
+            ->expects($this->once())
1830
+            ->method('get')
1831
+            ->with('UserToEdit')
1832
+            ->willReturn($targetUser);
1833
+        $targetUser
1834
+            ->expects($this->any())
1835
+            ->method('getUID')
1836
+            ->willReturn('UID');
1837
+
1838
+        $backend = $this->createMock(UserInterface::class);
1839
+        $targetUser
1840
+            ->expects($this->any())
1841
+            ->method('getBackend')
1842
+            ->willReturn($backend);
1843
+
1844
+        $this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => $default);
1845
+
1846
+        $this->api->editUser('UserToEdit', 'email', 'demo.org');
1847
+    }
1848
+
1849
+    public static function selfEditChangePropertyProvider(): array {
1850
+        return [
1851
+            [IAccountManager::PROPERTY_TWITTER, '@oldtwitter', '@newtwitter'],
1852
+            [IAccountManager::PROPERTY_FEDIVERSE, '@[email protected]', '@[email protected]'],
1853
+            [IAccountManager::PROPERTY_PHONE, '1234', '12345'],
1854
+            [IAccountManager::PROPERTY_ADDRESS, 'Something street 2', 'Another street 3'],
1855
+            [IAccountManager::PROPERTY_WEBSITE, 'https://examplesite1', 'https://examplesite2'],
1856
+            [IAccountManager::PROPERTY_ORGANISATION, 'Organisation A', 'Organisation B'],
1857
+            [IAccountManager::PROPERTY_ROLE, 'Human', 'Alien'],
1858
+            [IAccountManager::PROPERTY_HEADLINE, 'Hi', 'Hello'],
1859
+            [IAccountManager::PROPERTY_BIOGRAPHY, 'A biography', 'Another biography'],
1860
+            [IAccountManager::PROPERTY_PROFILE_ENABLED, '1', '0'],
1861
+            [IAccountManager::PROPERTY_PRONOUNS, 'they/them', 'he/him'],
1862
+        ];
1863
+    }
1864
+
1865
+    /**
1866
+     * @dataProvider selfEditChangePropertyProvider
1867
+     */
1868
+    public function testEditUserRegularUserSelfEditChangeProperty($propertyName, $oldValue, $newValue): void {
1869
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1870
+            ->disableOriginalConstructor()
1871
+            ->getMock();
1872
+        $loggedInUser
1873
+            ->expects($this->any())
1874
+            ->method('getUID')
1875
+            ->willReturn('UID');
1876
+        $this->userSession
1877
+            ->expects($this->once())
1878
+            ->method('getUser')
1879
+            ->willReturn($loggedInUser);
1880
+        $this->userManager
1881
+            ->expects($this->once())
1882
+            ->method('get')
1883
+            ->with('UserToEdit')
1884
+            ->willReturn($loggedInUser);
1885
+
1886
+        $backend = $this->createMock(UserInterface::class);
1887
+        $loggedInUser
1888
+            ->expects($this->any())
1889
+            ->method('getBackend')
1890
+            ->willReturn($backend);
1891
+
1892
+        $propertyMock = $this->createMock(IAccountProperty::class);
1893
+        $propertyMock->expects($this->any())
1894
+            ->method('getName')
1895
+            ->willReturn($propertyName);
1896
+        $propertyMock->expects($this->any())
1897
+            ->method('getValue')
1898
+            ->willReturn($oldValue);
1899
+        $propertyMock->expects($this->once())
1900
+            ->method('setValue')
1901
+            ->with($newValue)
1902
+            ->willReturnSelf();
1903
+        $propertyMock->expects($this->any())
1904
+            ->method('getScope')
1905
+            ->willReturn(IAccountManager::SCOPE_LOCAL);
1906
+
1907
+        $accountMock = $this->createMock(IAccount::class);
1908
+        $accountMock->expects($this->any())
1909
+            ->method('getProperty')
1910
+            ->with($propertyName)
1911
+            ->willReturn($propertyMock);
1912
+
1913
+        $this->accountManager->expects($this->atLeastOnce())
1914
+            ->method('getAccount')
1915
+            ->with($loggedInUser)
1916
+            ->willReturn($accountMock);
1917
+        $this->accountManager->expects($this->once())
1918
+            ->method('updateAccount')
1919
+            ->with($accountMock);
1920
+
1921
+        $this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName, $newValue)->getData());
1922
+    }
1923
+
1924
+    public function selfEditChangePropertyScopeProvider() {
1925
+        return [
1926
+            [IAccountManager::PROPERTY_AVATAR, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1927
+            [IAccountManager::PROPERTY_DISPLAYNAME, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1928
+            [IAccountManager::PROPERTY_EMAIL, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1929
+            [IAccountManager::PROPERTY_TWITTER, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1930
+            [IAccountManager::PROPERTY_FEDIVERSE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1931
+            [IAccountManager::PROPERTY_PHONE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1932
+            [IAccountManager::PROPERTY_ADDRESS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1933
+            [IAccountManager::PROPERTY_WEBSITE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1934
+            [IAccountManager::PROPERTY_ORGANISATION, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1935
+            [IAccountManager::PROPERTY_ROLE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1936
+            [IAccountManager::PROPERTY_HEADLINE, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1937
+            [IAccountManager::PROPERTY_BIOGRAPHY, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1938
+            [IAccountManager::PROPERTY_PROFILE_ENABLED, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1939
+            [IAccountManager::PROPERTY_PRONOUNS, IAccountManager::SCOPE_LOCAL, IAccountManager::SCOPE_FEDERATED],
1940
+        ];
1941
+    }
1942
+
1943
+    /**
1944
+     * @dataProvider selfEditChangePropertyProvider
1945
+     */
1946
+    public function testEditUserRegularUserSelfEditChangePropertyScope($propertyName, $oldScope, $newScope): void {
1947
+        $loggedInUser = $this->getMockBuilder(IUser::class)
1948
+            ->disableOriginalConstructor()
1949
+            ->getMock();
1950
+        $loggedInUser
1951
+            ->expects($this->any())
1952
+            ->method('getUID')
1953
+            ->willReturn('UID');
1954
+        $this->userSession
1955
+            ->expects($this->once())
1956
+            ->method('getUser')
1957
+            ->willReturn($loggedInUser);
1958
+        $this->userManager
1959
+            ->expects($this->once())
1960
+            ->method('get')
1961
+            ->with('UserToEdit')
1962
+            ->willReturn($loggedInUser);
1963
+
1964
+        $backend = $this->createMock(UserInterface::class);
1965
+        $loggedInUser
1966
+            ->expects($this->any())
1967
+            ->method('getBackend')
1968
+            ->willReturn($backend);
1969
+
1970
+        $propertyMock = $this->createMock(IAccountProperty::class);
1971
+        $propertyMock->expects($this->any())
1972
+            ->method('getName')
1973
+            ->willReturn($propertyName);
1974
+        $propertyMock->expects($this->any())
1975
+            ->method('getValue')
1976
+            ->willReturn('somevalue');
1977
+        $propertyMock->expects($this->any())
1978
+            ->method('getScope')
1979
+            ->willReturn($oldScope);
1980
+        $propertyMock->expects($this->atLeastOnce())
1981
+            ->method('setScope')
1982
+            ->with($newScope)
1983
+            ->willReturnSelf();
1984
+
1985
+        $accountMock = $this->createMock(IAccount::class);
1986
+        $accountMock->expects($this->any())
1987
+            ->method('getProperty')
1988
+            ->with($propertyName)
1989
+            ->willReturn($propertyMock);
1990
+
1991
+        $this->accountManager->expects($this->atLeastOnce())
1992
+            ->method('getAccount')
1993
+            ->with($loggedInUser)
1994
+            ->willReturn($accountMock);
1995
+        $this->accountManager->expects($this->once())
1996
+            ->method('updateAccount')
1997
+            ->with($accountMock);
1998
+
1999
+        $this->assertEquals([], $this->api->editUser('UserToEdit', $propertyName . 'Scope', $newScope)->getData());
2000
+    }
2001
+
2002
+    public function testEditUserRegularUserSelfEditChangePassword(): void {
2003
+        $loggedInUser = $this->getMockBuilder(IUser::class)
2004
+            ->disableOriginalConstructor()
2005
+            ->getMock();
2006
+        $loggedInUser
2007
+            ->expects($this->any())
2008
+            ->method('getUID')
2009
+            ->willReturn('UID');
2010
+        $targetUser = $this->getMockBuilder(IUser::class)
2011
+            ->disableOriginalConstructor()
2012
+            ->getMock();
2013
+        $this->userSession
2014
+            ->expects($this->once())
2015
+            ->method('getUser')
2016
+            ->willReturn($loggedInUser);
2017
+        $this->userManager
2018
+            ->expects($this->once())
2019
+            ->method('get')
2020
+            ->with('UserToEdit')
2021
+            ->willReturn($targetUser);
2022
+        $targetUser
2023
+            ->expects($this->once())
2024
+            ->method('canChangePassword')
2025
+            ->willReturn(true);
2026
+        $targetUser
2027
+            ->expects($this->once())
2028
+            ->method('setPassword')
2029
+            ->with('NewPassword');
2030
+        $targetUser
2031
+            ->expects($this->any())
2032
+            ->method('getUID')
2033
+            ->willReturn('UID');
2034
+
2035
+        $backend = $this->createMock(UserInterface::class);
2036
+        $targetUser
2037
+            ->expects($this->any())
2038
+            ->method('getBackend')
2039
+            ->willReturn($backend);
2040
+
2041
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'password', 'NewPassword')->getData());
2042
+    }
2043
+
2044
+
2045
+
2046
+    public function testEditUserRegularUserSelfEditChangeQuota(): void {
2047
+        $this->expectException(OCSException::class);
2048
+        $this->expectExceptionCode(113);
2049
+
2050
+        $loggedInUser = $this->getMockBuilder(IUser::class)
2051
+            ->disableOriginalConstructor()
2052
+            ->getMock();
2053
+        $loggedInUser
2054
+            ->expects($this->any())
2055
+            ->method('getUID')
2056
+            ->willReturn('UID');
2057
+        $targetUser = $this->getMockBuilder(IUser::class)
2058
+            ->disableOriginalConstructor()
2059
+            ->getMock();
2060
+        $this->userSession
2061
+            ->expects($this->once())
2062
+            ->method('getUser')
2063
+            ->willReturn($loggedInUser);
2064
+        $this->userManager
2065
+            ->expects($this->once())
2066
+            ->method('get')
2067
+            ->with('UserToEdit')
2068
+            ->willReturn($targetUser);
2069
+        $targetUser
2070
+            ->expects($this->any())
2071
+            ->method('getUID')
2072
+            ->willReturn('UID');
2073
+
2074
+        $backend = $this->createMock(UserInterface::class);
2075
+        $targetUser
2076
+            ->expects($this->any())
2077
+            ->method('getBackend')
2078
+            ->willReturn($backend);
2079
+
2080
+        $this->api->editUser('UserToEdit', 'quota', 'NewQuota');
2081
+    }
2082
+
2083
+    public function testEditUserAdminUserSelfEditChangeValidQuota(): void {
2084
+        $this->config
2085
+            ->expects($this->once())
2086
+            ->method('getAppValue')
2087
+            ->willReturnCallback(function ($appid, $key, $default) {
2088
+                if ($key === 'max_quota') {
2089
+                    return '-1';
2090
+                }
2091
+                return null;
2092
+            });
2093
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2094
+        $loggedInUser
2095
+            ->expects($this->any())
2096
+            ->method('getUID')
2097
+            ->willReturn('UID');
2098
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2099
+        $targetUser->expects($this->once())
2100
+            ->method('setQuota')
2101
+            ->with('2.9 MB');
2102
+        $this->userSession
2103
+            ->expects($this->once())
2104
+            ->method('getUser')
2105
+            ->willReturn($loggedInUser);
2106
+        $this->userManager
2107
+            ->expects($this->once())
2108
+            ->method('get')
2109
+            ->with('UserToEdit')
2110
+            ->willReturn($targetUser);
2111
+        $this->groupManager
2112
+            ->expects($this->exactly(3))
2113
+            ->method('isAdmin')
2114
+            ->with('UID')
2115
+            ->willReturn(true);
2116
+        $targetUser
2117
+            ->expects($this->any())
2118
+            ->method('getUID')
2119
+            ->willReturn('UID');
2120
+
2121
+        $backend = $this->createMock(UserInterface::class);
2122
+        $targetUser
2123
+            ->expects($this->any())
2124
+            ->method('getBackend')
2125
+            ->willReturn($backend);
2126
+
2127
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2128
+    }
2129
+
2130
+
2131
+
2132
+    public function testEditUserAdminUserSelfEditChangeInvalidQuota(): void {
2133
+        $this->expectException(OCSException::class);
2134
+        $this->expectExceptionMessage('Invalid quota value: ABC');
2135
+        $this->expectExceptionCode(101);
2136
+
2137
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2138
+        $loggedInUser
2139
+            ->expects($this->any())
2140
+            ->method('getUID')
2141
+            ->willReturn('UID');
2142
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2143
+        $this->userSession
2144
+            ->expects($this->once())
2145
+            ->method('getUser')
2146
+            ->willReturn($loggedInUser);
2147
+        $this->userManager
2148
+            ->expects($this->once())
2149
+            ->method('get')
2150
+            ->with('UserToEdit')
2151
+            ->willReturn($targetUser);
2152
+        $this->groupManager
2153
+            ->expects($this->exactly(3))
2154
+            ->method('isAdmin')
2155
+            ->with('UID')
2156
+            ->willReturn(true);
2157
+        $targetUser
2158
+            ->expects($this->any())
2159
+            ->method('getUID')
2160
+            ->willReturn('UID');
2161
+
2162
+        $backend = $this->createMock(UserInterface::class);
2163
+        $targetUser
2164
+            ->expects($this->any())
2165
+            ->method('getBackend')
2166
+            ->willReturn($backend);
2167
+
2168
+        $this->api->editUser('UserToEdit', 'quota', 'ABC');
2169
+    }
2170
+
2171
+    public function testEditUserAdminUserEditChangeValidQuota(): void {
2172
+        $this->config
2173
+            ->expects($this->once())
2174
+            ->method('getAppValue')
2175
+            ->willReturnCallback(function ($appid, $key, $default) {
2176
+                if ($key === 'max_quota') {
2177
+                    return '-1';
2178
+                }
2179
+                return null;
2180
+            });
2181
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2182
+        $loggedInUser
2183
+            ->expects($this->any())
2184
+            ->method('getUID')
2185
+            ->willReturn('admin');
2186
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2187
+        $targetUser->expects($this->once())
2188
+            ->method('setQuota')
2189
+            ->with('2.9 MB');
2190
+        $this->userSession
2191
+            ->expects($this->once())
2192
+            ->method('getUser')
2193
+            ->willReturn($loggedInUser);
2194
+        $this->userManager
2195
+            ->expects($this->once())
2196
+            ->method('get')
2197
+            ->with('UserToEdit')
2198
+            ->willReturn($targetUser);
2199
+        $this->groupManager
2200
+            ->expects($this->once())
2201
+            ->method('isAdmin')
2202
+            ->with('admin')
2203
+            ->willReturn(true);
2204
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2205
+            ->disableOriginalConstructor()
2206
+            ->getMock();
2207
+        $this->groupManager
2208
+            ->expects($this->once())
2209
+            ->method('getSubAdmin')
2210
+            ->willReturn($subAdminManager);
2211
+        $targetUser
2212
+            ->expects($this->any())
2213
+            ->method('getUID')
2214
+            ->willReturn('UID');
2215
+
2216
+        $backend = $this->createMock(UserInterface::class);
2217
+        $targetUser
2218
+            ->expects($this->any())
2219
+            ->method('getBackend')
2220
+            ->willReturn($backend);
2221
+
2222
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2223
+    }
2224
+
2225
+    public function testEditUserSelfEditChangeLanguage(): void {
2226
+        $this->l10nFactory->expects($this->once())
2227
+            ->method('findAvailableLanguages')
2228
+            ->willReturn(['en', 'de', 'sv']);
2229
+        $this->config->expects($this->any())
2230
+            ->method('getSystemValue')
2231
+            ->willReturnMap([
2232
+                ['allow_user_to_change_display_name', true, true],
2233
+                ['force_language', false, false],
2234
+            ]);
2235
+
2236
+        $loggedInUser = $this->createMock(IUser::class);
2237
+        $loggedInUser
2238
+            ->expects($this->any())
2239
+            ->method('getUID')
2240
+            ->willReturn('UserToEdit');
2241
+        $targetUser = $this->createMock(IUser::class);
2242
+        $this->config->expects($this->once())
2243
+            ->method('setUserValue')
2244
+            ->with('UserToEdit', 'core', 'lang', 'de');
2245
+        $this->userSession
2246
+            ->expects($this->once())
2247
+            ->method('getUser')
2248
+            ->willReturn($loggedInUser);
2249
+        $this->userManager
2250
+            ->expects($this->once())
2251
+            ->method('get')
2252
+            ->with('UserToEdit')
2253
+            ->willReturn($targetUser);
2254
+        $this->groupManager
2255
+            ->expects($this->atLeastOnce())
2256
+            ->method('isAdmin')
2257
+            ->with('UserToEdit')
2258
+            ->willReturn(false);
2259
+        $targetUser
2260
+            ->expects($this->any())
2261
+            ->method('getUID')
2262
+            ->willReturn('UserToEdit');
2263
+
2264
+        $backend = $this->createMock(UserInterface::class);
2265
+        $targetUser
2266
+            ->expects($this->any())
2267
+            ->method('getBackend')
2268
+            ->willReturn($backend);
2269
+
2270
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2271
+    }
2272
+
2273
+    public static function dataEditUserSelfEditChangeLanguageButForced(): array {
2274
+        return [
2275
+            ['de'],
2276
+            [true],
2277
+        ];
2278
+    }
2279
+
2280
+    /**
2281
+     * @dataProvider dataEditUserSelfEditChangeLanguageButForced
2282
+     */
2283
+    public function testEditUserSelfEditChangeLanguageButForced($forced): void {
2284
+        $this->expectException(OCSException::class);
2285
+
2286
+        $this->config->expects($this->any())
2287
+            ->method('getSystemValue')
2288
+            ->willReturnMap([
2289
+                ['allow_user_to_change_display_name', true, true],
2290
+                ['force_language', false, $forced],
2291
+            ]);
2292
+
2293
+        $loggedInUser = $this->createMock(IUser::class);
2294
+        $loggedInUser
2295
+            ->expects($this->any())
2296
+            ->method('getUID')
2297
+            ->willReturn('UserToEdit');
2298
+        $targetUser = $this->createMock(IUser::class);
2299
+        $this->config->expects($this->never())
2300
+            ->method('setUserValue');
2301
+        $this->userSession
2302
+            ->expects($this->once())
2303
+            ->method('getUser')
2304
+            ->willReturn($loggedInUser);
2305
+        $this->userManager
2306
+            ->expects($this->once())
2307
+            ->method('get')
2308
+            ->with('UserToEdit')
2309
+            ->willReturn($targetUser);
2310
+        $this->groupManager
2311
+            ->expects($this->atLeastOnce())
2312
+            ->method('isAdmin')
2313
+            ->with('UserToEdit')
2314
+            ->willReturn(false);
2315
+        $targetUser
2316
+            ->expects($this->any())
2317
+            ->method('getUID')
2318
+            ->willReturn('UserToEdit');
2319
+
2320
+        $backend = $this->createMock(UserInterface::class);
2321
+        $targetUser
2322
+            ->expects($this->any())
2323
+            ->method('getBackend')
2324
+            ->willReturn($backend);
2325
+
2326
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2327
+    }
2328
+
2329
+    public function testEditUserAdminEditChangeLanguage(): void {
2330
+        $this->l10nFactory->expects($this->once())
2331
+            ->method('findAvailableLanguages')
2332
+            ->willReturn(['en', 'de', 'sv']);
2333
+
2334
+        $loggedInUser = $this->createMock(IUser::class);
2335
+        $loggedInUser
2336
+            ->expects($this->any())
2337
+            ->method('getUID')
2338
+            ->willReturn('admin');
2339
+        $targetUser = $this->createMock(IUser::class);
2340
+        $this->config->expects($this->once())
2341
+            ->method('setUserValue')
2342
+            ->with('UserToEdit', 'core', 'lang', 'de');
2343
+        $this->userSession
2344
+            ->expects($this->once())
2345
+            ->method('getUser')
2346
+            ->willReturn($loggedInUser);
2347
+        $this->userManager
2348
+            ->expects($this->once())
2349
+            ->method('get')
2350
+            ->with('UserToEdit')
2351
+            ->willReturn($targetUser);
2352
+        $this->groupManager
2353
+            ->expects($this->once())
2354
+            ->method('isAdmin')
2355
+            ->with('admin')
2356
+            ->willReturn(true);
2357
+        $subAdminManager = $this->createMock(SubAdmin::class);
2358
+        $this->groupManager
2359
+            ->expects($this->once())
2360
+            ->method('getSubAdmin')
2361
+            ->willReturn($subAdminManager);
2362
+        $targetUser
2363
+            ->expects($this->any())
2364
+            ->method('getUID')
2365
+            ->willReturn('UserToEdit');
2366
+
2367
+        $backend = $this->createMock(UserInterface::class);
2368
+        $targetUser
2369
+            ->expects($this->any())
2370
+            ->method('getBackend')
2371
+            ->willReturn($backend);
2372
+
2373
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'de')->getData());
2374
+    }
2375
+
2376
+    /**
2377
+     * @dataProvider dataEditUserSelfEditChangeLanguageButForced
2378
+     */
2379
+    public function testEditUserAdminEditChangeLanguageInvalidLanguage(): void {
2380
+        $this->expectException(OCSException::class);
2381
+
2382
+
2383
+        $this->l10nFactory->expects($this->once())
2384
+            ->method('findAvailableLanguages')
2385
+            ->willReturn(['en', 'de', 'sv']);
2386
+
2387
+        $loggedInUser = $this->createMock(IUser::class);
2388
+        $loggedInUser
2389
+            ->expects($this->any())
2390
+            ->method('getUID')
2391
+            ->willReturn('admin');
2392
+        $targetUser = $this->createMock(IUser::class);
2393
+        $this->config->expects($this->never())
2394
+            ->method('setUserValue');
2395
+        $this->userSession
2396
+            ->expects($this->once())
2397
+            ->method('getUser')
2398
+            ->willReturn($loggedInUser);
2399
+        $this->userManager
2400
+            ->expects($this->once())
2401
+            ->method('get')
2402
+            ->with('UserToEdit')
2403
+            ->willReturn($targetUser);
2404
+        $this->groupManager
2405
+            ->expects($this->once())
2406
+            ->method('isAdmin')
2407
+            ->with('admin')
2408
+            ->willReturn(true);
2409
+        $subAdminManager = $this->createMock(SubAdmin::class);
2410
+        $this->groupManager
2411
+            ->expects($this->once())
2412
+            ->method('getSubAdmin')
2413
+            ->willReturn($subAdminManager);
2414
+        $targetUser
2415
+            ->expects($this->any())
2416
+            ->method('getUID')
2417
+            ->willReturn('UserToEdit');
2418
+
2419
+        $backend = $this->createMock(UserInterface::class);
2420
+        $targetUser
2421
+            ->expects($this->any())
2422
+            ->method('getBackend')
2423
+            ->willReturn($backend);
2424
+
2425
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'language', 'ru')->getData());
2426
+    }
2427
+
2428
+    public function testEditUserSubadminUserAccessible(): void {
2429
+        $this->config
2430
+            ->expects($this->once())
2431
+            ->method('getAppValue')
2432
+            ->willReturnCallback(function ($appid, $key, $default) {
2433
+                if ($key === 'max_quota') {
2434
+                    return '-1';
2435
+                }
2436
+                return null;
2437
+            });
2438
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2439
+        $loggedInUser
2440
+            ->expects($this->any())
2441
+            ->method('getUID')
2442
+            ->willReturn('subadmin');
2443
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2444
+        $targetUser->expects($this->once())
2445
+            ->method('setQuota')
2446
+            ->with('2.9 MB');
2447
+        $this->userSession
2448
+            ->expects($this->once())
2449
+            ->method('getUser')
2450
+            ->willReturn($loggedInUser);
2451
+        $this->userManager
2452
+            ->expects($this->once())
2453
+            ->method('get')
2454
+            ->with('UserToEdit')
2455
+            ->willReturn($targetUser);
2456
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2457
+            ->disableOriginalConstructor()
2458
+            ->getMock();
2459
+        $subAdminManager
2460
+            ->expects($this->once())
2461
+            ->method('isUserAccessible')
2462
+            ->with($loggedInUser, $targetUser)
2463
+            ->willReturn(true);
2464
+        $this->groupManager
2465
+            ->expects($this->once())
2466
+            ->method('getSubAdmin')
2467
+            ->willReturn($subAdminManager);
2468
+        $targetUser
2469
+            ->expects($this->any())
2470
+            ->method('getUID')
2471
+            ->willReturn('UID');
2472
+
2473
+        $backend = $this->createMock(UserInterface::class);
2474
+        $targetUser
2475
+            ->expects($this->any())
2476
+            ->method('getBackend')
2477
+            ->willReturn($backend);
2478
+
2479
+        $this->assertEquals([], $this->api->editUser('UserToEdit', 'quota', '3042824')->getData());
2480
+    }
2481
+
2482
+
2483
+    public function testEditUserSubadminUserInaccessible(): void {
2484
+        $this->expectException(OCSException::class);
2485
+        $this->expectExceptionCode(998);
2486
+
2487
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2488
+        $loggedInUser
2489
+            ->expects($this->any())
2490
+            ->method('getUID')
2491
+            ->willReturn('subadmin');
2492
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2493
+        $this->userSession
2494
+            ->expects($this->once())
2495
+            ->method('getUser')
2496
+            ->willReturn($loggedInUser);
2497
+        $this->userManager
2498
+            ->expects($this->once())
2499
+            ->method('get')
2500
+            ->with('UserToEdit')
2501
+            ->willReturn($targetUser);
2502
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2503
+            ->disableOriginalConstructor()
2504
+            ->getMock();
2505
+        $subAdminManager
2506
+            ->expects($this->once())
2507
+            ->method('isUserAccessible')
2508
+            ->with($loggedInUser, $targetUser)
2509
+            ->willReturn(false);
2510
+        $this->groupManager
2511
+            ->expects($this->once())
2512
+            ->method('getSubAdmin')
2513
+            ->willReturn($subAdminManager);
2514
+        $targetUser
2515
+            ->expects($this->any())
2516
+            ->method('getUID')
2517
+            ->willReturn('UID');
2518
+
2519
+        $this->api->editUser('UserToEdit', 'quota', 'value');
2520
+    }
2521
+
2522
+
2523
+    public function testDeleteUserNotExistingUser(): void {
2524
+        $this->expectException(OCSException::class);
2525
+        $this->expectExceptionCode(998);
2526
+
2527
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2528
+        $loggedInUser
2529
+            ->expects($this->any())
2530
+            ->method('getUID')
2531
+            ->willReturn('UserToEdit');
2532
+        $this->userSession
2533
+            ->expects($this->once())
2534
+            ->method('getUser')
2535
+            ->willReturn($loggedInUser);
2536
+        $this->userManager
2537
+            ->expects($this->once())
2538
+            ->method('get')
2539
+            ->with('UserToDelete')
2540
+            ->willReturn(null);
2541
+
2542
+        $this->api->deleteUser('UserToDelete');
2543
+    }
2544
+
2545
+
2546
+    public function testDeleteUserSelf(): void {
2547
+        $this->expectException(OCSException::class);
2548
+        $this->expectExceptionCode(101);
2549
+
2550
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2551
+        $loggedInUser
2552
+            ->expects($this->any())
2553
+            ->method('getUID')
2554
+            ->willReturn('UID');
2555
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2556
+        $targetUser
2557
+            ->expects($this->once())
2558
+            ->method('getUID')
2559
+            ->willReturn('UID');
2560
+        $this->userSession
2561
+            ->expects($this->once())
2562
+            ->method('getUser')
2563
+            ->willReturn($loggedInUser);
2564
+        $this->userManager
2565
+            ->expects($this->once())
2566
+            ->method('get')
2567
+            ->with('UserToDelete')
2568
+            ->willReturn($targetUser);
2569
+
2570
+        $this->api->deleteUser('UserToDelete');
2571
+    }
2572
+
2573
+    public function testDeleteSuccessfulUserAsAdmin(): void {
2574
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2575
+        $loggedInUser
2576
+            ->expects($this->any())
2577
+            ->method('getUID')
2578
+            ->willReturn('admin');
2579
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2580
+        $targetUser
2581
+            ->expects($this->once())
2582
+            ->method('getUID')
2583
+            ->willReturn('UID');
2584
+        $this->userSession
2585
+            ->expects($this->once())
2586
+            ->method('getUser')
2587
+            ->willReturn($loggedInUser);
2588
+        $this->userManager
2589
+            ->expects($this->once())
2590
+            ->method('get')
2591
+            ->with('UserToDelete')
2592
+            ->willReturn($targetUser);
2593
+        $this->groupManager
2594
+            ->expects($this->once())
2595
+            ->method('isAdmin')
2596
+            ->with('admin')
2597
+            ->willReturn(true);
2598
+        $targetUser
2599
+            ->expects($this->once())
2600
+            ->method('delete')
2601
+            ->willReturn(true);
2602
+
2603
+        $this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2604
+    }
2605
+
2606
+
2607
+    public function testDeleteUnsuccessfulUserAsAdmin(): void {
2608
+        $this->expectException(OCSException::class);
2609
+        $this->expectExceptionCode(101);
2610
+
2611
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2612
+        $loggedInUser
2613
+            ->expects($this->any())
2614
+            ->method('getUID')
2615
+            ->willReturn('admin');
2616
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2617
+        $targetUser
2618
+            ->expects($this->once())
2619
+            ->method('getUID')
2620
+            ->willReturn('UID');
2621
+        $this->userSession
2622
+            ->expects($this->once())
2623
+            ->method('getUser')
2624
+            ->willReturn($loggedInUser);
2625
+        $this->userManager
2626
+            ->expects($this->once())
2627
+            ->method('get')
2628
+            ->with('UserToDelete')
2629
+            ->willReturn($targetUser);
2630
+        $this->groupManager
2631
+            ->expects($this->once())
2632
+            ->method('isAdmin')
2633
+            ->with('admin')
2634
+            ->willReturn(true);
2635
+        $targetUser
2636
+            ->expects($this->once())
2637
+            ->method('delete')
2638
+            ->willReturn(false);
2639
+
2640
+        $this->api->deleteUser('UserToDelete');
2641
+    }
2642
+
2643
+    public function testDeleteSuccessfulUserAsSubadmin(): void {
2644
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2645
+        $loggedInUser
2646
+            ->expects($this->any())
2647
+            ->method('getUID')
2648
+            ->willReturn('subadmin');
2649
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2650
+        $targetUser
2651
+            ->expects($this->once())
2652
+            ->method('getUID')
2653
+            ->willReturn('UID');
2654
+        $this->userSession
2655
+            ->expects($this->once())
2656
+            ->method('getUser')
2657
+            ->willReturn($loggedInUser);
2658
+        $this->userManager
2659
+            ->expects($this->once())
2660
+            ->method('get')
2661
+            ->with('UserToDelete')
2662
+            ->willReturn($targetUser);
2663
+        $this->groupManager
2664
+            ->expects($this->once())
2665
+            ->method('isAdmin')
2666
+            ->with('subadmin')
2667
+            ->willReturn(false);
2668
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2669
+            ->disableOriginalConstructor()->getMock();
2670
+        $subAdminManager
2671
+            ->expects($this->once())
2672
+            ->method('isUserAccessible')
2673
+            ->with($loggedInUser, $targetUser)
2674
+            ->willReturn(true);
2675
+        $this->groupManager
2676
+            ->expects($this->once())
2677
+            ->method('getSubAdmin')
2678
+            ->willReturn($subAdminManager);
2679
+        $targetUser
2680
+            ->expects($this->once())
2681
+            ->method('delete')
2682
+            ->willReturn(true);
2683
+
2684
+        $this->assertEquals([], $this->api->deleteUser('UserToDelete')->getData());
2685
+    }
2686
+
2687
+
2688
+    public function testDeleteUnsuccessfulUserAsSubadmin(): void {
2689
+        $this->expectException(OCSException::class);
2690
+        $this->expectExceptionCode(101);
2691
+
2692
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2693
+        $loggedInUser
2694
+            ->expects($this->any())
2695
+            ->method('getUID')
2696
+            ->willReturn('subadmin');
2697
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2698
+        $targetUser
2699
+            ->expects($this->once())
2700
+            ->method('getUID')
2701
+            ->willReturn('UID');
2702
+        $this->userSession
2703
+            ->expects($this->once())
2704
+            ->method('getUser')
2705
+            ->willReturn($loggedInUser);
2706
+        $this->userManager
2707
+            ->expects($this->once())
2708
+            ->method('get')
2709
+            ->with('UserToDelete')
2710
+            ->willReturn($targetUser);
2711
+        $this->groupManager
2712
+            ->expects($this->once())
2713
+            ->method('isAdmin')
2714
+            ->with('subadmin')
2715
+            ->willReturn(false);
2716
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2717
+            ->disableOriginalConstructor()->getMock();
2718
+        $subAdminManager
2719
+            ->expects($this->once())
2720
+            ->method('isUserAccessible')
2721
+            ->with($loggedInUser, $targetUser)
2722
+            ->willReturn(true);
2723
+        $this->groupManager
2724
+            ->expects($this->once())
2725
+            ->method('getSubAdmin')
2726
+            ->willReturn($subAdminManager);
2727
+        $targetUser
2728
+            ->expects($this->once())
2729
+            ->method('delete')
2730
+            ->willReturn(false);
2731
+
2732
+        $this->api->deleteUser('UserToDelete');
2733
+    }
2734
+
2735
+
2736
+    public function testDeleteUserAsSubAdminAndUserIsNotAccessible(): void {
2737
+        $this->expectException(OCSException::class);
2738
+        $this->expectExceptionCode(998);
2739
+
2740
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2741
+        $loggedInUser
2742
+            ->expects($this->any())
2743
+            ->method('getUID')
2744
+            ->willReturn('subadmin');
2745
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2746
+        $targetUser
2747
+            ->expects($this->once())
2748
+            ->method('getUID')
2749
+            ->willReturn('UID');
2750
+        $this->userSession
2751
+            ->expects($this->once())
2752
+            ->method('getUser')
2753
+            ->willReturn($loggedInUser);
2754
+        $this->userManager
2755
+            ->expects($this->once())
2756
+            ->method('get')
2757
+            ->with('UserToDelete')
2758
+            ->willReturn($targetUser);
2759
+        $this->groupManager
2760
+            ->expects($this->once())
2761
+            ->method('isAdmin')
2762
+            ->with('subadmin')
2763
+            ->willReturn(false);
2764
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2765
+            ->disableOriginalConstructor()->getMock();
2766
+        $subAdminManager
2767
+            ->expects($this->once())
2768
+            ->method('isUserAccessible')
2769
+            ->with($loggedInUser, $targetUser)
2770
+            ->willReturn(false);
2771
+        $this->groupManager
2772
+            ->expects($this->once())
2773
+            ->method('getSubAdmin')
2774
+            ->willReturn($subAdminManager);
2775
+
2776
+        $this->api->deleteUser('UserToDelete');
2777
+    }
2778
+
2779
+
2780
+    public function testGetUsersGroupsTargetUserNotExisting(): void {
2781
+        $this->expectException(OCSException::class);
2782
+        $this->expectExceptionCode(998);
2783
+
2784
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2785
+        $this->userSession
2786
+            ->expects($this->once())
2787
+            ->method('getUser')
2788
+            ->willReturn($loggedInUser);
2789
+
2790
+        $this->api->getUsersGroups('UserToLookup');
2791
+    }
2792
+
2793
+    public function testGetUsersGroupsSelfTargetted(): void {
2794
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2795
+        $loggedInUser
2796
+            ->expects($this->exactly(3))
2797
+            ->method('getUID')
2798
+            ->willReturn('UserToLookup');
2799
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2800
+        $targetUser
2801
+            ->expects($this->once())
2802
+            ->method('getUID')
2803
+            ->willReturn('UserToLookup');
2804
+        $this->userSession
2805
+            ->expects($this->once())
2806
+            ->method('getUser')
2807
+            ->willReturn($loggedInUser);
2808
+        $this->userManager
2809
+            ->expects($this->once())
2810
+            ->method('get')
2811
+            ->with('UserToLookup')
2812
+            ->willReturn($targetUser);
2813
+        $this->groupManager
2814
+            ->expects($this->once())
2815
+            ->method('getUserGroupIds')
2816
+            ->with($targetUser)
2817
+            ->willReturn(['DummyValue']);
2818
+
2819
+        $this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2820
+    }
2821
+
2822
+    public function testGetUsersGroupsForAdminUser(): void {
2823
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2824
+        $loggedInUser
2825
+            ->expects($this->exactly(3))
2826
+            ->method('getUID')
2827
+            ->willReturn('admin');
2828
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2829
+        $targetUser
2830
+            ->expects($this->once())
2831
+            ->method('getUID')
2832
+            ->willReturn('UserToLookup');
2833
+        $this->userSession
2834
+            ->expects($this->once())
2835
+            ->method('getUser')
2836
+            ->willReturn($loggedInUser);
2837
+        $this->userManager
2838
+            ->expects($this->once())
2839
+            ->method('get')
2840
+            ->with('UserToLookup')
2841
+            ->willReturn($targetUser);
2842
+        $this->groupManager
2843
+            ->expects($this->once())
2844
+            ->method('getUserGroupIds')
2845
+            ->with($targetUser)
2846
+            ->willReturn(['DummyValue']);
2847
+        $this->groupManager
2848
+            ->expects($this->once())
2849
+            ->method('isAdmin')
2850
+            ->with('admin')
2851
+            ->willReturn(true);
2852
+
2853
+        $this->assertEquals(['groups' => ['DummyValue']], $this->api->getUsersGroups('UserToLookup')->getData());
2854
+    }
2855
+
2856
+    public function testGetUsersGroupsForSubAdminUserAndUserIsAccessible(): void {
2857
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2858
+        $loggedInUser
2859
+            ->expects($this->exactly(3))
2860
+            ->method('getUID')
2861
+            ->willReturn('subadmin');
2862
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2863
+        $targetUser
2864
+            ->expects($this->once())
2865
+            ->method('getUID')
2866
+            ->willReturn('UserToLookup');
2867
+        $this->userSession
2868
+            ->expects($this->once())
2869
+            ->method('getUser')
2870
+            ->willReturn($loggedInUser);
2871
+        $this->userManager
2872
+            ->expects($this->once())
2873
+            ->method('get')
2874
+            ->with('UserToLookup')
2875
+            ->willReturn($targetUser);
2876
+        $this->groupManager
2877
+            ->expects($this->once())
2878
+            ->method('isAdmin')
2879
+            ->with('subadmin')
2880
+            ->willReturn(false);
2881
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2882
+            ->disableOriginalConstructor()->getMock();
2883
+        $subAdminManager
2884
+            ->expects($this->once())
2885
+            ->method('isUserAccessible')
2886
+            ->with($loggedInUser, $targetUser)
2887
+            ->willReturn(true);
2888
+        $this->groupManager
2889
+            ->expects($this->once())
2890
+            ->method('getSubAdmin')
2891
+            ->willReturn($subAdminManager);
2892
+        $group1 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2893
+        $group1
2894
+            ->expects($this->any())
2895
+            ->method('getGID')
2896
+            ->willReturn('Group1');
2897
+        $group2 = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
2898
+        $group2
2899
+            ->expects($this->any())
2900
+            ->method('getGID')
2901
+            ->willReturn('Group2');
2902
+        $subAdminManager
2903
+            ->expects($this->once())
2904
+            ->method('getSubAdminsGroups')
2905
+            ->with($loggedInUser)
2906
+            ->willReturn([$group1, $group2]);
2907
+        $this->groupManager
2908
+            ->expects($this->any())
2909
+            ->method('getUserGroupIds')
2910
+            ->with($targetUser)
2911
+            ->willReturn(['Group1']);
2912
+
2913
+        $this->assertEquals(['groups' => ['Group1']], $this->api->getUsersGroups('UserToLookup')->getData());
2914
+    }
2915
+
2916
+
2917
+    public function testGetUsersGroupsForSubAdminUserAndUserIsInaccessible(): void {
2918
+        $this->expectException(OCSException::class);
2919
+        $this->expectExceptionCode(998);
2920
+
2921
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2922
+        $loggedInUser
2923
+            ->expects($this->exactly(3))
2924
+            ->method('getUID')
2925
+            ->willReturn('subadmin');
2926
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
2927
+        $targetUser
2928
+            ->expects($this->once())
2929
+            ->method('getUID')
2930
+            ->willReturn('UserToLookup');
2931
+        $this->userSession
2932
+            ->expects($this->once())
2933
+            ->method('getUser')
2934
+            ->willReturn($loggedInUser);
2935
+        $this->userManager
2936
+            ->expects($this->once())
2937
+            ->method('get')
2938
+            ->with('UserToLookup')
2939
+            ->willReturn($targetUser);
2940
+        $this->groupManager
2941
+            ->expects($this->once())
2942
+            ->method('isAdmin')
2943
+            ->with('subadmin')
2944
+            ->willReturn(false);
2945
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
2946
+            ->disableOriginalConstructor()->getMock();
2947
+        $subAdminManager
2948
+            ->expects($this->once())
2949
+            ->method('isUserAccessible')
2950
+            ->with($loggedInUser, $targetUser)
2951
+            ->willReturn(false);
2952
+        $this->groupManager
2953
+            ->expects($this->once())
2954
+            ->method('getSubAdmin')
2955
+            ->willReturn($subAdminManager);
2956
+        $this->groupManager
2957
+            ->expects($this->any())
2958
+            ->method('getUserGroupIds')
2959
+            ->with($targetUser)
2960
+            ->willReturn(['Group1']);
2961
+
2962
+        $this->api->getUsersGroups('UserToLookup');
2963
+    }
2964
+
2965
+
2966
+    public function testAddToGroupWithTargetGroupNotExisting(): void {
2967
+        $this->expectException(OCSException::class);
2968
+        $this->expectExceptionCode(102);
2969
+
2970
+        $this->groupManager->expects($this->once())
2971
+            ->method('get')
2972
+            ->with('GroupToAddTo')
2973
+            ->willReturn(null);
2974
+
2975
+        $this->api->addToGroup('TargetUser', 'GroupToAddTo');
2976
+    }
2977
+
2978
+
2979
+    public function testAddToGroupWithNoGroupSpecified(): void {
2980
+        $this->expectException(OCSException::class);
2981
+        $this->expectExceptionCode(101);
2982
+
2983
+        $this->api->addToGroup('TargetUser');
2984
+    }
2985
+
2986
+
2987
+    public function testAddToGroupWithTargetUserNotExisting(): void {
2988
+        $this->expectException(OCSException::class);
2989
+        $this->expectExceptionCode(103);
2990
+
2991
+        $targetGroup = $this->createMock(IGroup::class);
2992
+        $this->groupManager->expects($this->once())
2993
+            ->method('get')
2994
+            ->with('GroupToAddTo')
2995
+            ->willReturn($targetGroup);
2996
+
2997
+        $this->api->addToGroup('TargetUser', 'GroupToAddTo');
2998
+    }
2999
+
3000
+
3001
+    public function testAddToGroupNoSubadmin(): void {
3002
+        $this->expectException(OCSException::class);
3003
+        $this->expectExceptionCode(104);
3004
+
3005
+        $targetUser = $this->createMock(IUser::class);
3006
+        $loggedInUser = $this->createMock(IUser::class);
3007
+        $loggedInUser->expects($this->exactly(2))
3008
+            ->method('getUID')
3009
+            ->willReturn('subadmin');
3010
+
3011
+        $targetGroup = $this->createMock(IGroup::class);
3012
+        $targetGroup->expects($this->never())
3013
+            ->method('addUser')
3014
+            ->with($targetUser);
3015
+
3016
+        $this->groupManager->expects($this->once())
3017
+            ->method('get')
3018
+            ->with('GroupToAddTo')
3019
+            ->willReturn($targetGroup);
3020
+
3021
+
3022
+        $subAdminManager = $this->createMock(SubAdmin::class);
3023
+        $subAdminManager->expects($this->once())
3024
+            ->method('isSubAdminOfGroup')
3025
+            ->with($loggedInUser, $targetGroup)
3026
+            ->willReturn(false);
3027
+
3028
+        $this->groupManager->expects($this->once())
3029
+            ->method('getSubAdmin')
3030
+            ->willReturn($subAdminManager);
3031
+        $this->groupManager->expects($this->once())
3032
+            ->method('isAdmin')
3033
+            ->with('subadmin')
3034
+            ->willReturn(false);
3035
+
3036
+        $this->userManager->expects($this->once())
3037
+            ->method('get')
3038
+            ->with('TargetUser')
3039
+            ->willReturn($targetUser);
3040
+
3041
+        $this->userSession->expects($this->once())
3042
+            ->method('getUser')
3043
+            ->willReturn($loggedInUser);
3044
+
3045
+        $this->api->addToGroup('TargetUser', 'GroupToAddTo');
3046
+    }
3047
+
3048
+    public function testAddToGroupSuccessAsSubadmin(): void {
3049
+        $targetUser = $this->createMock(IUser::class);
3050
+        $loggedInUser = $this->createMock(IUser::class);
3051
+        $loggedInUser->expects($this->exactly(2))
3052
+            ->method('getUID')
3053
+            ->willReturn('subadmin');
3054
+
3055
+        $targetGroup = $this->createMock(IGroup::class);
3056
+        $targetGroup->expects($this->once())
3057
+            ->method('addUser')
3058
+            ->with($targetUser);
3059
+
3060
+        $this->groupManager->expects($this->once())
3061
+            ->method('get')
3062
+            ->with('GroupToAddTo')
3063
+            ->willReturn($targetGroup);
3064 3064
 
3065
-
3066
-		$subAdminManager = $this->createMock(SubAdmin::class);
3067
-		$subAdminManager->expects($this->once())
3068
-			->method('isSubAdminOfGroup')
3069
-			->with($loggedInUser, $targetGroup)
3070
-			->willReturn(true);
3071
-
3072
-		$this->groupManager->expects($this->once())
3073
-			->method('getSubAdmin')
3074
-			->willReturn($subAdminManager);
3075
-		$this->groupManager->expects($this->once())
3076
-			->method('isAdmin')
3077
-			->with('subadmin')
3078
-			->willReturn(false);
3079
-
3080
-		$this->userManager->expects($this->once())
3081
-			->method('get')
3082
-			->with('TargetUser')
3083
-			->willReturn($targetUser);
3084
-
3085
-		$this->userSession->expects($this->once())
3086
-			->method('getUser')
3087
-			->willReturn($loggedInUser);
3088
-
3089
-		$this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3090
-	}
3091
-
3092
-	public function testAddToGroupSuccessAsAdmin(): void {
3093
-		$targetUser = $this->createMock(IUser::class);
3094
-		$loggedInUser = $this->createMock(IUser::class);
3095
-		$loggedInUser->expects($this->exactly(2))
3096
-			->method('getUID')
3097
-			->willReturn('admin');
3098
-
3099
-		$targetGroup = $this->createMock(IGroup::class);
3100
-		$targetGroup->expects($this->once())
3101
-			->method('addUser')
3102
-			->with($targetUser);
3103
-
3104
-		$this->groupManager->expects($this->once())
3105
-			->method('get')
3106
-			->with('GroupToAddTo')
3107
-			->willReturn($targetGroup);
3108
-
3109
-
3110
-		$subAdminManager = $this->createMock(SubAdmin::class);
3111
-		$subAdminManager->expects($this->never())
3112
-			->method('isSubAdminOfGroup');
3113
-
3114
-		$this->groupManager->expects($this->once())
3115
-			->method('getSubAdmin')
3116
-			->willReturn($subAdminManager);
3117
-		$this->groupManager->expects($this->once())
3118
-			->method('isAdmin')
3119
-			->with('admin')
3120
-			->willReturn(true);
3121
-
3122
-		$this->userManager->expects($this->once())
3123
-			->method('get')
3124
-			->with('TargetUser')
3125
-			->willReturn($targetUser);
3126
-
3127
-		$this->userSession->expects($this->once())
3128
-			->method('getUser')
3129
-			->willReturn($loggedInUser);
3130
-
3131
-		$this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3132
-	}
3133
-
3134
-
3135
-	public function testRemoveFromGroupWithNoTargetGroup(): void {
3136
-		$this->expectException(OCSException::class);
3137
-		$this->expectExceptionCode(101);
3138
-
3139
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3140
-		$this->userSession
3141
-			->expects($this->once())
3142
-			->method('getUser')
3143
-			->willReturn($loggedInUser);
3144
-
3145
-		$this->api->removeFromGroup('TargetUser', '');
3146
-	}
3147
-
3148
-
3149
-	public function testRemoveFromGroupWithEmptyTargetGroup(): void {
3150
-		$this->expectException(OCSException::class);
3151
-		$this->expectExceptionCode(101);
3152
-
3153
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3154
-		$this->userSession
3155
-			->expects($this->once())
3156
-			->method('getUser')
3157
-			->willReturn($loggedInUser);
3158
-
3159
-		$this->api->removeFromGroup('TargetUser', '');
3160
-	}
3161
-
3162
-
3163
-	public function testRemoveFromGroupWithNotExistingTargetGroup(): void {
3164
-		$this->expectException(OCSException::class);
3165
-		$this->expectExceptionCode(102);
3166
-
3167
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3168
-		$this->userSession
3169
-			->expects($this->once())
3170
-			->method('getUser')
3171
-			->willReturn($loggedInUser);
3172
-		$this->groupManager
3173
-			->expects($this->once())
3174
-			->method('get')
3175
-			->with('TargetGroup')
3176
-			->willReturn(null);
3177
-
3178
-		$this->api->removeFromGroup('TargetUser', 'TargetGroup');
3179
-	}
3180
-
3181
-
3182
-	public function testRemoveFromGroupWithNotExistingTargetUser(): void {
3183
-		$this->expectException(OCSException::class);
3184
-		$this->expectExceptionCode(103);
3185
-
3186
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3187
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3188
-		$this->userSession
3189
-			->expects($this->once())
3190
-			->method('getUser')
3191
-			->willReturn($loggedInUser);
3192
-		$this->groupManager
3193
-			->expects($this->once())
3194
-			->method('get')
3195
-			->with('TargetGroup')
3196
-			->willReturn($targetGroup);
3197
-		$this->userManager
3198
-			->expects($this->once())
3199
-			->method('get')
3200
-			->with('TargetUser')
3201
-			->willReturn(null);
3202
-
3203
-		$this->api->removeFromGroup('TargetUser', 'TargetGroup');
3204
-	}
3205
-
3206
-
3207
-	public function testRemoveFromGroupWithoutPermission(): void {
3208
-		$this->expectException(OCSException::class);
3209
-		$this->expectExceptionCode(104);
3210
-
3211
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3212
-		$loggedInUser
3213
-			->expects($this->exactly(2))
3214
-			->method('getUID')
3215
-			->willReturn('unauthorizedUser');
3216
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3217
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3218
-		$this->userSession
3219
-			->expects($this->once())
3220
-			->method('getUser')
3221
-			->willReturn($loggedInUser);
3222
-		$this->groupManager
3223
-			->expects($this->once())
3224
-			->method('get')
3225
-			->with('TargetGroup')
3226
-			->willReturn($targetGroup);
3227
-		$this->userManager
3228
-			->expects($this->once())
3229
-			->method('get')
3230
-			->with('TargetUser')
3231
-			->willReturn($targetUser);
3232
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3233
-			->disableOriginalConstructor()->getMock();
3234
-		$this->groupManager
3235
-			->expects($this->once())
3236
-			->method('getSubAdmin')
3237
-			->willReturn($subAdminManager);
3238
-		$this->groupManager
3239
-			->expects($this->once())
3240
-			->method('isAdmin')
3241
-			->with('unauthorizedUser')
3242
-			->willReturn(false);
3243
-
3244
-		$this->api->removeFromGroup('TargetUser', 'TargetGroup');
3245
-	}
3246
-
3247
-
3248
-	public function testRemoveFromGroupAsAdminFromAdmin(): void {
3249
-		$this->expectException(OCSException::class);
3250
-		$this->expectExceptionMessage('Cannot remove yourself from the admin group');
3251
-		$this->expectExceptionCode(105);
3252
-
3253
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3254
-		$loggedInUser
3255
-			->expects($this->any())
3256
-			->method('getUID')
3257
-			->willReturn('admin');
3258
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3259
-		$targetUser
3260
-			->expects($this->once())
3261
-			->method('getUID')
3262
-			->willReturn('admin');
3263
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3264
-		$targetGroup
3265
-			->expects($this->once())
3266
-			->method('getGID')
3267
-			->willReturn('admin');
3268
-		$this->userSession
3269
-			->expects($this->once())
3270
-			->method('getUser')
3271
-			->willReturn($loggedInUser);
3272
-		$this->groupManager
3273
-			->expects($this->once())
3274
-			->method('get')
3275
-			->with('admin')
3276
-			->willReturn($targetGroup);
3277
-		$this->userManager
3278
-			->expects($this->once())
3279
-			->method('get')
3280
-			->with('Admin')
3281
-			->willReturn($targetUser);
3282
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3283
-			->disableOriginalConstructor()->getMock();
3284
-		$this->groupManager
3285
-			->expects($this->once())
3286
-			->method('getSubAdmin')
3287
-			->willReturn($subAdminManager);
3288
-		$this->groupManager
3289
-			->expects($this->any())
3290
-			->method('isAdmin')
3291
-			->with('admin')
3292
-			->willReturn(true);
3293
-
3294
-		$this->api->removeFromGroup('Admin', 'admin');
3295
-	}
3296
-
3297
-
3298
-	public function testRemoveFromGroupAsSubAdminFromSubAdmin(): void {
3299
-		$this->expectException(OCSException::class);
3300
-		$this->expectExceptionMessage('Cannot remove yourself from this group as you are a sub-admin');
3301
-		$this->expectExceptionCode(105);
3302
-
3303
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3304
-		$loggedInUser
3305
-			->expects($this->any())
3306
-			->method('getUID')
3307
-			->willReturn('subadmin');
3308
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3309
-		$targetUser
3310
-			->expects($this->once())
3311
-			->method('getUID')
3312
-			->willReturn('subadmin');
3313
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3314
-		$targetGroup
3315
-			->expects($this->any())
3316
-			->method('getGID')
3317
-			->willReturn('subadmin');
3318
-		$this->userSession
3319
-			->expects($this->once())
3320
-			->method('getUser')
3321
-			->willReturn($loggedInUser);
3322
-		$this->groupManager
3323
-			->expects($this->once())
3324
-			->method('get')
3325
-			->with('subadmin')
3326
-			->willReturn($targetGroup);
3327
-		$this->userManager
3328
-			->expects($this->once())
3329
-			->method('get')
3330
-			->with('SubAdmin')
3331
-			->willReturn($targetUser);
3332
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3333
-			->disableOriginalConstructor()->getMock();
3334
-		$subAdminManager
3335
-			->expects($this->once())
3336
-			->method('isSubAdminOfGroup')
3337
-			->with($loggedInUser, $targetGroup)
3338
-			->willReturn(true);
3339
-		$this->groupManager
3340
-			->expects($this->once())
3341
-			->method('getSubAdmin')
3342
-			->willReturn($subAdminManager);
3343
-		$this->groupManager
3344
-			->expects($this->any())
3345
-			->method('isAdmin')
3346
-			->with('subadmin')
3347
-			->willReturn(false);
3348
-
3349
-		$this->api->removeFromGroup('SubAdmin', 'subadmin');
3350
-	}
3351
-
3352
-
3353
-	public function testRemoveFromGroupAsSubAdminFromLastSubAdminGroup(): void {
3354
-		$this->expectException(OCSException::class);
3355
-		$this->expectExceptionMessage('Not viable to remove user from the last group you are sub-admin of');
3356
-		$this->expectExceptionCode(105);
3357
-
3358
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3359
-		$loggedInUser
3360
-			->expects($this->any())
3361
-			->method('getUID')
3362
-			->willReturn('subadmin');
3363
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3364
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3365
-		$targetGroup
3366
-			->expects($this->any())
3367
-			->method('getGID')
3368
-			->willReturn('subadmin');
3369
-		$this->userSession
3370
-			->expects($this->once())
3371
-			->method('getUser')
3372
-			->willReturn($loggedInUser);
3373
-		$this->groupManager
3374
-			->expects($this->once())
3375
-			->method('get')
3376
-			->with('subadmin')
3377
-			->willReturn($targetGroup);
3378
-		$this->userManager
3379
-			->expects($this->once())
3380
-			->method('get')
3381
-			->with('AnotherUser')
3382
-			->willReturn($targetUser);
3383
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3384
-			->disableOriginalConstructor()->getMock();
3385
-		$subAdminManager
3386
-			->expects($this->once())
3387
-			->method('isSubAdminOfGroup')
3388
-			->with($loggedInUser, $targetGroup)
3389
-			->willReturn(true);
3390
-		$this->groupManager
3391
-			->expects($this->once())
3392
-			->method('getSubAdmin')
3393
-			->willReturn($subAdminManager);
3394
-		$subAdminManager
3395
-			->expects($this->once())
3396
-			->method('getSubAdminsGroups')
3397
-			->with($loggedInUser)
3398
-			->willReturn([$targetGroup]);
3399
-
3400
-		$this->groupManager
3401
-			->expects($this->any())
3402
-			->method('isAdmin')
3403
-			->with('subadmin')
3404
-			->willReturn(false);
3405
-		$this->groupManager
3406
-			->expects($this->once())
3407
-			->method('getUserGroupIds')
3408
-			->with($targetUser)
3409
-			->willReturn(['subadmin', 'other group']);
3410
-
3411
-		$this->api->removeFromGroup('AnotherUser', 'subadmin');
3412
-	}
3413
-
3414
-	public function testRemoveFromGroupSuccessful(): void {
3415
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3416
-		$loggedInUser
3417
-			->expects($this->any())
3418
-			->method('getUID')
3419
-			->willReturn('admin');
3420
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3421
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3422
-		$this->userSession
3423
-			->expects($this->once())
3424
-			->method('getUser')
3425
-			->willReturn($loggedInUser);
3426
-		$this->groupManager
3427
-			->expects($this->once())
3428
-			->method('get')
3429
-			->with('admin')
3430
-			->willReturn($targetGroup);
3431
-		$this->userManager
3432
-			->expects($this->once())
3433
-			->method('get')
3434
-			->with('AnotherUser')
3435
-			->willReturn($targetUser);
3436
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3437
-			->disableOriginalConstructor()->getMock();
3438
-		$this->groupManager
3439
-			->expects($this->once())
3440
-			->method('getSubAdmin')
3441
-			->willReturn($subAdminManager);
3442
-		$this->groupManager
3443
-			->expects($this->any())
3444
-			->method('isAdmin')
3445
-			->with('admin')
3446
-			->willReturn(true);
3447
-		$targetGroup
3448
-			->expects($this->once())
3449
-			->method('removeUser')
3450
-			->with($targetUser);
3451
-
3452
-		$this->assertEquals([], $this->api->removeFromGroup('AnotherUser', 'admin')->getData());
3453
-	}
3454
-
3455
-
3456
-	public function testAddSubAdminWithNotExistingTargetUser(): void {
3457
-		$this->expectException(OCSException::class);
3458
-		$this->expectExceptionMessage('User does not exist');
3459
-		$this->expectExceptionCode(101);
3460
-
3461
-		$this->userManager
3462
-			->expects($this->once())
3463
-			->method('get')
3464
-			->with('NotExistingUser')
3465
-			->willReturn(null);
3466
-
3467
-		$this->api->addSubAdmin('NotExistingUser', '');
3468
-	}
3469
-
3470
-
3471
-	public function testAddSubAdminWithNotExistingTargetGroup(): void {
3472
-		$this->expectException(OCSException::class);
3473
-		$this->expectExceptionMessage('Group does not exist');
3474
-		$this->expectExceptionCode(102);
3475
-
3476
-
3477
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3478
-		$this->userManager
3479
-			->expects($this->once())
3480
-			->method('get')
3481
-			->with('ExistingUser')
3482
-			->willReturn($targetUser);
3483
-		$this->groupManager
3484
-			->expects($this->once())
3485
-			->method('get')
3486
-			->with('NotExistingGroup')
3487
-			->willReturn(null);
3488
-
3489
-		$this->api->addSubAdmin('ExistingUser', 'NotExistingGroup');
3490
-	}
3491
-
3492
-
3493
-	public function testAddSubAdminToAdminGroup(): void {
3494
-		$this->expectException(OCSException::class);
3495
-		$this->expectExceptionMessage('Cannot create sub-admins for admin group');
3496
-		$this->expectExceptionCode(103);
3497
-
3498
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3499
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3500
-		$targetGroup
3501
-			->expects($this->once())
3502
-			->method('getGID')
3503
-			->willReturn('admin');
3504
-		$this->userManager
3505
-			->expects($this->once())
3506
-			->method('get')
3507
-			->with('ExistingUser')
3508
-			->willReturn($targetUser);
3509
-		$this->groupManager
3510
-			->expects($this->once())
3511
-			->method('get')
3512
-			->with('ADmiN')
3513
-			->willReturn($targetGroup);
3514
-
3515
-		$this->api->addSubAdmin('ExistingUser', 'ADmiN');
3516
-	}
3517
-
3518
-	public function testAddSubAdminTwice(): void {
3519
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3520
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3521
-		$this->userManager
3522
-			->expects($this->once())
3523
-			->method('get')
3524
-			->with('ExistingUser')
3525
-			->willReturn($targetUser);
3526
-		$this->groupManager
3527
-			->expects($this->once())
3528
-			->method('get')
3529
-			->with('TargetGroup')
3530
-			->willReturn($targetGroup);
3531
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3532
-			->disableOriginalConstructor()->getMock();
3533
-		$subAdminManager
3534
-			->expects($this->once())
3535
-			->method('isSubAdminOfGroup')
3536
-			->with($targetUser, $targetGroup)
3537
-			->willReturn(true);
3538
-		$this->groupManager
3539
-			->expects($this->once())
3540
-			->method('getSubAdmin')
3541
-			->willReturn($subAdminManager);
3542
-
3543
-		$this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3544
-	}
3545
-
3546
-	public function testAddSubAdminSuccessful(): void {
3547
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3548
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3549
-		$this->userManager
3550
-			->expects($this->once())
3551
-			->method('get')
3552
-			->with('ExistingUser')
3553
-			->willReturn($targetUser);
3554
-		$this->groupManager
3555
-			->expects($this->once())
3556
-			->method('get')
3557
-			->with('TargetGroup')
3558
-			->willReturn($targetGroup);
3559
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3560
-			->disableOriginalConstructor()->getMock();
3561
-		$subAdminManager
3562
-			->expects($this->once())
3563
-			->method('isSubAdminOfGroup')
3564
-			->with($targetUser, $targetGroup)
3565
-			->willReturn(false);
3566
-		$subAdminManager
3567
-			->expects($this->once())
3568
-			->method('createSubAdmin')
3569
-			->with($targetUser, $targetGroup);
3570
-		$this->groupManager
3571
-			->expects($this->once())
3572
-			->method('getSubAdmin')
3573
-			->willReturn($subAdminManager);
3574
-
3575
-		$this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3576
-	}
3577
-
3578
-
3579
-	public function testRemoveSubAdminNotExistingTargetUser(): void {
3580
-		$this->expectException(OCSException::class);
3581
-		$this->expectExceptionMessage('User does not exist');
3582
-		$this->expectExceptionCode(101);
3583
-
3584
-		$this->userManager
3585
-			->expects($this->once())
3586
-			->method('get')
3587
-			->with('NotExistingUser')
3588
-			->willReturn(null);
3589
-
3590
-		$this->api->removeSubAdmin('NotExistingUser', 'GroupToDeleteFrom');
3591
-	}
3592
-
3593
-
3594
-	public function testRemoveSubAdminNotExistingTargetGroup(): void {
3595
-		$this->expectException(OCSException::class);
3596
-		$this->expectExceptionMessage('Group does not exist');
3597
-		$this->expectExceptionCode(101);
3598
-
3599
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3600
-		$this->userManager
3601
-			->expects($this->once())
3602
-			->method('get')
3603
-			->with('ExistingUser')
3604
-			->willReturn($targetUser);
3605
-		$this->groupManager
3606
-			->expects($this->once())
3607
-			->method('get')
3608
-			->with('GroupToDeleteFrom')
3609
-			->willReturn(null);
3610
-
3611
-		$this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3612
-	}
3613
-
3614
-
3615
-
3616
-	public function testRemoveSubAdminFromNotASubadmin(): void {
3617
-		$this->expectException(OCSException::class);
3618
-		$this->expectExceptionMessage('User is not a sub-admin of this group');
3619
-		$this->expectExceptionCode(102);
3620
-
3621
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3622
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3623
-		$this->userManager
3624
-			->expects($this->once())
3625
-			->method('get')
3626
-			->with('ExistingUser')
3627
-			->willReturn($targetUser);
3628
-		$this->groupManager
3629
-			->expects($this->once())
3630
-			->method('get')
3631
-			->with('GroupToDeleteFrom')
3632
-			->willReturn($targetGroup);
3633
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3634
-			->disableOriginalConstructor()->getMock();
3635
-		$subAdminManager
3636
-			->expects($this->once())
3637
-			->method('isSubAdminOfGroup')
3638
-			->with($targetUser, $targetGroup)
3639
-			->willReturn(false);
3640
-		$this->groupManager
3641
-			->expects($this->once())
3642
-			->method('getSubAdmin')
3643
-			->willReturn($subAdminManager);
3644
-
3645
-		$this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3646
-	}
3647
-
3648
-	public function testRemoveSubAdminSuccessful(): void {
3649
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3650
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3651
-		$this->userManager
3652
-			->expects($this->once())
3653
-			->method('get')
3654
-			->with('ExistingUser')
3655
-			->willReturn($targetUser);
3656
-		$this->groupManager
3657
-			->expects($this->once())
3658
-			->method('get')
3659
-			->with('GroupToDeleteFrom')
3660
-			->willReturn($targetGroup);
3661
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3662
-			->disableOriginalConstructor()->getMock();
3663
-		$subAdminManager
3664
-			->expects($this->once())
3665
-			->method('isSubAdminOfGroup')
3666
-			->with($targetUser, $targetGroup)
3667
-			->willReturn(true);
3668
-		$subAdminManager
3669
-			->expects($this->once())
3670
-			->method('deleteSubAdmin')
3671
-			->with($targetUser, $targetGroup);
3672
-		$this->groupManager
3673
-			->expects($this->once())
3674
-			->method('getSubAdmin')
3675
-			->willReturn($subAdminManager);
3676
-
3677
-		$this->assertEquals([], $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom')->getData());
3678
-	}
3679
-
3680
-
3681
-	public function testGetUserSubAdminGroupsNotExistingTargetUser(): void {
3682
-		$this->expectException(OCSException::class);
3683
-		$this->expectExceptionMessage('User does not exist');
3684
-		$this->expectExceptionCode(404);
3685
-
3686
-		$this->userManager
3687
-			->expects($this->once())
3688
-			->method('get')
3689
-			->with('RequestedUser')
3690
-			->willReturn(null);
3691
-
3692
-		$this->api->getUserSubAdminGroups('RequestedUser');
3693
-	}
3694
-
3695
-	public function testGetUserSubAdminGroupsWithGroups(): void {
3696
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3697
-		$targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3698
-		$targetGroup
3699
-			->expects($this->once())
3700
-			->method('getGID')
3701
-			->willReturn('TargetGroup');
3702
-		$this->userManager
3703
-			->expects($this->once())
3704
-			->method('get')
3705
-			->with('RequestedUser')
3706
-			->willReturn($targetUser);
3707
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3708
-			->disableOriginalConstructor()->getMock();
3709
-		$subAdminManager
3710
-			->expects($this->once())
3711
-			->method('getSubAdminsGroups')
3712
-			->with($targetUser)
3713
-			->willReturn([$targetGroup]);
3714
-		$this->groupManager
3715
-			->expects($this->once())
3716
-			->method('getSubAdmin')
3717
-			->willReturn($subAdminManager);
3718
-
3719
-		$this->assertEquals(['TargetGroup'], $this->api->getUserSubAdminGroups('RequestedUser')->getData());
3720
-	}
3721
-
3722
-	public function testEnableUser(): void {
3723
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3724
-		$targetUser->expects($this->once())
3725
-			->method('setEnabled')
3726
-			->with(true);
3727
-		$this->userManager
3728
-			->expects($this->once())
3729
-			->method('get')
3730
-			->with('RequestedUser')
3731
-			->willReturn($targetUser);
3732
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3733
-		$loggedInUser
3734
-			->expects($this->exactly(3))
3735
-			->method('getUID')
3736
-			->willReturn('admin');
3737
-		$this->userSession
3738
-			->expects($this->once())
3739
-			->method('getUser')
3740
-			->willReturn($loggedInUser);
3741
-		$this->groupManager
3742
-			->expects($this->once())
3743
-			->method('isAdmin')
3744
-			->willReturn(true);
3745
-
3746
-		$this->assertEquals([], $this->api->enableUser('RequestedUser')->getData());
3747
-	}
3748
-
3749
-	public function testDisableUser(): void {
3750
-		$targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3751
-		$targetUser->expects($this->once())
3752
-			->method('setEnabled')
3753
-			->with(false);
3754
-		$this->userManager
3755
-			->expects($this->once())
3756
-			->method('get')
3757
-			->with('RequestedUser')
3758
-			->willReturn($targetUser);
3759
-		$loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3760
-		$loggedInUser
3761
-			->expects($this->exactly(3))
3762
-			->method('getUID')
3763
-			->willReturn('admin');
3764
-		$this->userSession
3765
-			->expects($this->once())
3766
-			->method('getUser')
3767
-			->willReturn($loggedInUser);
3768
-		$this->groupManager
3769
-			->expects($this->once())
3770
-			->method('isAdmin')
3771
-			->willReturn(true);
3772
-
3773
-		$this->assertEquals([], $this->api->disableUser('RequestedUser')->getData());
3774
-	}
3775
-
3776
-	public function testGetCurrentUserLoggedIn(): void {
3777
-		$user = $this->createMock(IUser::class);
3778
-		$user->expects($this->once())->method('getUID')->willReturn('UID');
3779
-
3780
-		$this->userSession->expects($this->once())->method('getUser')
3781
-			->willReturn($user);
3782
-
3783
-		/** @var UsersController | MockObject $api */
3784
-		$api = $this->getMockBuilder(UsersController::class)
3785
-			->setConstructorArgs([
3786
-				'provisioning_api',
3787
-				$this->request,
3788
-				$this->userManager,
3789
-				$this->config,
3790
-				$this->groupManager,
3791
-				$this->userSession,
3792
-				$this->accountManager,
3793
-				$this->subAdminManager,
3794
-				$this->l10nFactory,
3795
-				$this->rootFolder,
3796
-				$this->urlGenerator,
3797
-				$this->logger,
3798
-				$this->newUserMailHelper,
3799
-				$this->secureRandom,
3800
-				$this->remoteWipe,
3801
-				$this->knownUserService,
3802
-				$this->eventDispatcher,
3803
-				$this->phoneNumberUtil,
3804
-				$this->appManager,
3805
-			])
3806
-			->onlyMethods(['getUserData'])
3807
-			->getMock();
3808
-
3809
-		$api->expects($this->once())->method('getUserData')->with('UID', true)
3810
-			->willReturn(
3811
-				[
3812
-					'id' => 'UID',
3813
-					'enabled' => 'true',
3814
-					'quota' => ['DummyValue'],
3815
-					'email' => '[email protected]',
3816
-					'displayname' => 'Demo User',
3817
-					'display-name' => 'Demo User',
3818
-					'phone' => 'phone',
3819
-					'address' => 'address',
3820
-					'website' => 'website',
3821
-					'twitter' => 'twitter',
3822
-					'fediverse' => 'fediverse',
3823
-					'organisation' => 'organisation',
3824
-					'role' => 'role',
3825
-					'headline' => 'headline',
3826
-					'biography' => 'biography',
3827
-					'profile_enabled' => '1',
3828
-					'pronouns' => 'they/them',
3829
-				]
3830
-			);
3831
-
3832
-		$expected = [
3833
-			'id' => 'UID',
3834
-			'enabled' => 'true',
3835
-			'quota' => ['DummyValue'],
3836
-			'email' => '[email protected]',
3837
-			'displayname' => 'Demo User',
3838
-			'display-name' => 'Demo User',
3839
-			'phone' => 'phone',
3840
-			'address' => 'address',
3841
-			'website' => 'website',
3842
-			'twitter' => 'twitter',
3843
-			'fediverse' => 'fediverse',
3844
-			'organisation' => 'organisation',
3845
-			'role' => 'role',
3846
-			'headline' => 'headline',
3847
-			'biography' => 'biography',
3848
-			'profile_enabled' => '1',
3849
-			'pronouns' => 'they/them',
3850
-		];
3851
-
3852
-		$this->assertSame($expected, $api->getCurrentUser()->getData());
3853
-	}
3854
-
3855
-
3856
-	public function testGetCurrentUserNotLoggedIn(): void {
3857
-		$this->expectException(OCSException::class);
3858
-
3859
-
3860
-		$this->userSession->expects($this->once())->method('getUser')
3861
-			->willReturn(null);
3862
-
3863
-		$this->api->getCurrentUser();
3864
-	}
3865
-
3866
-	public function testGetUser(): void {
3867
-		$loggedInUser = $this->createMock(IUser::class);
3868
-		$loggedInUser
3869
-			->method('getUID')
3870
-			->willReturn('currentuser');
3871
-		$this->userSession
3872
-			->method('getUser')
3873
-			->willReturn($loggedInUser);
3874
-
3875
-		/** @var UsersController | MockObject $api */
3876
-		$api = $this->getMockBuilder(UsersController::class)
3877
-			->setConstructorArgs([
3878
-				'provisioning_api',
3879
-				$this->request,
3880
-				$this->userManager,
3881
-				$this->config,
3882
-				$this->groupManager,
3883
-				$this->userSession,
3884
-				$this->accountManager,
3885
-				$this->subAdminManager,
3886
-				$this->l10nFactory,
3887
-				$this->rootFolder,
3888
-				$this->urlGenerator,
3889
-				$this->logger,
3890
-				$this->newUserMailHelper,
3891
-				$this->secureRandom,
3892
-				$this->remoteWipe,
3893
-				$this->knownUserService,
3894
-				$this->eventDispatcher,
3895
-				$this->phoneNumberUtil,
3896
-				$this->appManager,
3897
-			])
3898
-			->onlyMethods(['getUserData'])
3899
-			->getMock();
3900
-
3901
-		$expected = [
3902
-			'id' => 'UID',
3903
-			'enabled' => 'true',
3904
-			'quota' => ['DummyValue'],
3905
-			'email' => '[email protected]',
3906
-			'phone' => 'phone',
3907
-			'address' => 'address',
3908
-			'website' => 'website',
3909
-			'twitter' => 'twitter',
3910
-			'fediverse' => 'fediverse',
3911
-			'displayname' => 'Demo User',
3912
-			'display-name' => 'Demo User',
3913
-			'organisation' => 'organisation',
3914
-			'role' => 'role',
3915
-			'headline' => 'headline',
3916
-			'biography' => 'biography',
3917
-			'profile_enabled' => '1',
3918
-			'pronouns' => 'they/them',
3919
-		];
3920
-
3921
-		$api->expects($this->exactly(2))
3922
-			->method('getUserData')
3923
-			->willReturnMap([
3924
-				['uid', false, $expected],
3925
-				['currentuser', true, $expected],
3926
-			]);
3927
-
3928
-		$this->assertSame($expected, $api->getUser('uid')->getData());
3929
-
3930
-		$this->assertSame($expected, $api->getUser('currentuser')->getData());
3931
-	}
3932
-
3933
-
3934
-	public function testResendWelcomeMessageWithNotExistingTargetUser(): void {
3935
-		$this->expectException(OCSException::class);
3936
-		$this->expectExceptionCode(998);
3937
-
3938
-		$this->userManager
3939
-			->expects($this->once())
3940
-			->method('get')
3941
-			->with('NotExistingUser')
3942
-			->willReturn(null);
3943
-
3944
-		$this->api->resendWelcomeMessage('NotExistingUser');
3945
-	}
3946
-
3947
-
3948
-	public function testResendWelcomeMessageAsSubAdminAndUserIsNotAccessible(): void {
3949
-		$this->expectException(OCSException::class);
3950
-		$this->expectExceptionCode(998);
3951
-
3952
-		$loggedInUser = $this->getMockBuilder(IUser::class)
3953
-			->disableOriginalConstructor()
3954
-			->getMock();
3955
-		$loggedInUser
3956
-			->expects($this->exactly(2))
3957
-			->method('getUID')
3958
-			->willReturn('subadmin');
3959
-		$targetUser = $this->getMockBuilder(IUser::class)
3960
-			->disableOriginalConstructor()
3961
-			->getMock();
3962
-		$this->userSession
3963
-			->expects($this->once())
3964
-			->method('getUser')
3965
-			->willReturn($loggedInUser);
3966
-		$this->userManager
3967
-			->expects($this->once())
3968
-			->method('get')
3969
-			->with('UserToGet')
3970
-			->willReturn($targetUser);
3971
-		$this->groupManager
3972
-			->expects($this->once())
3973
-			->method('isAdmin')
3974
-			->with('subadmin')
3975
-			->willReturn(false);
3976
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3977
-			->disableOriginalConstructor()
3978
-			->getMock();
3979
-		$subAdminManager
3980
-			->expects($this->once())
3981
-			->method('isUserAccessible')
3982
-			->with($loggedInUser, $targetUser)
3983
-			->willReturn(false);
3984
-		$this->groupManager
3985
-			->expects($this->once())
3986
-			->method('getSubAdmin')
3987
-			->willReturn($subAdminManager);
3988
-
3989
-		$this->api->resendWelcomeMessage('UserToGet');
3990
-	}
3991
-
3992
-
3993
-	public function testResendWelcomeMessageNoEmail(): void {
3994
-		$this->expectException(OCSException::class);
3995
-		$this->expectExceptionMessage('Email address not available');
3996
-		$this->expectExceptionCode(101);
3997
-
3998
-		$loggedInUser = $this->getMockBuilder(IUser::class)
3999
-			->disableOriginalConstructor()
4000
-			->getMock();
4001
-		$targetUser = $this->getMockBuilder(IUser::class)
4002
-			->disableOriginalConstructor()
4003
-			->getMock();
4004
-		$this->userSession
4005
-			->expects($this->once())
4006
-			->method('getUser')
4007
-			->willReturn($loggedInUser);
4008
-		$this->userManager
4009
-			->expects($this->once())
4010
-			->method('get')
4011
-			->with('UserToGet')
4012
-			->willReturn($targetUser);
4013
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4014
-			->disableOriginalConstructor()
4015
-			->getMock();
4016
-		$subAdminManager
4017
-			->expects($this->once())
4018
-			->method('isUserAccessible')
4019
-			->with($loggedInUser, $targetUser)
4020
-			->willReturn(true);
4021
-		$this->groupManager
4022
-			->expects($this->once())
4023
-			->method('getSubAdmin')
4024
-			->willReturn($subAdminManager);
4025
-		$loggedInUser
4026
-			->expects($this->exactly(2))
4027
-			->method('getUID')
4028
-			->willReturn('logged-user-id');
4029
-		$targetUser
4030
-			->expects($this->once())
4031
-			->method('getEmailAddress')
4032
-			->willReturn('');
4033
-
4034
-		$this->api->resendWelcomeMessage('UserToGet');
4035
-	}
4036
-
4037
-
4038
-	public function testResendWelcomeMessageNullEmail(): void {
4039
-		$this->expectException(OCSException::class);
4040
-		$this->expectExceptionMessage('Email address not available');
4041
-		$this->expectExceptionCode(101);
4042
-
4043
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4044
-			->disableOriginalConstructor()
4045
-			->getMock();
4046
-		$targetUser = $this->getMockBuilder(IUser::class)
4047
-			->disableOriginalConstructor()
4048
-			->getMock();
4049
-		$this->userSession
4050
-			->expects($this->once())
4051
-			->method('getUser')
4052
-			->willReturn($loggedInUser);
4053
-		$this->userManager
4054
-			->expects($this->once())
4055
-			->method('get')
4056
-			->with('UserToGet')
4057
-			->willReturn($targetUser);
4058
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4059
-			->disableOriginalConstructor()
4060
-			->getMock();
4061
-		$subAdminManager
4062
-			->expects($this->once())
4063
-			->method('isUserAccessible')
4064
-			->with($loggedInUser, $targetUser)
4065
-			->willReturn(true);
4066
-		$this->groupManager
4067
-			->expects($this->once())
4068
-			->method('getSubAdmin')
4069
-			->willReturn($subAdminManager);
4070
-		$loggedInUser
4071
-			->expects($this->exactly(2))
4072
-			->method('getUID')
4073
-			->willReturn('logged-user-id');
4074
-		$targetUser
4075
-			->expects($this->once())
4076
-			->method('getEmailAddress')
4077
-			->willReturn(null);
4078
-
4079
-		$this->api->resendWelcomeMessage('UserToGet');
4080
-	}
4081
-
4082
-	public function testResendWelcomeMessageSuccess(): void {
4083
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4084
-			->disableOriginalConstructor()
4085
-			->getMock();
4086
-		$targetUser = $this->getMockBuilder(IUser::class)
4087
-			->disableOriginalConstructor()
4088
-			->getMock();
4089
-		$loggedInUser
4090
-			->method('getUID')
4091
-			->willReturn('logged-user-id');
4092
-		$targetUser
4093
-			->method('getUID')
4094
-			->willReturn('user-id');
4095
-		$this->userSession
4096
-			->expects($this->once())
4097
-			->method('getUser')
4098
-			->willReturn($loggedInUser);
4099
-		$this->userManager
4100
-			->expects($this->once())
4101
-			->method('get')
4102
-			->with('UserToGet')
4103
-			->willReturn($targetUser);
4104
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4105
-			->disableOriginalConstructor()
4106
-			->getMock();
4107
-		$subAdminManager
4108
-			->expects($this->once())
4109
-			->method('isUserAccessible')
4110
-			->with($loggedInUser, $targetUser)
4111
-			->willReturn(true);
4112
-		$this->groupManager
4113
-			->expects($this->once())
4114
-			->method('getSubAdmin')
4115
-			->willReturn($subAdminManager);
4116
-		$targetUser
4117
-			->expects($this->once())
4118
-			->method('getEmailAddress')
4119
-			->willReturn('[email protected]');
4120
-		$emailTemplate = $this->createMock(IEMailTemplate::class);
4121
-		$this->newUserMailHelper
4122
-			->expects($this->once())
4123
-			->method('generateTemplate')
4124
-			->willReturn($emailTemplate);
4125
-		$this->newUserMailHelper
4126
-			->expects($this->once())
4127
-			->method('sendMail')
4128
-			->with($targetUser, $emailTemplate);
4129
-
4130
-		$this->api->resendWelcomeMessage('UserToGet');
4131
-	}
4132
-
4133
-	public function testResendWelcomeMessageSuccessWithFallbackLanguage(): void {
4134
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4135
-			->disableOriginalConstructor()
4136
-			->getMock();
4137
-		$targetUser = $this->getMockBuilder(IUser::class)
4138
-			->disableOriginalConstructor()
4139
-			->getMock();
4140
-		$loggedInUser
4141
-			->method('getUID')
4142
-			->willReturn('logged-user-id');
4143
-		$targetUser
4144
-			->method('getUID')
4145
-			->willReturn('user-id');
4146
-		$this->userSession
4147
-			->expects($this->once())
4148
-			->method('getUser')
4149
-			->willReturn($loggedInUser);
4150
-		$this->userManager
4151
-			->expects($this->once())
4152
-			->method('get')
4153
-			->with('UserToGet')
4154
-			->willReturn($targetUser);
4155
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4156
-			->disableOriginalConstructor()
4157
-			->getMock();
4158
-		$subAdminManager
4159
-			->expects($this->once())
4160
-			->method('isUserAccessible')
4161
-			->with($loggedInUser, $targetUser)
4162
-			->willReturn(true);
4163
-		$this->groupManager
4164
-			->expects($this->once())
4165
-			->method('getSubAdmin')
4166
-			->willReturn($subAdminManager);
4167
-		$targetUser
4168
-			->expects($this->once())
4169
-			->method('getEmailAddress')
4170
-			->willReturn('[email protected]');
4171
-		$emailTemplate = $this->createMock(IEMailTemplate::class);
4172
-		$this->newUserMailHelper
4173
-			->expects($this->once())
4174
-			->method('generateTemplate')
4175
-			->willReturn($emailTemplate);
4176
-		$this->newUserMailHelper
4177
-			->expects($this->once())
4178
-			->method('sendMail')
4179
-			->with($targetUser, $emailTemplate);
4180
-
4181
-		$this->api->resendWelcomeMessage('UserToGet');
4182
-	}
4183
-
4184
-
4185
-	public function testResendWelcomeMessageFailed(): void {
4186
-		$this->expectException(OCSException::class);
4187
-		$this->expectExceptionMessage('Sending email failed');
4188
-		$this->expectExceptionCode(102);
4189
-
4190
-		$loggedInUser = $this->getMockBuilder(IUser::class)
4191
-			->disableOriginalConstructor()
4192
-			->getMock();
4193
-		$targetUser = $this->getMockBuilder(IUser::class)
4194
-			->disableOriginalConstructor()
4195
-			->getMock();
4196
-		$loggedInUser
4197
-			->expects($this->exactly(2))
4198
-			->method('getUID')
4199
-			->willReturn('logged-user-id');
4200
-		$targetUser
4201
-			->method('getUID')
4202
-			->willReturn('user-id');
4203
-		$this->userSession
4204
-			->expects($this->once())
4205
-			->method('getUser')
4206
-			->willReturn($loggedInUser);
4207
-		$this->userManager
4208
-			->expects($this->once())
4209
-			->method('get')
4210
-			->with('UserToGet')
4211
-			->willReturn($targetUser);
4212
-		$subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4213
-			->disableOriginalConstructor()
4214
-			->getMock();
4215
-		$subAdminManager
4216
-			->expects($this->once())
4217
-			->method('isUserAccessible')
4218
-			->with($loggedInUser, $targetUser)
4219
-			->willReturn(true);
4220
-		$this->groupManager
4221
-			->expects($this->once())
4222
-			->method('getSubAdmin')
4223
-			->willReturn($subAdminManager);
4224
-		$targetUser
4225
-			->expects($this->once())
4226
-			->method('getEmailAddress')
4227
-			->willReturn('[email protected]');
4228
-		$emailTemplate = $this->createMock(IEMailTemplate::class);
4229
-		$this->newUserMailHelper
4230
-			->expects($this->once())
4231
-			->method('generateTemplate')
4232
-			->willReturn($emailTemplate);
4233
-		$this->newUserMailHelper
4234
-			->expects($this->once())
4235
-			->method('sendMail')
4236
-			->with($targetUser, $emailTemplate)
4237
-			->willThrowException(new \Exception());
4238
-
4239
-		$this->api->resendWelcomeMessage('UserToGet');
4240
-	}
4241
-
4242
-
4243
-	public static function dataGetEditableFields(): array {
4244
-		return [
4245
-			[false, true, ISetDisplayNameBackend::class, [
4246
-				IAccountManager::PROPERTY_EMAIL,
4247
-				IAccountManager::COLLECTION_EMAIL,
4248
-				IAccountManager::PROPERTY_PHONE,
4249
-				IAccountManager::PROPERTY_ADDRESS,
4250
-				IAccountManager::PROPERTY_WEBSITE,
4251
-				IAccountManager::PROPERTY_TWITTER,
4252
-				IAccountManager::PROPERTY_FEDIVERSE,
4253
-				IAccountManager::PROPERTY_ORGANISATION,
4254
-				IAccountManager::PROPERTY_ROLE,
4255
-				IAccountManager::PROPERTY_HEADLINE,
4256
-				IAccountManager::PROPERTY_BIOGRAPHY,
4257
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4258
-				IAccountManager::PROPERTY_PRONOUNS,
4259
-			]],
4260
-			[true, false, ISetDisplayNameBackend::class, [
4261
-				IAccountManager::PROPERTY_DISPLAYNAME,
4262
-				IAccountManager::COLLECTION_EMAIL,
4263
-				IAccountManager::PROPERTY_PHONE,
4264
-				IAccountManager::PROPERTY_ADDRESS,
4265
-				IAccountManager::PROPERTY_WEBSITE,
4266
-				IAccountManager::PROPERTY_TWITTER,
4267
-				IAccountManager::PROPERTY_FEDIVERSE,
4268
-				IAccountManager::PROPERTY_ORGANISATION,
4269
-				IAccountManager::PROPERTY_ROLE,
4270
-				IAccountManager::PROPERTY_HEADLINE,
4271
-				IAccountManager::PROPERTY_BIOGRAPHY,
4272
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4273
-				IAccountManager::PROPERTY_PRONOUNS,
4274
-			]],
4275
-			[true, true, ISetDisplayNameBackend::class, [
4276
-				IAccountManager::PROPERTY_DISPLAYNAME,
4277
-				IAccountManager::PROPERTY_EMAIL,
4278
-				IAccountManager::COLLECTION_EMAIL,
4279
-				IAccountManager::PROPERTY_PHONE,
4280
-				IAccountManager::PROPERTY_ADDRESS,
4281
-				IAccountManager::PROPERTY_WEBSITE,
4282
-				IAccountManager::PROPERTY_TWITTER,
4283
-				IAccountManager::PROPERTY_FEDIVERSE,
4284
-				IAccountManager::PROPERTY_ORGANISATION,
4285
-				IAccountManager::PROPERTY_ROLE,
4286
-				IAccountManager::PROPERTY_HEADLINE,
4287
-				IAccountManager::PROPERTY_BIOGRAPHY,
4288
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4289
-				IAccountManager::PROPERTY_PRONOUNS,
4290
-			]],
4291
-			[false, false, ISetDisplayNameBackend::class, [
4292
-				IAccountManager::COLLECTION_EMAIL,
4293
-				IAccountManager::PROPERTY_PHONE,
4294
-				IAccountManager::PROPERTY_ADDRESS,
4295
-				IAccountManager::PROPERTY_WEBSITE,
4296
-				IAccountManager::PROPERTY_TWITTER,
4297
-				IAccountManager::PROPERTY_FEDIVERSE,
4298
-				IAccountManager::PROPERTY_ORGANISATION,
4299
-				IAccountManager::PROPERTY_ROLE,
4300
-				IAccountManager::PROPERTY_HEADLINE,
4301
-				IAccountManager::PROPERTY_BIOGRAPHY,
4302
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4303
-				IAccountManager::PROPERTY_PRONOUNS,
4304
-			]],
4305
-			[false, true, UserInterface::class, [
4306
-				IAccountManager::PROPERTY_EMAIL,
4307
-				IAccountManager::COLLECTION_EMAIL,
4308
-				IAccountManager::PROPERTY_PHONE,
4309
-				IAccountManager::PROPERTY_ADDRESS,
4310
-				IAccountManager::PROPERTY_WEBSITE,
4311
-				IAccountManager::PROPERTY_TWITTER,
4312
-				IAccountManager::PROPERTY_FEDIVERSE,
4313
-				IAccountManager::PROPERTY_ORGANISATION,
4314
-				IAccountManager::PROPERTY_ROLE,
4315
-				IAccountManager::PROPERTY_HEADLINE,
4316
-				IAccountManager::PROPERTY_BIOGRAPHY,
4317
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4318
-				IAccountManager::PROPERTY_PRONOUNS,
4319
-			]],
4320
-			[true, false, UserInterface::class, [
4321
-				IAccountManager::COLLECTION_EMAIL,
4322
-				IAccountManager::PROPERTY_PHONE,
4323
-				IAccountManager::PROPERTY_ADDRESS,
4324
-				IAccountManager::PROPERTY_WEBSITE,
4325
-				IAccountManager::PROPERTY_TWITTER,
4326
-				IAccountManager::PROPERTY_FEDIVERSE,
4327
-				IAccountManager::PROPERTY_ORGANISATION,
4328
-				IAccountManager::PROPERTY_ROLE,
4329
-				IAccountManager::PROPERTY_HEADLINE,
4330
-				IAccountManager::PROPERTY_BIOGRAPHY,
4331
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4332
-				IAccountManager::PROPERTY_PRONOUNS,
4333
-			]],
4334
-			[true, true, UserInterface::class, [
4335
-				IAccountManager::PROPERTY_EMAIL,
4336
-				IAccountManager::COLLECTION_EMAIL,
4337
-				IAccountManager::PROPERTY_PHONE,
4338
-				IAccountManager::PROPERTY_ADDRESS,
4339
-				IAccountManager::PROPERTY_WEBSITE,
4340
-				IAccountManager::PROPERTY_TWITTER,
4341
-				IAccountManager::PROPERTY_FEDIVERSE,
4342
-				IAccountManager::PROPERTY_ORGANISATION,
4343
-				IAccountManager::PROPERTY_ROLE,
4344
-				IAccountManager::PROPERTY_HEADLINE,
4345
-				IAccountManager::PROPERTY_BIOGRAPHY,
4346
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4347
-				IAccountManager::PROPERTY_PRONOUNS,
4348
-			]],
4349
-			[false, false, UserInterface::class, [
4350
-				IAccountManager::COLLECTION_EMAIL,
4351
-				IAccountManager::PROPERTY_PHONE,
4352
-				IAccountManager::PROPERTY_ADDRESS,
4353
-				IAccountManager::PROPERTY_WEBSITE,
4354
-				IAccountManager::PROPERTY_TWITTER,
4355
-				IAccountManager::PROPERTY_FEDIVERSE,
4356
-				IAccountManager::PROPERTY_ORGANISATION,
4357
-				IAccountManager::PROPERTY_ROLE,
4358
-				IAccountManager::PROPERTY_HEADLINE,
4359
-				IAccountManager::PROPERTY_BIOGRAPHY,
4360
-				IAccountManager::PROPERTY_PROFILE_ENABLED,
4361
-				IAccountManager::PROPERTY_PRONOUNS,
4362
-			]],
4363
-		];
4364
-	}
4365
-
4366
-	/**
4367
-	 * @dataProvider dataGetEditableFields
4368
-	 */
4369
-	public function testGetEditableFields(bool $allowedToChangeDisplayName, bool $allowedToChangeEmail, string $userBackend, array $expected): void {
4370
-		$this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => match ($key) {
4371
-			'allow_user_to_change_display_name' => $allowedToChangeDisplayName,
4372
-			'allow_user_to_change_email' => $allowedToChangeEmail,
4373
-			default => throw new RuntimeException('Unexpected system config key: ' . $key),
4374
-		});
4375
-
4376
-		$user = $this->createMock(IUser::class);
4377
-		$this->userSession->method('getUser')
4378
-			->willReturn($user);
4379
-
4380
-		$backend = $this->createMock($userBackend);
4381
-
4382
-		$user->method('getUID')
4383
-			->willReturn('userId');
4384
-		$user->method('getBackend')
4385
-			->willReturn($backend);
4386
-
4387
-		$expectedResp = new DataResponse($expected);
4388
-		$this->assertEquals($expectedResp, $this->api->getEditableFields('userId'));
4389
-	}
4390
-
4391
-	private function mockAccount($targetUser, $accountProperties) {
4392
-		$mockedProperties = [];
4393
-
4394
-		foreach ($accountProperties as $propertyName => $data) {
4395
-			$mockedProperty = $this->createMock(IAccountProperty::class);
4396
-			$mockedProperty->method('getValue')->willReturn($data['value'] ?? '');
4397
-			$mockedProperty->method('getScope')->willReturn($data['scope'] ?? '');
4398
-			$mockedProperties[] = [$propertyName, $mockedProperty];
4399
-		}
4400
-
4401
-		$account = $this->createMock(IAccount::class);
4402
-		$account->method('getProperty')
4403
-			->will($this->returnValueMap($mockedProperties));
4404
-
4405
-		$this->accountManager->expects($this->any())->method('getAccount')
4406
-			->with($targetUser)
4407
-			->willReturn($account);
4408
-	}
3065
+
3066
+        $subAdminManager = $this->createMock(SubAdmin::class);
3067
+        $subAdminManager->expects($this->once())
3068
+            ->method('isSubAdminOfGroup')
3069
+            ->with($loggedInUser, $targetGroup)
3070
+            ->willReturn(true);
3071
+
3072
+        $this->groupManager->expects($this->once())
3073
+            ->method('getSubAdmin')
3074
+            ->willReturn($subAdminManager);
3075
+        $this->groupManager->expects($this->once())
3076
+            ->method('isAdmin')
3077
+            ->with('subadmin')
3078
+            ->willReturn(false);
3079
+
3080
+        $this->userManager->expects($this->once())
3081
+            ->method('get')
3082
+            ->with('TargetUser')
3083
+            ->willReturn($targetUser);
3084
+
3085
+        $this->userSession->expects($this->once())
3086
+            ->method('getUser')
3087
+            ->willReturn($loggedInUser);
3088
+
3089
+        $this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3090
+    }
3091
+
3092
+    public function testAddToGroupSuccessAsAdmin(): void {
3093
+        $targetUser = $this->createMock(IUser::class);
3094
+        $loggedInUser = $this->createMock(IUser::class);
3095
+        $loggedInUser->expects($this->exactly(2))
3096
+            ->method('getUID')
3097
+            ->willReturn('admin');
3098
+
3099
+        $targetGroup = $this->createMock(IGroup::class);
3100
+        $targetGroup->expects($this->once())
3101
+            ->method('addUser')
3102
+            ->with($targetUser);
3103
+
3104
+        $this->groupManager->expects($this->once())
3105
+            ->method('get')
3106
+            ->with('GroupToAddTo')
3107
+            ->willReturn($targetGroup);
3108
+
3109
+
3110
+        $subAdminManager = $this->createMock(SubAdmin::class);
3111
+        $subAdminManager->expects($this->never())
3112
+            ->method('isSubAdminOfGroup');
3113
+
3114
+        $this->groupManager->expects($this->once())
3115
+            ->method('getSubAdmin')
3116
+            ->willReturn($subAdminManager);
3117
+        $this->groupManager->expects($this->once())
3118
+            ->method('isAdmin')
3119
+            ->with('admin')
3120
+            ->willReturn(true);
3121
+
3122
+        $this->userManager->expects($this->once())
3123
+            ->method('get')
3124
+            ->with('TargetUser')
3125
+            ->willReturn($targetUser);
3126
+
3127
+        $this->userSession->expects($this->once())
3128
+            ->method('getUser')
3129
+            ->willReturn($loggedInUser);
3130
+
3131
+        $this->assertEquals(new DataResponse(), $this->api->addToGroup('TargetUser', 'GroupToAddTo'));
3132
+    }
3133
+
3134
+
3135
+    public function testRemoveFromGroupWithNoTargetGroup(): void {
3136
+        $this->expectException(OCSException::class);
3137
+        $this->expectExceptionCode(101);
3138
+
3139
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3140
+        $this->userSession
3141
+            ->expects($this->once())
3142
+            ->method('getUser')
3143
+            ->willReturn($loggedInUser);
3144
+
3145
+        $this->api->removeFromGroup('TargetUser', '');
3146
+    }
3147
+
3148
+
3149
+    public function testRemoveFromGroupWithEmptyTargetGroup(): void {
3150
+        $this->expectException(OCSException::class);
3151
+        $this->expectExceptionCode(101);
3152
+
3153
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3154
+        $this->userSession
3155
+            ->expects($this->once())
3156
+            ->method('getUser')
3157
+            ->willReturn($loggedInUser);
3158
+
3159
+        $this->api->removeFromGroup('TargetUser', '');
3160
+    }
3161
+
3162
+
3163
+    public function testRemoveFromGroupWithNotExistingTargetGroup(): void {
3164
+        $this->expectException(OCSException::class);
3165
+        $this->expectExceptionCode(102);
3166
+
3167
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3168
+        $this->userSession
3169
+            ->expects($this->once())
3170
+            ->method('getUser')
3171
+            ->willReturn($loggedInUser);
3172
+        $this->groupManager
3173
+            ->expects($this->once())
3174
+            ->method('get')
3175
+            ->with('TargetGroup')
3176
+            ->willReturn(null);
3177
+
3178
+        $this->api->removeFromGroup('TargetUser', 'TargetGroup');
3179
+    }
3180
+
3181
+
3182
+    public function testRemoveFromGroupWithNotExistingTargetUser(): void {
3183
+        $this->expectException(OCSException::class);
3184
+        $this->expectExceptionCode(103);
3185
+
3186
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3187
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3188
+        $this->userSession
3189
+            ->expects($this->once())
3190
+            ->method('getUser')
3191
+            ->willReturn($loggedInUser);
3192
+        $this->groupManager
3193
+            ->expects($this->once())
3194
+            ->method('get')
3195
+            ->with('TargetGroup')
3196
+            ->willReturn($targetGroup);
3197
+        $this->userManager
3198
+            ->expects($this->once())
3199
+            ->method('get')
3200
+            ->with('TargetUser')
3201
+            ->willReturn(null);
3202
+
3203
+        $this->api->removeFromGroup('TargetUser', 'TargetGroup');
3204
+    }
3205
+
3206
+
3207
+    public function testRemoveFromGroupWithoutPermission(): void {
3208
+        $this->expectException(OCSException::class);
3209
+        $this->expectExceptionCode(104);
3210
+
3211
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3212
+        $loggedInUser
3213
+            ->expects($this->exactly(2))
3214
+            ->method('getUID')
3215
+            ->willReturn('unauthorizedUser');
3216
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3217
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3218
+        $this->userSession
3219
+            ->expects($this->once())
3220
+            ->method('getUser')
3221
+            ->willReturn($loggedInUser);
3222
+        $this->groupManager
3223
+            ->expects($this->once())
3224
+            ->method('get')
3225
+            ->with('TargetGroup')
3226
+            ->willReturn($targetGroup);
3227
+        $this->userManager
3228
+            ->expects($this->once())
3229
+            ->method('get')
3230
+            ->with('TargetUser')
3231
+            ->willReturn($targetUser);
3232
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3233
+            ->disableOriginalConstructor()->getMock();
3234
+        $this->groupManager
3235
+            ->expects($this->once())
3236
+            ->method('getSubAdmin')
3237
+            ->willReturn($subAdminManager);
3238
+        $this->groupManager
3239
+            ->expects($this->once())
3240
+            ->method('isAdmin')
3241
+            ->with('unauthorizedUser')
3242
+            ->willReturn(false);
3243
+
3244
+        $this->api->removeFromGroup('TargetUser', 'TargetGroup');
3245
+    }
3246
+
3247
+
3248
+    public function testRemoveFromGroupAsAdminFromAdmin(): void {
3249
+        $this->expectException(OCSException::class);
3250
+        $this->expectExceptionMessage('Cannot remove yourself from the admin group');
3251
+        $this->expectExceptionCode(105);
3252
+
3253
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3254
+        $loggedInUser
3255
+            ->expects($this->any())
3256
+            ->method('getUID')
3257
+            ->willReturn('admin');
3258
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3259
+        $targetUser
3260
+            ->expects($this->once())
3261
+            ->method('getUID')
3262
+            ->willReturn('admin');
3263
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3264
+        $targetGroup
3265
+            ->expects($this->once())
3266
+            ->method('getGID')
3267
+            ->willReturn('admin');
3268
+        $this->userSession
3269
+            ->expects($this->once())
3270
+            ->method('getUser')
3271
+            ->willReturn($loggedInUser);
3272
+        $this->groupManager
3273
+            ->expects($this->once())
3274
+            ->method('get')
3275
+            ->with('admin')
3276
+            ->willReturn($targetGroup);
3277
+        $this->userManager
3278
+            ->expects($this->once())
3279
+            ->method('get')
3280
+            ->with('Admin')
3281
+            ->willReturn($targetUser);
3282
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3283
+            ->disableOriginalConstructor()->getMock();
3284
+        $this->groupManager
3285
+            ->expects($this->once())
3286
+            ->method('getSubAdmin')
3287
+            ->willReturn($subAdminManager);
3288
+        $this->groupManager
3289
+            ->expects($this->any())
3290
+            ->method('isAdmin')
3291
+            ->with('admin')
3292
+            ->willReturn(true);
3293
+
3294
+        $this->api->removeFromGroup('Admin', 'admin');
3295
+    }
3296
+
3297
+
3298
+    public function testRemoveFromGroupAsSubAdminFromSubAdmin(): void {
3299
+        $this->expectException(OCSException::class);
3300
+        $this->expectExceptionMessage('Cannot remove yourself from this group as you are a sub-admin');
3301
+        $this->expectExceptionCode(105);
3302
+
3303
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3304
+        $loggedInUser
3305
+            ->expects($this->any())
3306
+            ->method('getUID')
3307
+            ->willReturn('subadmin');
3308
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3309
+        $targetUser
3310
+            ->expects($this->once())
3311
+            ->method('getUID')
3312
+            ->willReturn('subadmin');
3313
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3314
+        $targetGroup
3315
+            ->expects($this->any())
3316
+            ->method('getGID')
3317
+            ->willReturn('subadmin');
3318
+        $this->userSession
3319
+            ->expects($this->once())
3320
+            ->method('getUser')
3321
+            ->willReturn($loggedInUser);
3322
+        $this->groupManager
3323
+            ->expects($this->once())
3324
+            ->method('get')
3325
+            ->with('subadmin')
3326
+            ->willReturn($targetGroup);
3327
+        $this->userManager
3328
+            ->expects($this->once())
3329
+            ->method('get')
3330
+            ->with('SubAdmin')
3331
+            ->willReturn($targetUser);
3332
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3333
+            ->disableOriginalConstructor()->getMock();
3334
+        $subAdminManager
3335
+            ->expects($this->once())
3336
+            ->method('isSubAdminOfGroup')
3337
+            ->with($loggedInUser, $targetGroup)
3338
+            ->willReturn(true);
3339
+        $this->groupManager
3340
+            ->expects($this->once())
3341
+            ->method('getSubAdmin')
3342
+            ->willReturn($subAdminManager);
3343
+        $this->groupManager
3344
+            ->expects($this->any())
3345
+            ->method('isAdmin')
3346
+            ->with('subadmin')
3347
+            ->willReturn(false);
3348
+
3349
+        $this->api->removeFromGroup('SubAdmin', 'subadmin');
3350
+    }
3351
+
3352
+
3353
+    public function testRemoveFromGroupAsSubAdminFromLastSubAdminGroup(): void {
3354
+        $this->expectException(OCSException::class);
3355
+        $this->expectExceptionMessage('Not viable to remove user from the last group you are sub-admin of');
3356
+        $this->expectExceptionCode(105);
3357
+
3358
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3359
+        $loggedInUser
3360
+            ->expects($this->any())
3361
+            ->method('getUID')
3362
+            ->willReturn('subadmin');
3363
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3364
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3365
+        $targetGroup
3366
+            ->expects($this->any())
3367
+            ->method('getGID')
3368
+            ->willReturn('subadmin');
3369
+        $this->userSession
3370
+            ->expects($this->once())
3371
+            ->method('getUser')
3372
+            ->willReturn($loggedInUser);
3373
+        $this->groupManager
3374
+            ->expects($this->once())
3375
+            ->method('get')
3376
+            ->with('subadmin')
3377
+            ->willReturn($targetGroup);
3378
+        $this->userManager
3379
+            ->expects($this->once())
3380
+            ->method('get')
3381
+            ->with('AnotherUser')
3382
+            ->willReturn($targetUser);
3383
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3384
+            ->disableOriginalConstructor()->getMock();
3385
+        $subAdminManager
3386
+            ->expects($this->once())
3387
+            ->method('isSubAdminOfGroup')
3388
+            ->with($loggedInUser, $targetGroup)
3389
+            ->willReturn(true);
3390
+        $this->groupManager
3391
+            ->expects($this->once())
3392
+            ->method('getSubAdmin')
3393
+            ->willReturn($subAdminManager);
3394
+        $subAdminManager
3395
+            ->expects($this->once())
3396
+            ->method('getSubAdminsGroups')
3397
+            ->with($loggedInUser)
3398
+            ->willReturn([$targetGroup]);
3399
+
3400
+        $this->groupManager
3401
+            ->expects($this->any())
3402
+            ->method('isAdmin')
3403
+            ->with('subadmin')
3404
+            ->willReturn(false);
3405
+        $this->groupManager
3406
+            ->expects($this->once())
3407
+            ->method('getUserGroupIds')
3408
+            ->with($targetUser)
3409
+            ->willReturn(['subadmin', 'other group']);
3410
+
3411
+        $this->api->removeFromGroup('AnotherUser', 'subadmin');
3412
+    }
3413
+
3414
+    public function testRemoveFromGroupSuccessful(): void {
3415
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3416
+        $loggedInUser
3417
+            ->expects($this->any())
3418
+            ->method('getUID')
3419
+            ->willReturn('admin');
3420
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3421
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3422
+        $this->userSession
3423
+            ->expects($this->once())
3424
+            ->method('getUser')
3425
+            ->willReturn($loggedInUser);
3426
+        $this->groupManager
3427
+            ->expects($this->once())
3428
+            ->method('get')
3429
+            ->with('admin')
3430
+            ->willReturn($targetGroup);
3431
+        $this->userManager
3432
+            ->expects($this->once())
3433
+            ->method('get')
3434
+            ->with('AnotherUser')
3435
+            ->willReturn($targetUser);
3436
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3437
+            ->disableOriginalConstructor()->getMock();
3438
+        $this->groupManager
3439
+            ->expects($this->once())
3440
+            ->method('getSubAdmin')
3441
+            ->willReturn($subAdminManager);
3442
+        $this->groupManager
3443
+            ->expects($this->any())
3444
+            ->method('isAdmin')
3445
+            ->with('admin')
3446
+            ->willReturn(true);
3447
+        $targetGroup
3448
+            ->expects($this->once())
3449
+            ->method('removeUser')
3450
+            ->with($targetUser);
3451
+
3452
+        $this->assertEquals([], $this->api->removeFromGroup('AnotherUser', 'admin')->getData());
3453
+    }
3454
+
3455
+
3456
+    public function testAddSubAdminWithNotExistingTargetUser(): void {
3457
+        $this->expectException(OCSException::class);
3458
+        $this->expectExceptionMessage('User does not exist');
3459
+        $this->expectExceptionCode(101);
3460
+
3461
+        $this->userManager
3462
+            ->expects($this->once())
3463
+            ->method('get')
3464
+            ->with('NotExistingUser')
3465
+            ->willReturn(null);
3466
+
3467
+        $this->api->addSubAdmin('NotExistingUser', '');
3468
+    }
3469
+
3470
+
3471
+    public function testAddSubAdminWithNotExistingTargetGroup(): void {
3472
+        $this->expectException(OCSException::class);
3473
+        $this->expectExceptionMessage('Group does not exist');
3474
+        $this->expectExceptionCode(102);
3475
+
3476
+
3477
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3478
+        $this->userManager
3479
+            ->expects($this->once())
3480
+            ->method('get')
3481
+            ->with('ExistingUser')
3482
+            ->willReturn($targetUser);
3483
+        $this->groupManager
3484
+            ->expects($this->once())
3485
+            ->method('get')
3486
+            ->with('NotExistingGroup')
3487
+            ->willReturn(null);
3488
+
3489
+        $this->api->addSubAdmin('ExistingUser', 'NotExistingGroup');
3490
+    }
3491
+
3492
+
3493
+    public function testAddSubAdminToAdminGroup(): void {
3494
+        $this->expectException(OCSException::class);
3495
+        $this->expectExceptionMessage('Cannot create sub-admins for admin group');
3496
+        $this->expectExceptionCode(103);
3497
+
3498
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3499
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3500
+        $targetGroup
3501
+            ->expects($this->once())
3502
+            ->method('getGID')
3503
+            ->willReturn('admin');
3504
+        $this->userManager
3505
+            ->expects($this->once())
3506
+            ->method('get')
3507
+            ->with('ExistingUser')
3508
+            ->willReturn($targetUser);
3509
+        $this->groupManager
3510
+            ->expects($this->once())
3511
+            ->method('get')
3512
+            ->with('ADmiN')
3513
+            ->willReturn($targetGroup);
3514
+
3515
+        $this->api->addSubAdmin('ExistingUser', 'ADmiN');
3516
+    }
3517
+
3518
+    public function testAddSubAdminTwice(): void {
3519
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3520
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3521
+        $this->userManager
3522
+            ->expects($this->once())
3523
+            ->method('get')
3524
+            ->with('ExistingUser')
3525
+            ->willReturn($targetUser);
3526
+        $this->groupManager
3527
+            ->expects($this->once())
3528
+            ->method('get')
3529
+            ->with('TargetGroup')
3530
+            ->willReturn($targetGroup);
3531
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3532
+            ->disableOriginalConstructor()->getMock();
3533
+        $subAdminManager
3534
+            ->expects($this->once())
3535
+            ->method('isSubAdminOfGroup')
3536
+            ->with($targetUser, $targetGroup)
3537
+            ->willReturn(true);
3538
+        $this->groupManager
3539
+            ->expects($this->once())
3540
+            ->method('getSubAdmin')
3541
+            ->willReturn($subAdminManager);
3542
+
3543
+        $this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3544
+    }
3545
+
3546
+    public function testAddSubAdminSuccessful(): void {
3547
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3548
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3549
+        $this->userManager
3550
+            ->expects($this->once())
3551
+            ->method('get')
3552
+            ->with('ExistingUser')
3553
+            ->willReturn($targetUser);
3554
+        $this->groupManager
3555
+            ->expects($this->once())
3556
+            ->method('get')
3557
+            ->with('TargetGroup')
3558
+            ->willReturn($targetGroup);
3559
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3560
+            ->disableOriginalConstructor()->getMock();
3561
+        $subAdminManager
3562
+            ->expects($this->once())
3563
+            ->method('isSubAdminOfGroup')
3564
+            ->with($targetUser, $targetGroup)
3565
+            ->willReturn(false);
3566
+        $subAdminManager
3567
+            ->expects($this->once())
3568
+            ->method('createSubAdmin')
3569
+            ->with($targetUser, $targetGroup);
3570
+        $this->groupManager
3571
+            ->expects($this->once())
3572
+            ->method('getSubAdmin')
3573
+            ->willReturn($subAdminManager);
3574
+
3575
+        $this->assertEquals([], $this->api->addSubAdmin('ExistingUser', 'TargetGroup')->getData());
3576
+    }
3577
+
3578
+
3579
+    public function testRemoveSubAdminNotExistingTargetUser(): void {
3580
+        $this->expectException(OCSException::class);
3581
+        $this->expectExceptionMessage('User does not exist');
3582
+        $this->expectExceptionCode(101);
3583
+
3584
+        $this->userManager
3585
+            ->expects($this->once())
3586
+            ->method('get')
3587
+            ->with('NotExistingUser')
3588
+            ->willReturn(null);
3589
+
3590
+        $this->api->removeSubAdmin('NotExistingUser', 'GroupToDeleteFrom');
3591
+    }
3592
+
3593
+
3594
+    public function testRemoveSubAdminNotExistingTargetGroup(): void {
3595
+        $this->expectException(OCSException::class);
3596
+        $this->expectExceptionMessage('Group does not exist');
3597
+        $this->expectExceptionCode(101);
3598
+
3599
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3600
+        $this->userManager
3601
+            ->expects($this->once())
3602
+            ->method('get')
3603
+            ->with('ExistingUser')
3604
+            ->willReturn($targetUser);
3605
+        $this->groupManager
3606
+            ->expects($this->once())
3607
+            ->method('get')
3608
+            ->with('GroupToDeleteFrom')
3609
+            ->willReturn(null);
3610
+
3611
+        $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3612
+    }
3613
+
3614
+
3615
+
3616
+    public function testRemoveSubAdminFromNotASubadmin(): void {
3617
+        $this->expectException(OCSException::class);
3618
+        $this->expectExceptionMessage('User is not a sub-admin of this group');
3619
+        $this->expectExceptionCode(102);
3620
+
3621
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3622
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3623
+        $this->userManager
3624
+            ->expects($this->once())
3625
+            ->method('get')
3626
+            ->with('ExistingUser')
3627
+            ->willReturn($targetUser);
3628
+        $this->groupManager
3629
+            ->expects($this->once())
3630
+            ->method('get')
3631
+            ->with('GroupToDeleteFrom')
3632
+            ->willReturn($targetGroup);
3633
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3634
+            ->disableOriginalConstructor()->getMock();
3635
+        $subAdminManager
3636
+            ->expects($this->once())
3637
+            ->method('isSubAdminOfGroup')
3638
+            ->with($targetUser, $targetGroup)
3639
+            ->willReturn(false);
3640
+        $this->groupManager
3641
+            ->expects($this->once())
3642
+            ->method('getSubAdmin')
3643
+            ->willReturn($subAdminManager);
3644
+
3645
+        $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom');
3646
+    }
3647
+
3648
+    public function testRemoveSubAdminSuccessful(): void {
3649
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3650
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3651
+        $this->userManager
3652
+            ->expects($this->once())
3653
+            ->method('get')
3654
+            ->with('ExistingUser')
3655
+            ->willReturn($targetUser);
3656
+        $this->groupManager
3657
+            ->expects($this->once())
3658
+            ->method('get')
3659
+            ->with('GroupToDeleteFrom')
3660
+            ->willReturn($targetGroup);
3661
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3662
+            ->disableOriginalConstructor()->getMock();
3663
+        $subAdminManager
3664
+            ->expects($this->once())
3665
+            ->method('isSubAdminOfGroup')
3666
+            ->with($targetUser, $targetGroup)
3667
+            ->willReturn(true);
3668
+        $subAdminManager
3669
+            ->expects($this->once())
3670
+            ->method('deleteSubAdmin')
3671
+            ->with($targetUser, $targetGroup);
3672
+        $this->groupManager
3673
+            ->expects($this->once())
3674
+            ->method('getSubAdmin')
3675
+            ->willReturn($subAdminManager);
3676
+
3677
+        $this->assertEquals([], $this->api->removeSubAdmin('ExistingUser', 'GroupToDeleteFrom')->getData());
3678
+    }
3679
+
3680
+
3681
+    public function testGetUserSubAdminGroupsNotExistingTargetUser(): void {
3682
+        $this->expectException(OCSException::class);
3683
+        $this->expectExceptionMessage('User does not exist');
3684
+        $this->expectExceptionCode(404);
3685
+
3686
+        $this->userManager
3687
+            ->expects($this->once())
3688
+            ->method('get')
3689
+            ->with('RequestedUser')
3690
+            ->willReturn(null);
3691
+
3692
+        $this->api->getUserSubAdminGroups('RequestedUser');
3693
+    }
3694
+
3695
+    public function testGetUserSubAdminGroupsWithGroups(): void {
3696
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3697
+        $targetGroup = $this->getMockBuilder('\OCP\IGroup')->disableOriginalConstructor()->getMock();
3698
+        $targetGroup
3699
+            ->expects($this->once())
3700
+            ->method('getGID')
3701
+            ->willReturn('TargetGroup');
3702
+        $this->userManager
3703
+            ->expects($this->once())
3704
+            ->method('get')
3705
+            ->with('RequestedUser')
3706
+            ->willReturn($targetUser);
3707
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3708
+            ->disableOriginalConstructor()->getMock();
3709
+        $subAdminManager
3710
+            ->expects($this->once())
3711
+            ->method('getSubAdminsGroups')
3712
+            ->with($targetUser)
3713
+            ->willReturn([$targetGroup]);
3714
+        $this->groupManager
3715
+            ->expects($this->once())
3716
+            ->method('getSubAdmin')
3717
+            ->willReturn($subAdminManager);
3718
+
3719
+        $this->assertEquals(['TargetGroup'], $this->api->getUserSubAdminGroups('RequestedUser')->getData());
3720
+    }
3721
+
3722
+    public function testEnableUser(): void {
3723
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3724
+        $targetUser->expects($this->once())
3725
+            ->method('setEnabled')
3726
+            ->with(true);
3727
+        $this->userManager
3728
+            ->expects($this->once())
3729
+            ->method('get')
3730
+            ->with('RequestedUser')
3731
+            ->willReturn($targetUser);
3732
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3733
+        $loggedInUser
3734
+            ->expects($this->exactly(3))
3735
+            ->method('getUID')
3736
+            ->willReturn('admin');
3737
+        $this->userSession
3738
+            ->expects($this->once())
3739
+            ->method('getUser')
3740
+            ->willReturn($loggedInUser);
3741
+        $this->groupManager
3742
+            ->expects($this->once())
3743
+            ->method('isAdmin')
3744
+            ->willReturn(true);
3745
+
3746
+        $this->assertEquals([], $this->api->enableUser('RequestedUser')->getData());
3747
+    }
3748
+
3749
+    public function testDisableUser(): void {
3750
+        $targetUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3751
+        $targetUser->expects($this->once())
3752
+            ->method('setEnabled')
3753
+            ->with(false);
3754
+        $this->userManager
3755
+            ->expects($this->once())
3756
+            ->method('get')
3757
+            ->with('RequestedUser')
3758
+            ->willReturn($targetUser);
3759
+        $loggedInUser = $this->getMockBuilder(IUser::class)->disableOriginalConstructor()->getMock();
3760
+        $loggedInUser
3761
+            ->expects($this->exactly(3))
3762
+            ->method('getUID')
3763
+            ->willReturn('admin');
3764
+        $this->userSession
3765
+            ->expects($this->once())
3766
+            ->method('getUser')
3767
+            ->willReturn($loggedInUser);
3768
+        $this->groupManager
3769
+            ->expects($this->once())
3770
+            ->method('isAdmin')
3771
+            ->willReturn(true);
3772
+
3773
+        $this->assertEquals([], $this->api->disableUser('RequestedUser')->getData());
3774
+    }
3775
+
3776
+    public function testGetCurrentUserLoggedIn(): void {
3777
+        $user = $this->createMock(IUser::class);
3778
+        $user->expects($this->once())->method('getUID')->willReturn('UID');
3779
+
3780
+        $this->userSession->expects($this->once())->method('getUser')
3781
+            ->willReturn($user);
3782
+
3783
+        /** @var UsersController | MockObject $api */
3784
+        $api = $this->getMockBuilder(UsersController::class)
3785
+            ->setConstructorArgs([
3786
+                'provisioning_api',
3787
+                $this->request,
3788
+                $this->userManager,
3789
+                $this->config,
3790
+                $this->groupManager,
3791
+                $this->userSession,
3792
+                $this->accountManager,
3793
+                $this->subAdminManager,
3794
+                $this->l10nFactory,
3795
+                $this->rootFolder,
3796
+                $this->urlGenerator,
3797
+                $this->logger,
3798
+                $this->newUserMailHelper,
3799
+                $this->secureRandom,
3800
+                $this->remoteWipe,
3801
+                $this->knownUserService,
3802
+                $this->eventDispatcher,
3803
+                $this->phoneNumberUtil,
3804
+                $this->appManager,
3805
+            ])
3806
+            ->onlyMethods(['getUserData'])
3807
+            ->getMock();
3808
+
3809
+        $api->expects($this->once())->method('getUserData')->with('UID', true)
3810
+            ->willReturn(
3811
+                [
3812
+                    'id' => 'UID',
3813
+                    'enabled' => 'true',
3814
+                    'quota' => ['DummyValue'],
3815
+                    'email' => '[email protected]',
3816
+                    'displayname' => 'Demo User',
3817
+                    'display-name' => 'Demo User',
3818
+                    'phone' => 'phone',
3819
+                    'address' => 'address',
3820
+                    'website' => 'website',
3821
+                    'twitter' => 'twitter',
3822
+                    'fediverse' => 'fediverse',
3823
+                    'organisation' => 'organisation',
3824
+                    'role' => 'role',
3825
+                    'headline' => 'headline',
3826
+                    'biography' => 'biography',
3827
+                    'profile_enabled' => '1',
3828
+                    'pronouns' => 'they/them',
3829
+                ]
3830
+            );
3831
+
3832
+        $expected = [
3833
+            'id' => 'UID',
3834
+            'enabled' => 'true',
3835
+            'quota' => ['DummyValue'],
3836
+            'email' => '[email protected]',
3837
+            'displayname' => 'Demo User',
3838
+            'display-name' => 'Demo User',
3839
+            'phone' => 'phone',
3840
+            'address' => 'address',
3841
+            'website' => 'website',
3842
+            'twitter' => 'twitter',
3843
+            'fediverse' => 'fediverse',
3844
+            'organisation' => 'organisation',
3845
+            'role' => 'role',
3846
+            'headline' => 'headline',
3847
+            'biography' => 'biography',
3848
+            'profile_enabled' => '1',
3849
+            'pronouns' => 'they/them',
3850
+        ];
3851
+
3852
+        $this->assertSame($expected, $api->getCurrentUser()->getData());
3853
+    }
3854
+
3855
+
3856
+    public function testGetCurrentUserNotLoggedIn(): void {
3857
+        $this->expectException(OCSException::class);
3858
+
3859
+
3860
+        $this->userSession->expects($this->once())->method('getUser')
3861
+            ->willReturn(null);
3862
+
3863
+        $this->api->getCurrentUser();
3864
+    }
3865
+
3866
+    public function testGetUser(): void {
3867
+        $loggedInUser = $this->createMock(IUser::class);
3868
+        $loggedInUser
3869
+            ->method('getUID')
3870
+            ->willReturn('currentuser');
3871
+        $this->userSession
3872
+            ->method('getUser')
3873
+            ->willReturn($loggedInUser);
3874
+
3875
+        /** @var UsersController | MockObject $api */
3876
+        $api = $this->getMockBuilder(UsersController::class)
3877
+            ->setConstructorArgs([
3878
+                'provisioning_api',
3879
+                $this->request,
3880
+                $this->userManager,
3881
+                $this->config,
3882
+                $this->groupManager,
3883
+                $this->userSession,
3884
+                $this->accountManager,
3885
+                $this->subAdminManager,
3886
+                $this->l10nFactory,
3887
+                $this->rootFolder,
3888
+                $this->urlGenerator,
3889
+                $this->logger,
3890
+                $this->newUserMailHelper,
3891
+                $this->secureRandom,
3892
+                $this->remoteWipe,
3893
+                $this->knownUserService,
3894
+                $this->eventDispatcher,
3895
+                $this->phoneNumberUtil,
3896
+                $this->appManager,
3897
+            ])
3898
+            ->onlyMethods(['getUserData'])
3899
+            ->getMock();
3900
+
3901
+        $expected = [
3902
+            'id' => 'UID',
3903
+            'enabled' => 'true',
3904
+            'quota' => ['DummyValue'],
3905
+            'email' => '[email protected]',
3906
+            'phone' => 'phone',
3907
+            'address' => 'address',
3908
+            'website' => 'website',
3909
+            'twitter' => 'twitter',
3910
+            'fediverse' => 'fediverse',
3911
+            'displayname' => 'Demo User',
3912
+            'display-name' => 'Demo User',
3913
+            'organisation' => 'organisation',
3914
+            'role' => 'role',
3915
+            'headline' => 'headline',
3916
+            'biography' => 'biography',
3917
+            'profile_enabled' => '1',
3918
+            'pronouns' => 'they/them',
3919
+        ];
3920
+
3921
+        $api->expects($this->exactly(2))
3922
+            ->method('getUserData')
3923
+            ->willReturnMap([
3924
+                ['uid', false, $expected],
3925
+                ['currentuser', true, $expected],
3926
+            ]);
3927
+
3928
+        $this->assertSame($expected, $api->getUser('uid')->getData());
3929
+
3930
+        $this->assertSame($expected, $api->getUser('currentuser')->getData());
3931
+    }
3932
+
3933
+
3934
+    public function testResendWelcomeMessageWithNotExistingTargetUser(): void {
3935
+        $this->expectException(OCSException::class);
3936
+        $this->expectExceptionCode(998);
3937
+
3938
+        $this->userManager
3939
+            ->expects($this->once())
3940
+            ->method('get')
3941
+            ->with('NotExistingUser')
3942
+            ->willReturn(null);
3943
+
3944
+        $this->api->resendWelcomeMessage('NotExistingUser');
3945
+    }
3946
+
3947
+
3948
+    public function testResendWelcomeMessageAsSubAdminAndUserIsNotAccessible(): void {
3949
+        $this->expectException(OCSException::class);
3950
+        $this->expectExceptionCode(998);
3951
+
3952
+        $loggedInUser = $this->getMockBuilder(IUser::class)
3953
+            ->disableOriginalConstructor()
3954
+            ->getMock();
3955
+        $loggedInUser
3956
+            ->expects($this->exactly(2))
3957
+            ->method('getUID')
3958
+            ->willReturn('subadmin');
3959
+        $targetUser = $this->getMockBuilder(IUser::class)
3960
+            ->disableOriginalConstructor()
3961
+            ->getMock();
3962
+        $this->userSession
3963
+            ->expects($this->once())
3964
+            ->method('getUser')
3965
+            ->willReturn($loggedInUser);
3966
+        $this->userManager
3967
+            ->expects($this->once())
3968
+            ->method('get')
3969
+            ->with('UserToGet')
3970
+            ->willReturn($targetUser);
3971
+        $this->groupManager
3972
+            ->expects($this->once())
3973
+            ->method('isAdmin')
3974
+            ->with('subadmin')
3975
+            ->willReturn(false);
3976
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
3977
+            ->disableOriginalConstructor()
3978
+            ->getMock();
3979
+        $subAdminManager
3980
+            ->expects($this->once())
3981
+            ->method('isUserAccessible')
3982
+            ->with($loggedInUser, $targetUser)
3983
+            ->willReturn(false);
3984
+        $this->groupManager
3985
+            ->expects($this->once())
3986
+            ->method('getSubAdmin')
3987
+            ->willReturn($subAdminManager);
3988
+
3989
+        $this->api->resendWelcomeMessage('UserToGet');
3990
+    }
3991
+
3992
+
3993
+    public function testResendWelcomeMessageNoEmail(): void {
3994
+        $this->expectException(OCSException::class);
3995
+        $this->expectExceptionMessage('Email address not available');
3996
+        $this->expectExceptionCode(101);
3997
+
3998
+        $loggedInUser = $this->getMockBuilder(IUser::class)
3999
+            ->disableOriginalConstructor()
4000
+            ->getMock();
4001
+        $targetUser = $this->getMockBuilder(IUser::class)
4002
+            ->disableOriginalConstructor()
4003
+            ->getMock();
4004
+        $this->userSession
4005
+            ->expects($this->once())
4006
+            ->method('getUser')
4007
+            ->willReturn($loggedInUser);
4008
+        $this->userManager
4009
+            ->expects($this->once())
4010
+            ->method('get')
4011
+            ->with('UserToGet')
4012
+            ->willReturn($targetUser);
4013
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4014
+            ->disableOriginalConstructor()
4015
+            ->getMock();
4016
+        $subAdminManager
4017
+            ->expects($this->once())
4018
+            ->method('isUserAccessible')
4019
+            ->with($loggedInUser, $targetUser)
4020
+            ->willReturn(true);
4021
+        $this->groupManager
4022
+            ->expects($this->once())
4023
+            ->method('getSubAdmin')
4024
+            ->willReturn($subAdminManager);
4025
+        $loggedInUser
4026
+            ->expects($this->exactly(2))
4027
+            ->method('getUID')
4028
+            ->willReturn('logged-user-id');
4029
+        $targetUser
4030
+            ->expects($this->once())
4031
+            ->method('getEmailAddress')
4032
+            ->willReturn('');
4033
+
4034
+        $this->api->resendWelcomeMessage('UserToGet');
4035
+    }
4036
+
4037
+
4038
+    public function testResendWelcomeMessageNullEmail(): void {
4039
+        $this->expectException(OCSException::class);
4040
+        $this->expectExceptionMessage('Email address not available');
4041
+        $this->expectExceptionCode(101);
4042
+
4043
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4044
+            ->disableOriginalConstructor()
4045
+            ->getMock();
4046
+        $targetUser = $this->getMockBuilder(IUser::class)
4047
+            ->disableOriginalConstructor()
4048
+            ->getMock();
4049
+        $this->userSession
4050
+            ->expects($this->once())
4051
+            ->method('getUser')
4052
+            ->willReturn($loggedInUser);
4053
+        $this->userManager
4054
+            ->expects($this->once())
4055
+            ->method('get')
4056
+            ->with('UserToGet')
4057
+            ->willReturn($targetUser);
4058
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4059
+            ->disableOriginalConstructor()
4060
+            ->getMock();
4061
+        $subAdminManager
4062
+            ->expects($this->once())
4063
+            ->method('isUserAccessible')
4064
+            ->with($loggedInUser, $targetUser)
4065
+            ->willReturn(true);
4066
+        $this->groupManager
4067
+            ->expects($this->once())
4068
+            ->method('getSubAdmin')
4069
+            ->willReturn($subAdminManager);
4070
+        $loggedInUser
4071
+            ->expects($this->exactly(2))
4072
+            ->method('getUID')
4073
+            ->willReturn('logged-user-id');
4074
+        $targetUser
4075
+            ->expects($this->once())
4076
+            ->method('getEmailAddress')
4077
+            ->willReturn(null);
4078
+
4079
+        $this->api->resendWelcomeMessage('UserToGet');
4080
+    }
4081
+
4082
+    public function testResendWelcomeMessageSuccess(): void {
4083
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4084
+            ->disableOriginalConstructor()
4085
+            ->getMock();
4086
+        $targetUser = $this->getMockBuilder(IUser::class)
4087
+            ->disableOriginalConstructor()
4088
+            ->getMock();
4089
+        $loggedInUser
4090
+            ->method('getUID')
4091
+            ->willReturn('logged-user-id');
4092
+        $targetUser
4093
+            ->method('getUID')
4094
+            ->willReturn('user-id');
4095
+        $this->userSession
4096
+            ->expects($this->once())
4097
+            ->method('getUser')
4098
+            ->willReturn($loggedInUser);
4099
+        $this->userManager
4100
+            ->expects($this->once())
4101
+            ->method('get')
4102
+            ->with('UserToGet')
4103
+            ->willReturn($targetUser);
4104
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4105
+            ->disableOriginalConstructor()
4106
+            ->getMock();
4107
+        $subAdminManager
4108
+            ->expects($this->once())
4109
+            ->method('isUserAccessible')
4110
+            ->with($loggedInUser, $targetUser)
4111
+            ->willReturn(true);
4112
+        $this->groupManager
4113
+            ->expects($this->once())
4114
+            ->method('getSubAdmin')
4115
+            ->willReturn($subAdminManager);
4116
+        $targetUser
4117
+            ->expects($this->once())
4118
+            ->method('getEmailAddress')
4119
+            ->willReturn('[email protected]');
4120
+        $emailTemplate = $this->createMock(IEMailTemplate::class);
4121
+        $this->newUserMailHelper
4122
+            ->expects($this->once())
4123
+            ->method('generateTemplate')
4124
+            ->willReturn($emailTemplate);
4125
+        $this->newUserMailHelper
4126
+            ->expects($this->once())
4127
+            ->method('sendMail')
4128
+            ->with($targetUser, $emailTemplate);
4129
+
4130
+        $this->api->resendWelcomeMessage('UserToGet');
4131
+    }
4132
+
4133
+    public function testResendWelcomeMessageSuccessWithFallbackLanguage(): void {
4134
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4135
+            ->disableOriginalConstructor()
4136
+            ->getMock();
4137
+        $targetUser = $this->getMockBuilder(IUser::class)
4138
+            ->disableOriginalConstructor()
4139
+            ->getMock();
4140
+        $loggedInUser
4141
+            ->method('getUID')
4142
+            ->willReturn('logged-user-id');
4143
+        $targetUser
4144
+            ->method('getUID')
4145
+            ->willReturn('user-id');
4146
+        $this->userSession
4147
+            ->expects($this->once())
4148
+            ->method('getUser')
4149
+            ->willReturn($loggedInUser);
4150
+        $this->userManager
4151
+            ->expects($this->once())
4152
+            ->method('get')
4153
+            ->with('UserToGet')
4154
+            ->willReturn($targetUser);
4155
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4156
+            ->disableOriginalConstructor()
4157
+            ->getMock();
4158
+        $subAdminManager
4159
+            ->expects($this->once())
4160
+            ->method('isUserAccessible')
4161
+            ->with($loggedInUser, $targetUser)
4162
+            ->willReturn(true);
4163
+        $this->groupManager
4164
+            ->expects($this->once())
4165
+            ->method('getSubAdmin')
4166
+            ->willReturn($subAdminManager);
4167
+        $targetUser
4168
+            ->expects($this->once())
4169
+            ->method('getEmailAddress')
4170
+            ->willReturn('[email protected]');
4171
+        $emailTemplate = $this->createMock(IEMailTemplate::class);
4172
+        $this->newUserMailHelper
4173
+            ->expects($this->once())
4174
+            ->method('generateTemplate')
4175
+            ->willReturn($emailTemplate);
4176
+        $this->newUserMailHelper
4177
+            ->expects($this->once())
4178
+            ->method('sendMail')
4179
+            ->with($targetUser, $emailTemplate);
4180
+
4181
+        $this->api->resendWelcomeMessage('UserToGet');
4182
+    }
4183
+
4184
+
4185
+    public function testResendWelcomeMessageFailed(): void {
4186
+        $this->expectException(OCSException::class);
4187
+        $this->expectExceptionMessage('Sending email failed');
4188
+        $this->expectExceptionCode(102);
4189
+
4190
+        $loggedInUser = $this->getMockBuilder(IUser::class)
4191
+            ->disableOriginalConstructor()
4192
+            ->getMock();
4193
+        $targetUser = $this->getMockBuilder(IUser::class)
4194
+            ->disableOriginalConstructor()
4195
+            ->getMock();
4196
+        $loggedInUser
4197
+            ->expects($this->exactly(2))
4198
+            ->method('getUID')
4199
+            ->willReturn('logged-user-id');
4200
+        $targetUser
4201
+            ->method('getUID')
4202
+            ->willReturn('user-id');
4203
+        $this->userSession
4204
+            ->expects($this->once())
4205
+            ->method('getUser')
4206
+            ->willReturn($loggedInUser);
4207
+        $this->userManager
4208
+            ->expects($this->once())
4209
+            ->method('get')
4210
+            ->with('UserToGet')
4211
+            ->willReturn($targetUser);
4212
+        $subAdminManager = $this->getMockBuilder('OC\SubAdmin')
4213
+            ->disableOriginalConstructor()
4214
+            ->getMock();
4215
+        $subAdminManager
4216
+            ->expects($this->once())
4217
+            ->method('isUserAccessible')
4218
+            ->with($loggedInUser, $targetUser)
4219
+            ->willReturn(true);
4220
+        $this->groupManager
4221
+            ->expects($this->once())
4222
+            ->method('getSubAdmin')
4223
+            ->willReturn($subAdminManager);
4224
+        $targetUser
4225
+            ->expects($this->once())
4226
+            ->method('getEmailAddress')
4227
+            ->willReturn('[email protected]');
4228
+        $emailTemplate = $this->createMock(IEMailTemplate::class);
4229
+        $this->newUserMailHelper
4230
+            ->expects($this->once())
4231
+            ->method('generateTemplate')
4232
+            ->willReturn($emailTemplate);
4233
+        $this->newUserMailHelper
4234
+            ->expects($this->once())
4235
+            ->method('sendMail')
4236
+            ->with($targetUser, $emailTemplate)
4237
+            ->willThrowException(new \Exception());
4238
+
4239
+        $this->api->resendWelcomeMessage('UserToGet');
4240
+    }
4241
+
4242
+
4243
+    public static function dataGetEditableFields(): array {
4244
+        return [
4245
+            [false, true, ISetDisplayNameBackend::class, [
4246
+                IAccountManager::PROPERTY_EMAIL,
4247
+                IAccountManager::COLLECTION_EMAIL,
4248
+                IAccountManager::PROPERTY_PHONE,
4249
+                IAccountManager::PROPERTY_ADDRESS,
4250
+                IAccountManager::PROPERTY_WEBSITE,
4251
+                IAccountManager::PROPERTY_TWITTER,
4252
+                IAccountManager::PROPERTY_FEDIVERSE,
4253
+                IAccountManager::PROPERTY_ORGANISATION,
4254
+                IAccountManager::PROPERTY_ROLE,
4255
+                IAccountManager::PROPERTY_HEADLINE,
4256
+                IAccountManager::PROPERTY_BIOGRAPHY,
4257
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4258
+                IAccountManager::PROPERTY_PRONOUNS,
4259
+            ]],
4260
+            [true, false, ISetDisplayNameBackend::class, [
4261
+                IAccountManager::PROPERTY_DISPLAYNAME,
4262
+                IAccountManager::COLLECTION_EMAIL,
4263
+                IAccountManager::PROPERTY_PHONE,
4264
+                IAccountManager::PROPERTY_ADDRESS,
4265
+                IAccountManager::PROPERTY_WEBSITE,
4266
+                IAccountManager::PROPERTY_TWITTER,
4267
+                IAccountManager::PROPERTY_FEDIVERSE,
4268
+                IAccountManager::PROPERTY_ORGANISATION,
4269
+                IAccountManager::PROPERTY_ROLE,
4270
+                IAccountManager::PROPERTY_HEADLINE,
4271
+                IAccountManager::PROPERTY_BIOGRAPHY,
4272
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4273
+                IAccountManager::PROPERTY_PRONOUNS,
4274
+            ]],
4275
+            [true, true, ISetDisplayNameBackend::class, [
4276
+                IAccountManager::PROPERTY_DISPLAYNAME,
4277
+                IAccountManager::PROPERTY_EMAIL,
4278
+                IAccountManager::COLLECTION_EMAIL,
4279
+                IAccountManager::PROPERTY_PHONE,
4280
+                IAccountManager::PROPERTY_ADDRESS,
4281
+                IAccountManager::PROPERTY_WEBSITE,
4282
+                IAccountManager::PROPERTY_TWITTER,
4283
+                IAccountManager::PROPERTY_FEDIVERSE,
4284
+                IAccountManager::PROPERTY_ORGANISATION,
4285
+                IAccountManager::PROPERTY_ROLE,
4286
+                IAccountManager::PROPERTY_HEADLINE,
4287
+                IAccountManager::PROPERTY_BIOGRAPHY,
4288
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4289
+                IAccountManager::PROPERTY_PRONOUNS,
4290
+            ]],
4291
+            [false, false, ISetDisplayNameBackend::class, [
4292
+                IAccountManager::COLLECTION_EMAIL,
4293
+                IAccountManager::PROPERTY_PHONE,
4294
+                IAccountManager::PROPERTY_ADDRESS,
4295
+                IAccountManager::PROPERTY_WEBSITE,
4296
+                IAccountManager::PROPERTY_TWITTER,
4297
+                IAccountManager::PROPERTY_FEDIVERSE,
4298
+                IAccountManager::PROPERTY_ORGANISATION,
4299
+                IAccountManager::PROPERTY_ROLE,
4300
+                IAccountManager::PROPERTY_HEADLINE,
4301
+                IAccountManager::PROPERTY_BIOGRAPHY,
4302
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4303
+                IAccountManager::PROPERTY_PRONOUNS,
4304
+            ]],
4305
+            [false, true, UserInterface::class, [
4306
+                IAccountManager::PROPERTY_EMAIL,
4307
+                IAccountManager::COLLECTION_EMAIL,
4308
+                IAccountManager::PROPERTY_PHONE,
4309
+                IAccountManager::PROPERTY_ADDRESS,
4310
+                IAccountManager::PROPERTY_WEBSITE,
4311
+                IAccountManager::PROPERTY_TWITTER,
4312
+                IAccountManager::PROPERTY_FEDIVERSE,
4313
+                IAccountManager::PROPERTY_ORGANISATION,
4314
+                IAccountManager::PROPERTY_ROLE,
4315
+                IAccountManager::PROPERTY_HEADLINE,
4316
+                IAccountManager::PROPERTY_BIOGRAPHY,
4317
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4318
+                IAccountManager::PROPERTY_PRONOUNS,
4319
+            ]],
4320
+            [true, false, UserInterface::class, [
4321
+                IAccountManager::COLLECTION_EMAIL,
4322
+                IAccountManager::PROPERTY_PHONE,
4323
+                IAccountManager::PROPERTY_ADDRESS,
4324
+                IAccountManager::PROPERTY_WEBSITE,
4325
+                IAccountManager::PROPERTY_TWITTER,
4326
+                IAccountManager::PROPERTY_FEDIVERSE,
4327
+                IAccountManager::PROPERTY_ORGANISATION,
4328
+                IAccountManager::PROPERTY_ROLE,
4329
+                IAccountManager::PROPERTY_HEADLINE,
4330
+                IAccountManager::PROPERTY_BIOGRAPHY,
4331
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4332
+                IAccountManager::PROPERTY_PRONOUNS,
4333
+            ]],
4334
+            [true, true, UserInterface::class, [
4335
+                IAccountManager::PROPERTY_EMAIL,
4336
+                IAccountManager::COLLECTION_EMAIL,
4337
+                IAccountManager::PROPERTY_PHONE,
4338
+                IAccountManager::PROPERTY_ADDRESS,
4339
+                IAccountManager::PROPERTY_WEBSITE,
4340
+                IAccountManager::PROPERTY_TWITTER,
4341
+                IAccountManager::PROPERTY_FEDIVERSE,
4342
+                IAccountManager::PROPERTY_ORGANISATION,
4343
+                IAccountManager::PROPERTY_ROLE,
4344
+                IAccountManager::PROPERTY_HEADLINE,
4345
+                IAccountManager::PROPERTY_BIOGRAPHY,
4346
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4347
+                IAccountManager::PROPERTY_PRONOUNS,
4348
+            ]],
4349
+            [false, false, UserInterface::class, [
4350
+                IAccountManager::COLLECTION_EMAIL,
4351
+                IAccountManager::PROPERTY_PHONE,
4352
+                IAccountManager::PROPERTY_ADDRESS,
4353
+                IAccountManager::PROPERTY_WEBSITE,
4354
+                IAccountManager::PROPERTY_TWITTER,
4355
+                IAccountManager::PROPERTY_FEDIVERSE,
4356
+                IAccountManager::PROPERTY_ORGANISATION,
4357
+                IAccountManager::PROPERTY_ROLE,
4358
+                IAccountManager::PROPERTY_HEADLINE,
4359
+                IAccountManager::PROPERTY_BIOGRAPHY,
4360
+                IAccountManager::PROPERTY_PROFILE_ENABLED,
4361
+                IAccountManager::PROPERTY_PRONOUNS,
4362
+            ]],
4363
+        ];
4364
+    }
4365
+
4366
+    /**
4367
+     * @dataProvider dataGetEditableFields
4368
+     */
4369
+    public function testGetEditableFields(bool $allowedToChangeDisplayName, bool $allowedToChangeEmail, string $userBackend, array $expected): void {
4370
+        $this->config->method('getSystemValue')->willReturnCallback(fn (string $key, mixed $default) => match ($key) {
4371
+            'allow_user_to_change_display_name' => $allowedToChangeDisplayName,
4372
+            'allow_user_to_change_email' => $allowedToChangeEmail,
4373
+            default => throw new RuntimeException('Unexpected system config key: ' . $key),
4374
+        });
4375
+
4376
+        $user = $this->createMock(IUser::class);
4377
+        $this->userSession->method('getUser')
4378
+            ->willReturn($user);
4379
+
4380
+        $backend = $this->createMock($userBackend);
4381
+
4382
+        $user->method('getUID')
4383
+            ->willReturn('userId');
4384
+        $user->method('getBackend')
4385
+            ->willReturn($backend);
4386
+
4387
+        $expectedResp = new DataResponse($expected);
4388
+        $this->assertEquals($expectedResp, $this->api->getEditableFields('userId'));
4389
+    }
4390
+
4391
+    private function mockAccount($targetUser, $accountProperties) {
4392
+        $mockedProperties = [];
4393
+
4394
+        foreach ($accountProperties as $propertyName => $data) {
4395
+            $mockedProperty = $this->createMock(IAccountProperty::class);
4396
+            $mockedProperty->method('getValue')->willReturn($data['value'] ?? '');
4397
+            $mockedProperty->method('getScope')->willReturn($data['scope'] ?? '');
4398
+            $mockedProperties[] = [$propertyName, $mockedProperty];
4399
+        }
4400
+
4401
+        $account = $this->createMock(IAccount::class);
4402
+        $account->method('getProperty')
4403
+            ->will($this->returnValueMap($mockedProperties));
4404
+
4405
+        $this->accountManager->expects($this->any())->method('getAccount')
4406
+            ->with($targetUser)
4407
+            ->willReturn($account);
4408
+    }
4409 4409
 }
Please login to merge, or discard this patch.
apps/provisioning_api/lib/Controller/UsersController.php 1 patch
Indentation   +1739 added lines, -1739 removed lines patch added patch discarded remove patch
@@ -59,1743 +59,1743 @@
 block discarded – undo
59 59
  */
60 60
 class UsersController extends AUserDataOCSController {
61 61
 
62
-	private IL10N $l10n;
63
-
64
-	public function __construct(
65
-		string $appName,
66
-		IRequest $request,
67
-		IUserManager $userManager,
68
-		IConfig $config,
69
-		IGroupManager $groupManager,
70
-		IUserSession $userSession,
71
-		IAccountManager $accountManager,
72
-		ISubAdmin $subAdminManager,
73
-		IFactory $l10nFactory,
74
-		IRootFolder $rootFolder,
75
-		private IURLGenerator $urlGenerator,
76
-		private LoggerInterface $logger,
77
-		private NewUserMailHelper $newUserMailHelper,
78
-		private ISecureRandom $secureRandom,
79
-		private RemoteWipe $remoteWipe,
80
-		private KnownUserService $knownUserService,
81
-		private IEventDispatcher $eventDispatcher,
82
-		private IPhoneNumberUtil $phoneNumberUtil,
83
-		private IAppManager $appManager,
84
-	) {
85
-		parent::__construct(
86
-			$appName,
87
-			$request,
88
-			$userManager,
89
-			$config,
90
-			$groupManager,
91
-			$userSession,
92
-			$accountManager,
93
-			$subAdminManager,
94
-			$l10nFactory,
95
-			$rootFolder,
96
-		);
97
-
98
-		$this->l10n = $l10nFactory->get($appName);
99
-	}
100
-
101
-	/**
102
-	 * Get a list of users
103
-	 *
104
-	 * @param string $search Text to search for
105
-	 * @param int|null $limit Limit the amount of groups returned
106
-	 * @param int $offset Offset for searching for groups
107
-	 * @return DataResponse<Http::STATUS_OK, array{users: list<string>}, array{}>
108
-	 *
109
-	 * 200: Users returned
110
-	 */
111
-	#[NoAdminRequired]
112
-	public function getUsers(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
113
-		$user = $this->userSession->getUser();
114
-		$users = [];
115
-
116
-		// Admin? Or SubAdmin?
117
-		$uid = $user->getUID();
118
-		$subAdminManager = $this->groupManager->getSubAdmin();
119
-		$isAdmin = $this->groupManager->isAdmin($uid);
120
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
121
-		if ($isAdmin || $isDelegatedAdmin) {
122
-			$users = $this->userManager->search($search, $limit, $offset);
123
-		} elseif ($subAdminManager->isSubAdmin($user)) {
124
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
125
-			foreach ($subAdminOfGroups as $key => $group) {
126
-				$subAdminOfGroups[$key] = $group->getGID();
127
-			}
128
-
129
-			$users = [];
130
-			foreach ($subAdminOfGroups as $group) {
131
-				$users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
132
-			}
133
-		}
134
-
135
-		/** @var list<string> $users */
136
-		$users = array_keys($users);
137
-
138
-		return new DataResponse([
139
-			'users' => $users
140
-		]);
141
-	}
142
-
143
-	/**
144
-	 * Get a list of users and their details
145
-	 *
146
-	 * @param string $search Text to search for
147
-	 * @param int|null $limit Limit the amount of groups returned
148
-	 * @param int $offset Offset for searching for groups
149
-	 * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
150
-	 *
151
-	 * 200: Users details returned
152
-	 */
153
-	#[NoAdminRequired]
154
-	public function getUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
155
-		$currentUser = $this->userSession->getUser();
156
-		$users = [];
157
-
158
-		// Admin? Or SubAdmin?
159
-		$uid = $currentUser->getUID();
160
-		$subAdminManager = $this->groupManager->getSubAdmin();
161
-		$isAdmin = $this->groupManager->isAdmin($uid);
162
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
163
-		if ($isAdmin || $isDelegatedAdmin) {
164
-			$users = $this->userManager->search($search, $limit, $offset);
165
-			$users = array_keys($users);
166
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
167
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
168
-			foreach ($subAdminOfGroups as $key => $group) {
169
-				$subAdminOfGroups[$key] = $group->getGID();
170
-			}
171
-
172
-			$users = [];
173
-			foreach ($subAdminOfGroups as $group) {
174
-				$users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
175
-			}
176
-			$users = array_merge(...$users);
177
-		}
178
-
179
-		$usersDetails = [];
180
-		foreach ($users as $userId) {
181
-			$userId = (string)$userId;
182
-			try {
183
-				$userData = $this->getUserData($userId);
184
-			} catch (OCSNotFoundException $e) {
185
-				// We still want to return all other accounts, but this one was removed from the backends
186
-				// yet they are still in our database. Might be a LDAP remnant.
187
-				$userData = null;
188
-				$this->logger->warning('Found one enabled account that is removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
189
-			}
190
-			// Do not insert empty entry
191
-			if ($userData !== null) {
192
-				$usersDetails[$userId] = $userData;
193
-			} else {
194
-				// Logged user does not have permissions to see this user
195
-				// only showing its id
196
-				$usersDetails[$userId] = ['id' => $userId];
197
-			}
198
-		}
199
-
200
-		return new DataResponse([
201
-			'users' => $usersDetails
202
-		]);
203
-	}
204
-
205
-	/**
206
-	 * Get the list of disabled users and their details
207
-	 *
208
-	 * @param string $search Text to search for
209
-	 * @param ?int $limit Limit the amount of users returned
210
-	 * @param int $offset Offset
211
-	 * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
212
-	 *
213
-	 * 200: Disabled users details returned
214
-	 */
215
-	#[NoAdminRequired]
216
-	public function getDisabledUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
217
-		$currentUser = $this->userSession->getUser();
218
-		if ($currentUser === null) {
219
-			return new DataResponse(['users' => []]);
220
-		}
221
-		if ($limit !== null && $limit < 0) {
222
-			throw new InvalidArgumentException("Invalid limit value: $limit");
223
-		}
224
-		if ($offset < 0) {
225
-			throw new InvalidArgumentException("Invalid offset value: $offset");
226
-		}
227
-
228
-		$users = [];
229
-
230
-		// Admin? Or SubAdmin?
231
-		$uid = $currentUser->getUID();
232
-		$subAdminManager = $this->groupManager->getSubAdmin();
233
-		$isAdmin = $this->groupManager->isAdmin($uid);
234
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
235
-		if ($isAdmin || $isDelegatedAdmin) {
236
-			$users = $this->userManager->getDisabledUsers($limit, $offset, $search);
237
-			$users = array_map(fn (IUser $user): string => $user->getUID(), $users);
238
-		} elseif ($subAdminManager->isSubAdmin($currentUser)) {
239
-			$subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
240
-
241
-			$users = [];
242
-			/* We have to handle offset ourselve for correctness */
243
-			$tempLimit = ($limit === null ? null : $limit + $offset);
244
-			foreach ($subAdminOfGroups as $group) {
245
-				$users = array_unique(array_merge(
246
-					$users,
247
-					array_map(
248
-						fn (IUser $user): string => $user->getUID(),
249
-						array_filter(
250
-							$group->searchUsers($search),
251
-							fn (IUser $user): bool => !$user->isEnabled()
252
-						)
253
-					)
254
-				));
255
-				if (($tempLimit !== null) && (count($users) >= $tempLimit)) {
256
-					break;
257
-				}
258
-			}
259
-			$users = array_slice($users, $offset, $limit);
260
-		}
261
-
262
-		$usersDetails = [];
263
-		foreach ($users as $userId) {
264
-			try {
265
-				$userData = $this->getUserData($userId);
266
-			} catch (OCSNotFoundException $e) {
267
-				// We still want to return all other accounts, but this one was removed from the backends
268
-				// yet they are still in our database. Might be a LDAP remnant.
269
-				$userData = null;
270
-				$this->logger->warning('Found one disabled account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
271
-			}
272
-			// Do not insert empty entry
273
-			if ($userData !== null) {
274
-				$usersDetails[$userId] = $userData;
275
-			} else {
276
-				// Currently logged in user does not have permissions to see this user
277
-				// only showing its id
278
-				$usersDetails[$userId] = ['id' => $userId];
279
-			}
280
-		}
281
-
282
-		return new DataResponse([
283
-			'users' => $usersDetails
284
-		]);
285
-	}
286
-
287
-	/**
288
-	 * Gets the list of users sorted by lastLogin, from most recent to least recent
289
-	 *
290
-	 * @param string $search Text to search for
291
-	 * @param ?int $limit Limit the amount of users returned
292
-	 * @param int $offset Offset
293
-	 * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
294
-	 *
295
-	 * 200: Users details returned based on last logged in information
296
-	 */
297
-	#[AuthorizedAdminSetting(settings:Users::class)]
298
-	public function getLastLoggedInUsers(string $search = '',
299
-		?int $limit = null,
300
-		int $offset = 0,
301
-	): DataResponse {
302
-		$currentUser = $this->userSession->getUser();
303
-		if ($currentUser === null) {
304
-			return new DataResponse(['users' => []]);
305
-		}
306
-		if ($limit !== null && $limit < 0) {
307
-			throw new InvalidArgumentException("Invalid limit value: $limit");
308
-		}
309
-		if ($offset < 0) {
310
-			throw new InvalidArgumentException("Invalid offset value: $offset");
311
-		}
312
-
313
-		$users = [];
314
-
315
-		// For Admin alone user sorting based on lastLogin. For sub admin and groups this is not supported
316
-		$users = $this->userManager->getLastLoggedInUsers($limit, $offset, $search);
317
-
318
-		$usersDetails = [];
319
-		foreach ($users as $userId) {
320
-			try {
321
-				$userData = $this->getUserData($userId);
322
-			} catch (OCSNotFoundException $e) {
323
-				// We still want to return all other accounts, but this one was removed from the backends
324
-				// yet they are still in our database. Might be a LDAP remnant.
325
-				$userData = null;
326
-				$this->logger->warning('Found one account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
327
-			}
328
-			// Do not insert empty entry
329
-			if ($userData !== null) {
330
-				$usersDetails[$userId] = $userData;
331
-			} else {
332
-				// Currently logged-in user does not have permissions to see this user
333
-				// only showing its id
334
-				$usersDetails[$userId] = ['id' => $userId];
335
-			}
336
-		}
337
-
338
-		return new DataResponse([
339
-			'users' => $usersDetails
340
-		]);
341
-	}
342
-
343
-
344
-
345
-	/**
346
-	 * @NoSubAdminRequired
347
-	 *
348
-	 * Search users by their phone numbers
349
-	 *
350
-	 * @param string $location Location of the phone number (for country code)
351
-	 * @param array<string, list<string>> $search Phone numbers to search for
352
-	 * @return DataResponse<Http::STATUS_OK, array<string, string>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, list<empty>, array{}>
353
-	 *
354
-	 * 200: Users returned
355
-	 * 400: Invalid location
356
-	 */
357
-	#[NoAdminRequired]
358
-	public function searchByPhoneNumbers(string $location, array $search): DataResponse {
359
-		if ($this->phoneNumberUtil->getCountryCodeForRegion($location) === null) {
360
-			// Not a valid region code
361
-			return new DataResponse([], Http::STATUS_BAD_REQUEST);
362
-		}
363
-
364
-		/** @var IUser $user */
365
-		$user = $this->userSession->getUser();
366
-		$knownTo = $user->getUID();
367
-		$defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
368
-
369
-		$normalizedNumberToKey = [];
370
-		foreach ($search as $key => $phoneNumbers) {
371
-			foreach ($phoneNumbers as $phone) {
372
-				$normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $location);
373
-				if ($normalizedNumber !== null) {
374
-					$normalizedNumberToKey[$normalizedNumber] = (string)$key;
375
-				}
376
-
377
-				if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && str_starts_with($phone, '0')) {
378
-					// If the number has a leading zero (no country code),
379
-					// we also check the default phone region of the instance,
380
-					// when it's different to the user's given region.
381
-					$normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $defaultPhoneRegion);
382
-					if ($normalizedNumber !== null) {
383
-						$normalizedNumberToKey[$normalizedNumber] = (string)$key;
384
-					}
385
-				}
386
-			}
387
-		}
388
-
389
-		$phoneNumbers = array_keys($normalizedNumberToKey);
390
-
391
-		if (empty($phoneNumbers)) {
392
-			return new DataResponse();
393
-		}
394
-
395
-		// Cleanup all previous entries and only allow new matches
396
-		$this->knownUserService->deleteKnownTo($knownTo);
397
-
398
-		$userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
399
-
400
-		if (empty($userMatches)) {
401
-			return new DataResponse();
402
-		}
403
-
404
-		$cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
405
-		if (strpos($cloudUrl, 'http://') === 0) {
406
-			$cloudUrl = substr($cloudUrl, strlen('http://'));
407
-		} elseif (strpos($cloudUrl, 'https://') === 0) {
408
-			$cloudUrl = substr($cloudUrl, strlen('https://'));
409
-		}
410
-
411
-		$matches = [];
412
-		foreach ($userMatches as $phone => $userId) {
413
-			// Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
414
-			$matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
415
-			$this->knownUserService->storeIsKnownToUser($knownTo, $userId);
416
-		}
417
-
418
-		return new DataResponse($matches);
419
-	}
420
-
421
-	/**
422
-	 * @throws OCSException
423
-	 */
424
-	private function createNewUserId(): string {
425
-		$attempts = 0;
426
-		do {
427
-			$uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
428
-			if (!$this->userManager->userExists($uidCandidate)) {
429
-				return $uidCandidate;
430
-			}
431
-			$attempts++;
432
-		} while ($attempts < 10);
433
-		throw new OCSException($this->l10n->t('Could not create non-existing user ID'), 111);
434
-	}
435
-
436
-	/**
437
-	 * Create a new user
438
-	 *
439
-	 * @param string $userid ID of the user
440
-	 * @param string $password Password of the user
441
-	 * @param string $displayName Display name of the user
442
-	 * @param string $email Email of the user
443
-	 * @param list<string> $groups Groups of the user
444
-	 * @param list<string> $subadmin Groups where the user is subadmin
445
-	 * @param string $quota Quota of the user
446
-	 * @param string $language Language of the user
447
-	 * @param ?string $manager Manager of the user
448
-	 * @return DataResponse<Http::STATUS_OK, array{id: string}, array{}>
449
-	 * @throws OCSException
450
-	 * @throws OCSForbiddenException Missing permissions to make user subadmin
451
-	 *
452
-	 * 200: User added successfully
453
-	 */
454
-	#[PasswordConfirmationRequired]
455
-	#[NoAdminRequired]
456
-	public function addUser(
457
-		string $userid,
458
-		string $password = '',
459
-		string $displayName = '',
460
-		string $email = '',
461
-		array $groups = [],
462
-		array $subadmin = [],
463
-		string $quota = '',
464
-		string $language = '',
465
-		?string $manager = null,
466
-	): DataResponse {
467
-		$user = $this->userSession->getUser();
468
-		$isAdmin = $this->groupManager->isAdmin($user->getUID());
469
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($user->getUID());
470
-		$subAdminManager = $this->groupManager->getSubAdmin();
471
-
472
-		if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
473
-			$userid = $this->createNewUserId();
474
-		}
475
-
476
-		if ($this->userManager->userExists($userid)) {
477
-			$this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
478
-			throw new OCSException($this->l10n->t('User already exists'), 102);
479
-		}
480
-
481
-		if ($groups !== []) {
482
-			foreach ($groups as $group) {
483
-				if (!$this->groupManager->groupExists($group)) {
484
-					throw new OCSException($this->l10n->t('Group %1$s does not exist', [$group]), 104);
485
-				}
486
-				if (!$isAdmin && !($isDelegatedAdmin && $group !== 'admin') && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
487
-					throw new OCSException($this->l10n->t('Insufficient privileges for group %1$s', [$group]), 105);
488
-				}
489
-			}
490
-		} else {
491
-			if (!$isAdmin && !$isDelegatedAdmin) {
492
-				throw new OCSException($this->l10n->t('No group specified (required for sub-admins)'), 106);
493
-			}
494
-		}
495
-
496
-		$subadminGroups = [];
497
-		if ($subadmin !== []) {
498
-			foreach ($subadmin as $groupid) {
499
-				$group = $this->groupManager->get($groupid);
500
-				// Check if group exists
501
-				if ($group === null) {
502
-					throw new OCSException($this->l10n->t('Sub-admin group does not exist'), 109);
503
-				}
504
-				// Check if trying to make subadmin of admin group
505
-				if ($group->getGID() === 'admin') {
506
-					throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
507
-				}
508
-				// Check if has permission to promote subadmins
509
-				if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin && !$isDelegatedAdmin) {
510
-					throw new OCSForbiddenException($this->l10n->t('No permissions to promote sub-admins'));
511
-				}
512
-				$subadminGroups[] = $group;
513
-			}
514
-		}
515
-
516
-		$generatePasswordResetToken = false;
517
-		if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
518
-			throw new OCSException($this->l10n->t('Invalid password value'), 101);
519
-		}
520
-		if ($password === '') {
521
-			if ($email === '') {
522
-				throw new OCSException($this->l10n->t('An email address is required, to send a password link to the user.'), 108);
523
-			}
524
-
525
-			$passwordEvent = new GenerateSecurePasswordEvent();
526
-			$this->eventDispatcher->dispatchTyped($passwordEvent);
527
-
528
-			$password = $passwordEvent->getPassword();
529
-			if ($password === null) {
530
-				// Fallback: ensure to pass password_policy in any case
531
-				$password = $this->secureRandom->generate(10)
532
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
533
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
534
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
535
-					. $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
536
-			}
537
-			$generatePasswordResetToken = true;
538
-		}
539
-
540
-		if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
541
-			throw new OCSException($this->l10n->t('Required email address was not provided'), 110);
542
-		}
543
-
544
-		try {
545
-			$newUser = $this->userManager->createUser($userid, $password);
546
-			$this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
547
-
548
-			foreach ($groups as $group) {
549
-				$this->groupManager->get($group)->addUser($newUser);
550
-				$this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
551
-			}
552
-			foreach ($subadminGroups as $group) {
553
-				$subAdminManager->createSubAdmin($newUser, $group);
554
-			}
555
-
556
-			if ($displayName !== '') {
557
-				try {
558
-					$this->editUser($userid, self::USER_FIELD_DISPLAYNAME, $displayName);
559
-				} catch (OCSException $e) {
560
-					if ($newUser instanceof IUser) {
561
-						$newUser->delete();
562
-					}
563
-					throw $e;
564
-				}
565
-			}
566
-
567
-			if ($quota !== '') {
568
-				$this->editUser($userid, self::USER_FIELD_QUOTA, $quota);
569
-			}
570
-
571
-			if ($language !== '') {
572
-				$this->editUser($userid, self::USER_FIELD_LANGUAGE, $language);
573
-			}
574
-
575
-			/**
576
-			 * null -> nothing sent
577
-			 * '' -> unset manager
578
-			 * else -> set manager
579
-			 */
580
-			if ($manager !== null) {
581
-				$this->editUser($userid, self::USER_FIELD_MANAGER, $manager);
582
-			}
583
-
584
-			// Send new user mail only if a mail is set
585
-			if ($email !== '') {
586
-				$newUser->setEMailAddress($email);
587
-				if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
588
-					try {
589
-						$emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
590
-						$this->newUserMailHelper->sendMail($newUser, $emailTemplate);
591
-					} catch (\Exception $e) {
592
-						// Mail could be failing hard or just be plain not configured
593
-						// Logging error as it is the hardest of the two
594
-						$this->logger->error(
595
-							"Unable to send the invitation mail to $email",
596
-							[
597
-								'app' => 'ocs_api',
598
-								'exception' => $e,
599
-							]
600
-						);
601
-					}
602
-				}
603
-			}
604
-
605
-			return new DataResponse(['id' => $userid]);
606
-		} catch (HintException $e) {
607
-			$this->logger->warning(
608
-				'Failed addUser attempt with hint exception.',
609
-				[
610
-					'app' => 'ocs_api',
611
-					'exception' => $e,
612
-				]
613
-			);
614
-			throw new OCSException($e->getHint(), 107);
615
-		} catch (OCSException $e) {
616
-			$this->logger->warning(
617
-				'Failed addUser attempt with ocs exception.',
618
-				[
619
-					'app' => 'ocs_api',
620
-					'exception' => $e,
621
-				]
622
-			);
623
-			throw $e;
624
-		} catch (InvalidArgumentException $e) {
625
-			$this->logger->error(
626
-				'Failed addUser attempt with invalid argument exception.',
627
-				[
628
-					'app' => 'ocs_api',
629
-					'exception' => $e,
630
-				]
631
-			);
632
-			throw new OCSException($e->getMessage(), 101);
633
-		} catch (\Exception $e) {
634
-			$this->logger->error(
635
-				'Failed addUser attempt with exception.',
636
-				[
637
-					'app' => 'ocs_api',
638
-					'exception' => $e
639
-				]
640
-			);
641
-			throw new OCSException('Bad request', 101);
642
-		}
643
-	}
644
-
645
-	/**
646
-	 * @NoSubAdminRequired
647
-	 *
648
-	 * Get the details of a user
649
-	 *
650
-	 * @param string $userId ID of the user
651
-	 * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
652
-	 * @throws OCSException
653
-	 *
654
-	 * 200: User returned
655
-	 */
656
-	#[NoAdminRequired]
657
-	public function getUser(string $userId): DataResponse {
658
-		$includeScopes = false;
659
-		$currentUser = $this->userSession->getUser();
660
-		if ($currentUser && $currentUser->getUID() === $userId) {
661
-			$includeScopes = true;
662
-		}
663
-
664
-		$data = $this->getUserData($userId, $includeScopes);
665
-		// getUserData returns null if not enough permissions
666
-		if ($data === null) {
667
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
668
-		}
669
-		return new DataResponse($data);
670
-	}
671
-
672
-	/**
673
-	 * @NoSubAdminRequired
674
-	 *
675
-	 * Get the details of the current user
676
-	 *
677
-	 * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
678
-	 * @throws OCSException
679
-	 *
680
-	 * 200: Current user returned
681
-	 */
682
-	#[NoAdminRequired]
683
-	public function getCurrentUser(): DataResponse {
684
-		$user = $this->userSession->getUser();
685
-		if ($user) {
686
-			/** @var Provisioning_APIUserDetails $data */
687
-			$data = $this->getUserData($user->getUID(), true);
688
-			return new DataResponse($data);
689
-		}
690
-
691
-		throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
692
-	}
693
-
694
-	/**
695
-	 * @NoSubAdminRequired
696
-	 *
697
-	 * Get a list of fields that are editable for the current user
698
-	 *
699
-	 * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
700
-	 * @throws OCSException
701
-	 *
702
-	 * 200: Editable fields returned
703
-	 */
704
-	#[NoAdminRequired]
705
-	public function getEditableFields(): DataResponse {
706
-		$currentLoggedInUser = $this->userSession->getUser();
707
-		if (!$currentLoggedInUser instanceof IUser) {
708
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
709
-		}
710
-
711
-		return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
712
-	}
713
-
714
-	/**
715
-	 * Get a list of enabled apps for the current user
716
-	 *
717
-	 * @return DataResponse<Http::STATUS_OK, array{apps: list<string>}, array{}>
718
-	 *
719
-	 * 200: Enabled apps returned
720
-	 */
721
-	#[NoAdminRequired]
722
-	public function getEnabledApps(): DataResponse {
723
-		$currentLoggedInUser = $this->userSession->getUser();
724
-		return new DataResponse(['apps' => $this->appManager->getEnabledAppsForUser($currentLoggedInUser)]);
725
-	}
726
-
727
-	/**
728
-	 * @NoSubAdminRequired
729
-	 *
730
-	 * Get a list of fields that are editable for a user
731
-	 *
732
-	 * @param string $userId ID of the user
733
-	 * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
734
-	 * @throws OCSException
735
-	 *
736
-	 * 200: Editable fields for user returned
737
-	 */
738
-	#[NoAdminRequired]
739
-	public function getEditableFieldsForUser(string $userId): DataResponse {
740
-		$currentLoggedInUser = $this->userSession->getUser();
741
-		if (!$currentLoggedInUser instanceof IUser) {
742
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
743
-		}
744
-
745
-		$permittedFields = [];
746
-
747
-		if ($userId !== $currentLoggedInUser->getUID()) {
748
-			$targetUser = $this->userManager->get($userId);
749
-			if (!$targetUser instanceof IUser) {
750
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
751
-			}
752
-
753
-			$subAdminManager = $this->groupManager->getSubAdmin();
754
-			$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
755
-			$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
756
-			if (
757
-				!($isAdmin || $isDelegatedAdmin)
758
-				&& !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
759
-			) {
760
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
761
-			}
762
-		} else {
763
-			$targetUser = $currentLoggedInUser;
764
-		}
765
-
766
-		$allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
767
-		if ($allowDisplayNameChange === true && (
768
-			$targetUser->getBackend() instanceof ISetDisplayNameBackend
769
-			|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
770
-		)) {
771
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
772
-		}
773
-
774
-		// Fallback to display name value to avoid changing behavior with the new option.
775
-		if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
776
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
777
-		}
778
-
779
-		$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
780
-		$permittedFields[] = IAccountManager::PROPERTY_PHONE;
781
-		$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
782
-		$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
783
-		$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
784
-		$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
785
-		$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
786
-		$permittedFields[] = IAccountManager::PROPERTY_ROLE;
787
-		$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
788
-		$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
789
-		$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
790
-		$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
791
-
792
-		return new DataResponse($permittedFields);
793
-	}
794
-
795
-	/**
796
-	 * @NoSubAdminRequired
797
-	 *
798
-	 * Update multiple values of the user's details
799
-	 *
800
-	 * @param string $userId ID of the user
801
-	 * @param string $collectionName Collection to update
802
-	 * @param string $key Key that will be updated
803
-	 * @param string $value New value for the key
804
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
805
-	 * @throws OCSException
806
-	 *
807
-	 * 200: User values edited successfully
808
-	 */
809
-	#[PasswordConfirmationRequired]
810
-	#[NoAdminRequired]
811
-	#[UserRateLimit(limit: 5, period: 60)]
812
-	public function editUserMultiValue(
813
-		string $userId,
814
-		string $collectionName,
815
-		string $key,
816
-		string $value,
817
-	): DataResponse {
818
-		$currentLoggedInUser = $this->userSession->getUser();
819
-		if ($currentLoggedInUser === null) {
820
-			throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
821
-		}
822
-
823
-		$targetUser = $this->userManager->get($userId);
824
-		if ($targetUser === null) {
825
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
826
-		}
827
-
828
-		$subAdminManager = $this->groupManager->getSubAdmin();
829
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
830
-		$isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID())
831
-			|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser);
832
-
833
-		$permittedFields = [];
834
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
835
-			// Editing self (display, email)
836
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
837
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
838
-		} else {
839
-			// Check if admin / subadmin
840
-			if ($isAdminOrSubadmin || $isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) {
841
-				// They have permissions over the user
842
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
843
-			} else {
844
-				// No rights
845
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
846
-			}
847
-		}
848
-
849
-		// Check if permitted to edit this field
850
-		if (!in_array($collectionName, $permittedFields)) {
851
-			throw new OCSException('', 103);
852
-		}
853
-
854
-		switch ($collectionName) {
855
-			case IAccountManager::COLLECTION_EMAIL:
856
-				$userAccount = $this->accountManager->getAccount($targetUser);
857
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
858
-				$mailCollection->removePropertyByValue($key);
859
-				if ($value !== '') {
860
-					$mailCollection->addPropertyWithDefaults($value);
861
-					$property = $mailCollection->getPropertyByValue($key);
862
-					if ($isAdminOrSubadmin && $property) {
863
-						// admin set mails are auto-verified
864
-						$property->setLocallyVerified(IAccountManager::VERIFIED);
865
-					}
866
-				}
867
-				$this->accountManager->updateAccount($userAccount);
868
-				if ($value === '' && $key === $targetUser->getPrimaryEMailAddress()) {
869
-					$targetUser->setPrimaryEMailAddress('');
870
-				}
871
-				break;
872
-
873
-			case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
874
-				$userAccount = $this->accountManager->getAccount($targetUser);
875
-				$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
876
-				$targetProperty = null;
877
-				foreach ($mailCollection->getProperties() as $property) {
878
-					if ($property->getValue() === $key) {
879
-						$targetProperty = $property;
880
-						break;
881
-					}
882
-				}
883
-				if ($targetProperty instanceof IAccountProperty) {
884
-					try {
885
-						$targetProperty->setScope($value);
886
-						$this->accountManager->updateAccount($userAccount);
887
-					} catch (InvalidArgumentException $e) {
888
-						throw new OCSException('', 102);
889
-					}
890
-				} else {
891
-					throw new OCSException('', 102);
892
-				}
893
-				break;
894
-
895
-			default:
896
-				throw new OCSException('', 103);
897
-		}
898
-		return new DataResponse();
899
-	}
900
-
901
-	/**
902
-	 * @NoSubAdminRequired
903
-	 *
904
-	 * Update a value of the user's details
905
-	 *
906
-	 * @param string $userId ID of the user
907
-	 * @param string $key Key that will be updated
908
-	 * @param string $value New value for the key
909
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
910
-	 * @throws OCSException
911
-	 *
912
-	 * 200: User value edited successfully
913
-	 */
914
-	#[PasswordConfirmationRequired]
915
-	#[NoAdminRequired]
916
-	#[UserRateLimit(limit: 50, period: 600)]
917
-	public function editUser(string $userId, string $key, string $value): DataResponse {
918
-		$currentLoggedInUser = $this->userSession->getUser();
919
-
920
-		$targetUser = $this->userManager->get($userId);
921
-		if ($targetUser === null) {
922
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
923
-		}
924
-
925
-		$permittedFields = [];
926
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
927
-			$allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
928
-			if ($allowDisplayNameChange !== false && (
929
-				$targetUser->getBackend() instanceof ISetDisplayNameBackend
930
-				|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
931
-			)) {
932
-				$permittedFields[] = self::USER_FIELD_DISPLAYNAME;
933
-				$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
934
-			}
935
-
936
-			// Fallback to display name value to avoid changing behavior with the new option.
937
-			if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
938
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
939
-			}
940
-
941
-			$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
942
-			$permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
943
-
944
-			$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
945
-
946
-			$permittedFields[] = self::USER_FIELD_PASSWORD;
947
-			$permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
948
-			if (
949
-				$this->config->getSystemValue('force_language', false) === false ||
950
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
951
-				$this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
952
-			) {
953
-				$permittedFields[] = self::USER_FIELD_LANGUAGE;
954
-			}
955
-
956
-			if (
957
-				$this->config->getSystemValue('force_locale', false) === false ||
958
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
959
-				$this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
960
-			) {
961
-				$permittedFields[] = self::USER_FIELD_LOCALE;
962
-				$permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
963
-			}
964
-
965
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE;
966
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
967
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
968
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
969
-			$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
970
-			$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
971
-			$permittedFields[] = IAccountManager::PROPERTY_ROLE;
972
-			$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
973
-			$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
974
-			$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
975
-			$permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE;
976
-			$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
977
-
978
-			$permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
979
-			$permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
980
-			$permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
981
-			$permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
982
-			$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX;
983
-			$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX;
984
-			$permittedFields[] = IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX;
985
-			$permittedFields[] = IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX;
986
-			$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX;
987
-			$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX;
988
-			$permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX;
989
-			$permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
990
-			$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX;
991
-
992
-			// If admin they can edit their own quota and manager
993
-			$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
994
-			$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
995
-			if ($isAdmin || $isDelegatedAdmin) {
996
-				$permittedFields[] = self::USER_FIELD_QUOTA;
997
-				$permittedFields[] = self::USER_FIELD_MANAGER;
998
-			}
999
-		} else {
1000
-			// Check if admin / subadmin
1001
-			$subAdminManager = $this->groupManager->getSubAdmin();
1002
-			if (
1003
-				$this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
1004
-				$this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID()) && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')
1005
-				|| $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1006
-			) {
1007
-				// They have permissions over the user
1008
-				if (
1009
-					$targetUser->getBackend() instanceof ISetDisplayNameBackend
1010
-					|| $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
1011
-				) {
1012
-					$permittedFields[] = self::USER_FIELD_DISPLAYNAME;
1013
-					$permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
1014
-				}
1015
-				$permittedFields[] = IAccountManager::PROPERTY_EMAIL;
1016
-				$permittedFields[] = IAccountManager::COLLECTION_EMAIL;
1017
-				$permittedFields[] = self::USER_FIELD_PASSWORD;
1018
-				$permittedFields[] = self::USER_FIELD_LANGUAGE;
1019
-				$permittedFields[] = self::USER_FIELD_LOCALE;
1020
-				$permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
1021
-				$permittedFields[] = IAccountManager::PROPERTY_PHONE;
1022
-				$permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
1023
-				$permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
1024
-				$permittedFields[] = IAccountManager::PROPERTY_TWITTER;
1025
-				$permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
1026
-				$permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
1027
-				$permittedFields[] = IAccountManager::PROPERTY_ROLE;
1028
-				$permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
1029
-				$permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
1030
-				$permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
1031
-				$permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
1032
-				$permittedFields[] = self::USER_FIELD_QUOTA;
1033
-				$permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
1034
-				$permittedFields[] = self::USER_FIELD_MANAGER;
1035
-			} else {
1036
-				// No rights
1037
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1038
-			}
1039
-		}
1040
-		// Check if permitted to edit this field
1041
-		if (!in_array($key, $permittedFields)) {
1042
-			throw new OCSException('', 113);
1043
-		}
1044
-		// Process the edit
1045
-		switch ($key) {
1046
-			case self::USER_FIELD_DISPLAYNAME:
1047
-			case IAccountManager::PROPERTY_DISPLAYNAME:
1048
-				try {
1049
-					$targetUser->setDisplayName($value);
1050
-				} catch (InvalidArgumentException $e) {
1051
-					throw new OCSException($e->getMessage(), 101);
1052
-				}
1053
-				break;
1054
-			case self::USER_FIELD_QUOTA:
1055
-				$quota = $value;
1056
-				if ($quota !== 'none' && $quota !== 'default') {
1057
-					if (is_numeric($quota)) {
1058
-						$quota = (float)$quota;
1059
-					} else {
1060
-						$quota = Util::computerFileSize($quota);
1061
-					}
1062
-					if ($quota === false) {
1063
-						throw new OCSException($this->l10n->t('Invalid quota value: %1$s', [$value]), 101);
1064
-					}
1065
-					if ($quota === -1) {
1066
-						$quota = 'none';
1067
-					} else {
1068
-						$maxQuota = (int)$this->config->getAppValue('files', 'max_quota', '-1');
1069
-						if ($maxQuota !== -1 && $quota > $maxQuota) {
1070
-							throw new OCSException($this->l10n->t('Invalid quota value. %1$s is exceeding the maximum quota', [$value]), 101);
1071
-						}
1072
-						$quota = Util::humanFileSize($quota);
1073
-					}
1074
-				}
1075
-				// no else block because quota can be set to 'none' in previous if
1076
-				if ($quota === 'none') {
1077
-					$allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
1078
-					if (!$allowUnlimitedQuota) {
1079
-						throw new OCSException($this->l10n->t('Unlimited quota is forbidden on this instance'), 101);
1080
-					}
1081
-				}
1082
-				$targetUser->setQuota($quota);
1083
-				break;
1084
-			case self::USER_FIELD_MANAGER:
1085
-				$targetUser->setManagerUids([$value]);
1086
-				break;
1087
-			case self::USER_FIELD_PASSWORD:
1088
-				try {
1089
-					if (strlen($value) > IUserManager::MAX_PASSWORD_LENGTH) {
1090
-						throw new OCSException($this->l10n->t('Invalid password value'), 101);
1091
-					}
1092
-					if (!$targetUser->canChangePassword()) {
1093
-						throw new OCSException($this->l10n->t('Setting the password is not supported by the users backend'), 112);
1094
-					}
1095
-					$targetUser->setPassword($value);
1096
-				} catch (HintException $e) { // password policy error
1097
-					throw new OCSException($e->getHint(), 107);
1098
-				}
1099
-				break;
1100
-			case self::USER_FIELD_LANGUAGE:
1101
-				$languagesCodes = $this->l10nFactory->findAvailableLanguages();
1102
-				if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
1103
-					throw new OCSException($this->l10n->t('Invalid language'), 101);
1104
-				}
1105
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
1106
-				break;
1107
-			case self::USER_FIELD_LOCALE:
1108
-				if (!$this->l10nFactory->localeExists($value)) {
1109
-					throw new OCSException($this->l10n->t('Invalid locale'), 101);
1110
-				}
1111
-				$this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
1112
-				break;
1113
-			case self::USER_FIELD_FIRST_DAY_OF_WEEK:
1114
-				$intValue = (int)$value;
1115
-				if ($intValue < -1 || $intValue > 6) {
1116
-					throw new OCSException($this->l10n->t('Invalid first day of week'), 101);
1117
-				}
1118
-				if ($intValue === -1) {
1119
-					$this->config->deleteUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK);
1120
-				} else {
1121
-					$this->config->setUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK, $value);
1122
-				}
1123
-				break;
1124
-			case self::USER_FIELD_NOTIFICATION_EMAIL:
1125
-				$success = false;
1126
-				if ($value === '' || filter_var($value, FILTER_VALIDATE_EMAIL)) {
1127
-					try {
1128
-						$targetUser->setPrimaryEMailAddress($value);
1129
-						$success = true;
1130
-					} catch (InvalidArgumentException $e) {
1131
-						$this->logger->info(
1132
-							'Cannot set primary email, because provided address is not verified',
1133
-							[
1134
-								'app' => 'provisioning_api',
1135
-								'exception' => $e,
1136
-							]
1137
-						);
1138
-					}
1139
-				}
1140
-				if (!$success) {
1141
-					throw new OCSException('', 101);
1142
-				}
1143
-				break;
1144
-			case IAccountManager::PROPERTY_EMAIL:
1145
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
1146
-					$targetUser->setEMailAddress($value);
1147
-				} else {
1148
-					throw new OCSException('', 101);
1149
-				}
1150
-				break;
1151
-			case IAccountManager::COLLECTION_EMAIL:
1152
-				if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getSystemEMailAddress()) {
1153
-					$userAccount = $this->accountManager->getAccount($targetUser);
1154
-					$mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
1155
-
1156
-					if ($mailCollection->getPropertyByValue($value)) {
1157
-						throw new OCSException('', 101);
1158
-					}
1159
-
1160
-					$mailCollection->addPropertyWithDefaults($value);
1161
-					$this->accountManager->updateAccount($userAccount);
1162
-				} else {
1163
-					throw new OCSException('', 101);
1164
-				}
1165
-				break;
1166
-			case IAccountManager::PROPERTY_PHONE:
1167
-			case IAccountManager::PROPERTY_ADDRESS:
1168
-			case IAccountManager::PROPERTY_WEBSITE:
1169
-			case IAccountManager::PROPERTY_TWITTER:
1170
-			case IAccountManager::PROPERTY_FEDIVERSE:
1171
-			case IAccountManager::PROPERTY_ORGANISATION:
1172
-			case IAccountManager::PROPERTY_ROLE:
1173
-			case IAccountManager::PROPERTY_HEADLINE:
1174
-			case IAccountManager::PROPERTY_BIOGRAPHY:
1175
-			case IAccountManager::PROPERTY_BIRTHDATE:
1176
-			case IAccountManager::PROPERTY_PRONOUNS:
1177
-				$userAccount = $this->accountManager->getAccount($targetUser);
1178
-				try {
1179
-					$userProperty = $userAccount->getProperty($key);
1180
-					if ($userProperty->getValue() !== $value) {
1181
-						try {
1182
-							$userProperty->setValue($value);
1183
-							if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
1184
-								$this->knownUserService->deleteByContactUserId($targetUser->getUID());
1185
-							}
1186
-						} catch (InvalidArgumentException $e) {
1187
-							throw new OCSException('Invalid ' . $e->getMessage(), 101);
1188
-						}
1189
-					}
1190
-				} catch (PropertyDoesNotExistException $e) {
1191
-					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
1192
-				}
1193
-				try {
1194
-					$this->accountManager->updateAccount($userAccount);
1195
-				} catch (InvalidArgumentException $e) {
1196
-					throw new OCSException('Invalid ' . $e->getMessage(), 101);
1197
-				}
1198
-				break;
1199
-			case IAccountManager::PROPERTY_PROFILE_ENABLED:
1200
-				$userAccount = $this->accountManager->getAccount($targetUser);
1201
-				try {
1202
-					$userProperty = $userAccount->getProperty($key);
1203
-					if ($userProperty->getValue() !== $value) {
1204
-						$userProperty->setValue($value);
1205
-					}
1206
-				} catch (PropertyDoesNotExistException $e) {
1207
-					$userAccount->setProperty($key, $value, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
1208
-				}
1209
-				$this->accountManager->updateAccount($userAccount);
1210
-				break;
1211
-			case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
1212
-			case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
1213
-			case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
1214
-			case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
1215
-			case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
1216
-			case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
1217
-			case IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX:
1218
-			case IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX:
1219
-			case IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX:
1220
-			case IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX:
1221
-			case IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX:
1222
-			case IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX:
1223
-			case IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX:
1224
-			case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
1225
-			case IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX:
1226
-				$propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
1227
-				$userAccount = $this->accountManager->getAccount($targetUser);
1228
-				$userProperty = $userAccount->getProperty($propertyName);
1229
-				if ($userProperty->getScope() !== $value) {
1230
-					try {
1231
-						$userProperty->setScope($value);
1232
-						$this->accountManager->updateAccount($userAccount);
1233
-					} catch (InvalidArgumentException $e) {
1234
-						throw new OCSException('Invalid ' . $e->getMessage(), 101);
1235
-					}
1236
-				}
1237
-				break;
1238
-			default:
1239
-				throw new OCSException('', 113);
1240
-		}
1241
-		return new DataResponse();
1242
-	}
1243
-
1244
-	/**
1245
-	 * Wipe all devices of a user
1246
-	 *
1247
-	 * @param string $userId ID of the user
1248
-	 *
1249
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1250
-	 *
1251
-	 * @throws OCSException
1252
-	 *
1253
-	 * 200: Wiped all user devices successfully
1254
-	 */
1255
-	#[PasswordConfirmationRequired]
1256
-	#[NoAdminRequired]
1257
-	public function wipeUserDevices(string $userId): DataResponse {
1258
-		/** @var IUser $currentLoggedInUser */
1259
-		$currentLoggedInUser = $this->userSession->getUser();
1260
-
1261
-		$targetUser = $this->userManager->get($userId);
1262
-
1263
-		if ($targetUser === null) {
1264
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1265
-		}
1266
-
1267
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1268
-			throw new OCSException('', 101);
1269
-		}
1270
-
1271
-		// If not permitted
1272
-		$subAdminManager = $this->groupManager->getSubAdmin();
1273
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1274
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1275
-		if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1276
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1277
-		}
1278
-
1279
-		$this->remoteWipe->markAllTokensForWipe($targetUser);
1280
-
1281
-		return new DataResponse();
1282
-	}
1283
-
1284
-	/**
1285
-	 * Delete a user
1286
-	 *
1287
-	 * @param string $userId ID of the user
1288
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1289
-	 * @throws OCSException
1290
-	 *
1291
-	 * 200: User deleted successfully
1292
-	 */
1293
-	#[PasswordConfirmationRequired]
1294
-	#[NoAdminRequired]
1295
-	public function deleteUser(string $userId): DataResponse {
1296
-		$currentLoggedInUser = $this->userSession->getUser();
1297
-
1298
-		$targetUser = $this->userManager->get($userId);
1299
-
1300
-		if ($targetUser === null) {
1301
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1302
-		}
1303
-
1304
-		if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1305
-			throw new OCSException('', 101);
1306
-		}
1307
-
1308
-		// If not permitted
1309
-		$subAdminManager = $this->groupManager->getSubAdmin();
1310
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1311
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1312
-		if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1313
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1314
-		}
1315
-
1316
-		// Go ahead with the delete
1317
-		if ($targetUser->delete()) {
1318
-			return new DataResponse();
1319
-		} else {
1320
-			throw new OCSException('', 101);
1321
-		}
1322
-	}
1323
-
1324
-	/**
1325
-	 * Disable a user
1326
-	 *
1327
-	 * @param string $userId ID of the user
1328
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1329
-	 * @throws OCSException
1330
-	 *
1331
-	 * 200: User disabled successfully
1332
-	 */
1333
-	#[PasswordConfirmationRequired]
1334
-	#[NoAdminRequired]
1335
-	public function disableUser(string $userId): DataResponse {
1336
-		return $this->setEnabled($userId, false);
1337
-	}
1338
-
1339
-	/**
1340
-	 * Enable a user
1341
-	 *
1342
-	 * @param string $userId ID of the user
1343
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1344
-	 * @throws OCSException
1345
-	 *
1346
-	 * 200: User enabled successfully
1347
-	 */
1348
-	#[PasswordConfirmationRequired]
1349
-	#[NoAdminRequired]
1350
-	public function enableUser(string $userId): DataResponse {
1351
-		return $this->setEnabled($userId, true);
1352
-	}
1353
-
1354
-	/**
1355
-	 * @param string $userId
1356
-	 * @param bool $value
1357
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1358
-	 * @throws OCSException
1359
-	 */
1360
-	private function setEnabled(string $userId, bool $value): DataResponse {
1361
-		$currentLoggedInUser = $this->userSession->getUser();
1362
-
1363
-		$targetUser = $this->userManager->get($userId);
1364
-		if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1365
-			throw new OCSException('', 101);
1366
-		}
1367
-
1368
-		// If not permitted
1369
-		$subAdminManager = $this->groupManager->getSubAdmin();
1370
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1371
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1372
-		if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1373
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1374
-		}
1375
-
1376
-		// enable/disable the user now
1377
-		$targetUser->setEnabled($value);
1378
-		return new DataResponse();
1379
-	}
1380
-
1381
-	/**
1382
-	 * @NoSubAdminRequired
1383
-	 *
1384
-	 * Get a list of groups the user belongs to
1385
-	 *
1386
-	 * @param string $userId ID of the user
1387
-	 * @return DataResponse<Http::STATUS_OK, array{groups: list<string>}, array{}>
1388
-	 * @throws OCSException
1389
-	 *
1390
-	 * 200: Users groups returned
1391
-	 */
1392
-	#[NoAdminRequired]
1393
-	public function getUsersGroups(string $userId): DataResponse {
1394
-		$loggedInUser = $this->userSession->getUser();
1395
-
1396
-		$targetUser = $this->userManager->get($userId);
1397
-		if ($targetUser === null) {
1398
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1399
-		}
1400
-
1401
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1402
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1403
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1404
-			// Self lookup or admin lookup
1405
-			return new DataResponse([
1406
-				'groups' => $this->groupManager->getUserGroupIds($targetUser)
1407
-			]);
1408
-		} else {
1409
-			$subAdminManager = $this->groupManager->getSubAdmin();
1410
-
1411
-			// Looking up someone else
1412
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1413
-				// Return the group that the method caller is subadmin of for the user in question
1414
-				$groups = array_values(array_intersect(
1415
-					array_map(static fn (IGroup $group) => $group->getGID(), $subAdminManager->getSubAdminsGroups($loggedInUser)),
1416
-					$this->groupManager->getUserGroupIds($targetUser)
1417
-				));
1418
-				return new DataResponse(['groups' => $groups]);
1419
-			} else {
1420
-				// Not permitted
1421
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1422
-			}
1423
-		}
1424
-	}
1425
-
1426
-	/**
1427
-	 * @NoSubAdminRequired
1428
-	 *
1429
-	 * Get a list of groups with details
1430
-	 *
1431
-	 * @param string $userId ID of the user
1432
-	 * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1433
-	 * @throws OCSException
1434
-	 *
1435
-	 * 200: Users groups returned
1436
-	 */
1437
-	#[NoAdminRequired]
1438
-	public function getUsersGroupsDetails(string $userId): DataResponse {
1439
-		$loggedInUser = $this->userSession->getUser();
1440
-
1441
-		$targetUser = $this->userManager->get($userId);
1442
-		if ($targetUser === null) {
1443
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1444
-		}
1445
-
1446
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1447
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1448
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1449
-			// Self lookup or admin lookup
1450
-			$groups = array_map(
1451
-				function (Group $group) {
1452
-					return [
1453
-						'id' => $group->getGID(),
1454
-						'displayname' => $group->getDisplayName(),
1455
-						'usercount' => $group->count(),
1456
-						'disabled' => $group->countDisabled(),
1457
-						'canAdd' => $group->canAddUser(),
1458
-						'canRemove' => $group->canRemoveUser(),
1459
-					];
1460
-				},
1461
-				array_values($this->groupManager->getUserGroups($targetUser)),
1462
-			);
1463
-			return new DataResponse([
1464
-				'groups' => $groups,
1465
-			]);
1466
-		} else {
1467
-			$subAdminManager = $this->groupManager->getSubAdmin();
1468
-
1469
-			// Looking up someone else
1470
-			if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1471
-				// Return the group that the method caller is subadmin of for the user in question
1472
-				$gids = array_values(array_intersect(
1473
-					array_map(
1474
-						static fn (IGroup $group) => $group->getGID(),
1475
-						$subAdminManager->getSubAdminsGroups($loggedInUser),
1476
-					),
1477
-					$this->groupManager->getUserGroupIds($targetUser)
1478
-				));
1479
-				$groups = array_map(
1480
-					function (string $gid) {
1481
-						$group = $this->groupManager->get($gid);
1482
-						return [
1483
-							'id' => $group->getGID(),
1484
-							'displayname' => $group->getDisplayName(),
1485
-							'usercount' => $group->count(),
1486
-							'disabled' => $group->countDisabled(),
1487
-							'canAdd' => $group->canAddUser(),
1488
-							'canRemove' => $group->canRemoveUser(),
1489
-						];
1490
-					},
1491
-					$gids,
1492
-				);
1493
-				return new DataResponse([
1494
-					'groups' => $groups,
1495
-				]);
1496
-			} else {
1497
-				// Not permitted
1498
-				throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1499
-			}
1500
-		}
1501
-	}
1502
-
1503
-	/**
1504
-	 * @NoSubAdminRequired
1505
-	 *
1506
-	 * Get a list of the groups the user is a subadmin of, with details
1507
-	 *
1508
-	 * @param string $userId ID of the user
1509
-	 * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1510
-	 * @throws OCSException
1511
-	 *
1512
-	 * 200: Users subadmin groups returned
1513
-	 */
1514
-	#[NoAdminRequired]
1515
-	public function getUserSubAdminGroupsDetails(string $userId): DataResponse {
1516
-		$loggedInUser = $this->userSession->getUser();
1517
-
1518
-		$targetUser = $this->userManager->get($userId);
1519
-		if ($targetUser === null) {
1520
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1521
-		}
1522
-
1523
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1524
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1525
-		if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1526
-			$subAdminManager = $this->groupManager->getSubAdmin();
1527
-			$groups = array_map(
1528
-				function (IGroup $group) {
1529
-					return [
1530
-						'id' => $group->getGID(),
1531
-						'displayname' => $group->getDisplayName(),
1532
-						'usercount' => $group->count(),
1533
-						'disabled' => $group->countDisabled(),
1534
-						'canAdd' => $group->canAddUser(),
1535
-						'canRemove' => $group->canRemoveUser(),
1536
-					];
1537
-				},
1538
-				array_values($subAdminManager->getSubAdminsGroups($targetUser)),
1539
-			);
1540
-			return new DataResponse([
1541
-				'groups' => $groups,
1542
-			]);
1543
-		}
1544
-		throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1545
-	}
1546
-
1547
-	/**
1548
-	 * Add a user to a group
1549
-	 *
1550
-	 * @param string $userId ID of the user
1551
-	 * @param string $groupid ID of the group
1552
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1553
-	 * @throws OCSException
1554
-	 *
1555
-	 * 200: User added to group successfully
1556
-	 */
1557
-	#[PasswordConfirmationRequired]
1558
-	#[NoAdminRequired]
1559
-	public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1560
-		if ($groupid === '') {
1561
-			throw new OCSException('', 101);
1562
-		}
1563
-
1564
-		$group = $this->groupManager->get($groupid);
1565
-		$targetUser = $this->userManager->get($userId);
1566
-		if ($group === null) {
1567
-			throw new OCSException('', 102);
1568
-		}
1569
-		if ($targetUser === null) {
1570
-			throw new OCSException('', 103);
1571
-		}
1572
-
1573
-		// If they're not an admin, check they are a subadmin of the group in question
1574
-		$loggedInUser = $this->userSession->getUser();
1575
-		$subAdminManager = $this->groupManager->getSubAdmin();
1576
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1577
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1578
-		if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1579
-			throw new OCSException('', 104);
1580
-		}
1581
-
1582
-		// Add user to group
1583
-		$group->addUser($targetUser);
1584
-		return new DataResponse();
1585
-	}
1586
-
1587
-	/**
1588
-	 * Remove a user from a group
1589
-	 *
1590
-	 * @param string $userId ID of the user
1591
-	 * @param string $groupid ID of the group
1592
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1593
-	 * @throws OCSException
1594
-	 *
1595
-	 * 200: User removed from group successfully
1596
-	 */
1597
-	#[PasswordConfirmationRequired]
1598
-	#[NoAdminRequired]
1599
-	public function removeFromGroup(string $userId, string $groupid): DataResponse {
1600
-		$loggedInUser = $this->userSession->getUser();
1601
-
1602
-		if ($groupid === null || trim($groupid) === '') {
1603
-			throw new OCSException('', 101);
1604
-		}
1605
-
1606
-		$group = $this->groupManager->get($groupid);
1607
-		if ($group === null) {
1608
-			throw new OCSException('', 102);
1609
-		}
1610
-
1611
-		$targetUser = $this->userManager->get($userId);
1612
-		if ($targetUser === null) {
1613
-			throw new OCSException('', 103);
1614
-		}
1615
-
1616
-		// If they're not an admin, check they are a subadmin of the group in question
1617
-		$subAdminManager = $this->groupManager->getSubAdmin();
1618
-		$isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1619
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1620
-		if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1621
-			throw new OCSException('', 104);
1622
-		}
1623
-
1624
-		// Check they aren't removing themselves from 'admin' or their 'subadmin; group
1625
-		if ($targetUser->getUID() === $loggedInUser->getUID()) {
1626
-			if ($isAdmin || $isDelegatedAdmin) {
1627
-				if ($group->getGID() === 'admin') {
1628
-					throw new OCSException($this->l10n->t('Cannot remove yourself from the admin group'), 105);
1629
-				}
1630
-			} else {
1631
-				// Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1632
-				throw new OCSException($this->l10n->t('Cannot remove yourself from this group as you are a sub-admin'), 105);
1633
-			}
1634
-		} elseif (!($isAdmin || $isDelegatedAdmin)) {
1635
-			/** @var IGroup[] $subAdminGroups */
1636
-			$subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1637
-			$subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1638
-				return $subAdminGroup->getGID();
1639
-			}, $subAdminGroups);
1640
-			$userGroups = $this->groupManager->getUserGroupIds($targetUser);
1641
-			$userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1642
-
1643
-			if (count($userSubAdminGroups) <= 1) {
1644
-				// Subadmin must not be able to remove a user from all their subadmin groups.
1645
-				throw new OCSException($this->l10n->t('Not viable to remove user from the last group you are sub-admin of'), 105);
1646
-			}
1647
-		}
1648
-
1649
-		// Remove user from group
1650
-		$group->removeUser($targetUser);
1651
-		return new DataResponse();
1652
-	}
1653
-
1654
-	/**
1655
-	 * Make a user a subadmin of a group
1656
-	 *
1657
-	 * @param string $userId ID of the user
1658
-	 * @param string $groupid ID of the group
1659
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1660
-	 * @throws OCSException
1661
-	 *
1662
-	 * 200: User added as group subadmin successfully
1663
-	 */
1664
-	#[AuthorizedAdminSetting(settings:Users::class)]
1665
-	#[PasswordConfirmationRequired]
1666
-	public function addSubAdmin(string $userId, string $groupid): DataResponse {
1667
-		$group = $this->groupManager->get($groupid);
1668
-		$user = $this->userManager->get($userId);
1669
-
1670
-		// Check if the user exists
1671
-		if ($user === null) {
1672
-			throw new OCSException($this->l10n->t('User does not exist'), 101);
1673
-		}
1674
-		// Check if group exists
1675
-		if ($group === null) {
1676
-			throw new OCSException($this->l10n->t('Group does not exist'), 102);
1677
-		}
1678
-		// Check if trying to make subadmin of admin group
1679
-		if ($group->getGID() === 'admin') {
1680
-			throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
1681
-		}
1682
-
1683
-		$subAdminManager = $this->groupManager->getSubAdmin();
1684
-
1685
-		// We cannot be subadmin twice
1686
-		if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1687
-			return new DataResponse();
1688
-		}
1689
-		// Go
1690
-		$subAdminManager->createSubAdmin($user, $group);
1691
-		return new DataResponse();
1692
-	}
1693
-
1694
-	/**
1695
-	 * Remove a user from the subadmins of a group
1696
-	 *
1697
-	 * @param string $userId ID of the user
1698
-	 * @param string $groupid ID of the group
1699
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1700
-	 * @throws OCSException
1701
-	 *
1702
-	 * 200: User removed as group subadmin successfully
1703
-	 */
1704
-	#[AuthorizedAdminSetting(settings:Users::class)]
1705
-	#[PasswordConfirmationRequired]
1706
-	public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1707
-		$group = $this->groupManager->get($groupid);
1708
-		$user = $this->userManager->get($userId);
1709
-		$subAdminManager = $this->groupManager->getSubAdmin();
1710
-
1711
-		// Check if the user exists
1712
-		if ($user === null) {
1713
-			throw new OCSException($this->l10n->t('User does not exist'), 101);
1714
-		}
1715
-		// Check if the group exists
1716
-		if ($group === null) {
1717
-			throw new OCSException($this->l10n->t('Group does not exist'), 101);
1718
-		}
1719
-		// Check if they are a subadmin of this said group
1720
-		if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1721
-			throw new OCSException($this->l10n->t('User is not a sub-admin of this group'), 102);
1722
-		}
1723
-
1724
-		// Go
1725
-		$subAdminManager->deleteSubAdmin($user, $group);
1726
-		return new DataResponse();
1727
-	}
1728
-
1729
-	/**
1730
-	 * Get the groups a user is a subadmin of
1731
-	 *
1732
-	 * @param string $userId ID if the user
1733
-	 * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
1734
-	 * @throws OCSException
1735
-	 *
1736
-	 * 200: User subadmin groups returned
1737
-	 */
1738
-	#[AuthorizedAdminSetting(settings:Users::class)]
1739
-	public function getUserSubAdminGroups(string $userId): DataResponse {
1740
-		$groups = $this->getUserSubAdminGroupsData($userId);
1741
-		return new DataResponse($groups);
1742
-	}
1743
-
1744
-	/**
1745
-	 * Resend the welcome message
1746
-	 *
1747
-	 * @param string $userId ID if the user
1748
-	 * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1749
-	 * @throws OCSException
1750
-	 *
1751
-	 * 200: Resent welcome message successfully
1752
-	 */
1753
-	#[PasswordConfirmationRequired]
1754
-	#[NoAdminRequired]
1755
-	public function resendWelcomeMessage(string $userId): DataResponse {
1756
-		$currentLoggedInUser = $this->userSession->getUser();
1757
-
1758
-		$targetUser = $this->userManager->get($userId);
1759
-		if ($targetUser === null) {
1760
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1761
-		}
1762
-
1763
-		// Check if admin / subadmin
1764
-		$subAdminManager = $this->groupManager->getSubAdmin();
1765
-		$isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1766
-		$isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1767
-		if (
1768
-			!$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1769
-			&& !($isAdmin || $isDelegatedAdmin)
1770
-		) {
1771
-			// No rights
1772
-			throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1773
-		}
1774
-
1775
-		$email = $targetUser->getEMailAddress();
1776
-		if ($email === '' || $email === null) {
1777
-			throw new OCSException($this->l10n->t('Email address not available'), 101);
1778
-		}
1779
-
1780
-		try {
1781
-			if ($this->config->getUserValue($targetUser->getUID(), 'core', 'lostpassword')) {
1782
-				$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, true);
1783
-			} else {
1784
-				$emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1785
-			}
1786
-
1787
-			$this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1788
-		} catch (\Exception $e) {
1789
-			$this->logger->error(
1790
-				"Can't send new user mail to $email",
1791
-				[
1792
-					'app' => 'settings',
1793
-					'exception' => $e,
1794
-				]
1795
-			);
1796
-			throw new OCSException($this->l10n->t('Sending email failed'), 102);
1797
-		}
1798
-
1799
-		return new DataResponse();
1800
-	}
62
+    private IL10N $l10n;
63
+
64
+    public function __construct(
65
+        string $appName,
66
+        IRequest $request,
67
+        IUserManager $userManager,
68
+        IConfig $config,
69
+        IGroupManager $groupManager,
70
+        IUserSession $userSession,
71
+        IAccountManager $accountManager,
72
+        ISubAdmin $subAdminManager,
73
+        IFactory $l10nFactory,
74
+        IRootFolder $rootFolder,
75
+        private IURLGenerator $urlGenerator,
76
+        private LoggerInterface $logger,
77
+        private NewUserMailHelper $newUserMailHelper,
78
+        private ISecureRandom $secureRandom,
79
+        private RemoteWipe $remoteWipe,
80
+        private KnownUserService $knownUserService,
81
+        private IEventDispatcher $eventDispatcher,
82
+        private IPhoneNumberUtil $phoneNumberUtil,
83
+        private IAppManager $appManager,
84
+    ) {
85
+        parent::__construct(
86
+            $appName,
87
+            $request,
88
+            $userManager,
89
+            $config,
90
+            $groupManager,
91
+            $userSession,
92
+            $accountManager,
93
+            $subAdminManager,
94
+            $l10nFactory,
95
+            $rootFolder,
96
+        );
97
+
98
+        $this->l10n = $l10nFactory->get($appName);
99
+    }
100
+
101
+    /**
102
+     * Get a list of users
103
+     *
104
+     * @param string $search Text to search for
105
+     * @param int|null $limit Limit the amount of groups returned
106
+     * @param int $offset Offset for searching for groups
107
+     * @return DataResponse<Http::STATUS_OK, array{users: list<string>}, array{}>
108
+     *
109
+     * 200: Users returned
110
+     */
111
+    #[NoAdminRequired]
112
+    public function getUsers(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
113
+        $user = $this->userSession->getUser();
114
+        $users = [];
115
+
116
+        // Admin? Or SubAdmin?
117
+        $uid = $user->getUID();
118
+        $subAdminManager = $this->groupManager->getSubAdmin();
119
+        $isAdmin = $this->groupManager->isAdmin($uid);
120
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
121
+        if ($isAdmin || $isDelegatedAdmin) {
122
+            $users = $this->userManager->search($search, $limit, $offset);
123
+        } elseif ($subAdminManager->isSubAdmin($user)) {
124
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($user);
125
+            foreach ($subAdminOfGroups as $key => $group) {
126
+                $subAdminOfGroups[$key] = $group->getGID();
127
+            }
128
+
129
+            $users = [];
130
+            foreach ($subAdminOfGroups as $group) {
131
+                $users = array_merge($users, $this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
132
+            }
133
+        }
134
+
135
+        /** @var list<string> $users */
136
+        $users = array_keys($users);
137
+
138
+        return new DataResponse([
139
+            'users' => $users
140
+        ]);
141
+    }
142
+
143
+    /**
144
+     * Get a list of users and their details
145
+     *
146
+     * @param string $search Text to search for
147
+     * @param int|null $limit Limit the amount of groups returned
148
+     * @param int $offset Offset for searching for groups
149
+     * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
150
+     *
151
+     * 200: Users details returned
152
+     */
153
+    #[NoAdminRequired]
154
+    public function getUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
155
+        $currentUser = $this->userSession->getUser();
156
+        $users = [];
157
+
158
+        // Admin? Or SubAdmin?
159
+        $uid = $currentUser->getUID();
160
+        $subAdminManager = $this->groupManager->getSubAdmin();
161
+        $isAdmin = $this->groupManager->isAdmin($uid);
162
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
163
+        if ($isAdmin || $isDelegatedAdmin) {
164
+            $users = $this->userManager->search($search, $limit, $offset);
165
+            $users = array_keys($users);
166
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
167
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
168
+            foreach ($subAdminOfGroups as $key => $group) {
169
+                $subAdminOfGroups[$key] = $group->getGID();
170
+            }
171
+
172
+            $users = [];
173
+            foreach ($subAdminOfGroups as $group) {
174
+                $users[] = array_keys($this->groupManager->displayNamesInGroup($group, $search, $limit, $offset));
175
+            }
176
+            $users = array_merge(...$users);
177
+        }
178
+
179
+        $usersDetails = [];
180
+        foreach ($users as $userId) {
181
+            $userId = (string)$userId;
182
+            try {
183
+                $userData = $this->getUserData($userId);
184
+            } catch (OCSNotFoundException $e) {
185
+                // We still want to return all other accounts, but this one was removed from the backends
186
+                // yet they are still in our database. Might be a LDAP remnant.
187
+                $userData = null;
188
+                $this->logger->warning('Found one enabled account that is removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
189
+            }
190
+            // Do not insert empty entry
191
+            if ($userData !== null) {
192
+                $usersDetails[$userId] = $userData;
193
+            } else {
194
+                // Logged user does not have permissions to see this user
195
+                // only showing its id
196
+                $usersDetails[$userId] = ['id' => $userId];
197
+            }
198
+        }
199
+
200
+        return new DataResponse([
201
+            'users' => $usersDetails
202
+        ]);
203
+    }
204
+
205
+    /**
206
+     * Get the list of disabled users and their details
207
+     *
208
+     * @param string $search Text to search for
209
+     * @param ?int $limit Limit the amount of users returned
210
+     * @param int $offset Offset
211
+     * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
212
+     *
213
+     * 200: Disabled users details returned
214
+     */
215
+    #[NoAdminRequired]
216
+    public function getDisabledUsersDetails(string $search = '', ?int $limit = null, int $offset = 0): DataResponse {
217
+        $currentUser = $this->userSession->getUser();
218
+        if ($currentUser === null) {
219
+            return new DataResponse(['users' => []]);
220
+        }
221
+        if ($limit !== null && $limit < 0) {
222
+            throw new InvalidArgumentException("Invalid limit value: $limit");
223
+        }
224
+        if ($offset < 0) {
225
+            throw new InvalidArgumentException("Invalid offset value: $offset");
226
+        }
227
+
228
+        $users = [];
229
+
230
+        // Admin? Or SubAdmin?
231
+        $uid = $currentUser->getUID();
232
+        $subAdminManager = $this->groupManager->getSubAdmin();
233
+        $isAdmin = $this->groupManager->isAdmin($uid);
234
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($uid);
235
+        if ($isAdmin || $isDelegatedAdmin) {
236
+            $users = $this->userManager->getDisabledUsers($limit, $offset, $search);
237
+            $users = array_map(fn (IUser $user): string => $user->getUID(), $users);
238
+        } elseif ($subAdminManager->isSubAdmin($currentUser)) {
239
+            $subAdminOfGroups = $subAdminManager->getSubAdminsGroups($currentUser);
240
+
241
+            $users = [];
242
+            /* We have to handle offset ourselve for correctness */
243
+            $tempLimit = ($limit === null ? null : $limit + $offset);
244
+            foreach ($subAdminOfGroups as $group) {
245
+                $users = array_unique(array_merge(
246
+                    $users,
247
+                    array_map(
248
+                        fn (IUser $user): string => $user->getUID(),
249
+                        array_filter(
250
+                            $group->searchUsers($search),
251
+                            fn (IUser $user): bool => !$user->isEnabled()
252
+                        )
253
+                    )
254
+                ));
255
+                if (($tempLimit !== null) && (count($users) >= $tempLimit)) {
256
+                    break;
257
+                }
258
+            }
259
+            $users = array_slice($users, $offset, $limit);
260
+        }
261
+
262
+        $usersDetails = [];
263
+        foreach ($users as $userId) {
264
+            try {
265
+                $userData = $this->getUserData($userId);
266
+            } catch (OCSNotFoundException $e) {
267
+                // We still want to return all other accounts, but this one was removed from the backends
268
+                // yet they are still in our database. Might be a LDAP remnant.
269
+                $userData = null;
270
+                $this->logger->warning('Found one disabled account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
271
+            }
272
+            // Do not insert empty entry
273
+            if ($userData !== null) {
274
+                $usersDetails[$userId] = $userData;
275
+            } else {
276
+                // Currently logged in user does not have permissions to see this user
277
+                // only showing its id
278
+                $usersDetails[$userId] = ['id' => $userId];
279
+            }
280
+        }
281
+
282
+        return new DataResponse([
283
+            'users' => $usersDetails
284
+        ]);
285
+    }
286
+
287
+    /**
288
+     * Gets the list of users sorted by lastLogin, from most recent to least recent
289
+     *
290
+     * @param string $search Text to search for
291
+     * @param ?int $limit Limit the amount of users returned
292
+     * @param int $offset Offset
293
+     * @return DataResponse<Http::STATUS_OK, array{users: array<string, Provisioning_APIUserDetails|array{id: string}>}, array{}>
294
+     *
295
+     * 200: Users details returned based on last logged in information
296
+     */
297
+    #[AuthorizedAdminSetting(settings:Users::class)]
298
+    public function getLastLoggedInUsers(string $search = '',
299
+        ?int $limit = null,
300
+        int $offset = 0,
301
+    ): DataResponse {
302
+        $currentUser = $this->userSession->getUser();
303
+        if ($currentUser === null) {
304
+            return new DataResponse(['users' => []]);
305
+        }
306
+        if ($limit !== null && $limit < 0) {
307
+            throw new InvalidArgumentException("Invalid limit value: $limit");
308
+        }
309
+        if ($offset < 0) {
310
+            throw new InvalidArgumentException("Invalid offset value: $offset");
311
+        }
312
+
313
+        $users = [];
314
+
315
+        // For Admin alone user sorting based on lastLogin. For sub admin and groups this is not supported
316
+        $users = $this->userManager->getLastLoggedInUsers($limit, $offset, $search);
317
+
318
+        $usersDetails = [];
319
+        foreach ($users as $userId) {
320
+            try {
321
+                $userData = $this->getUserData($userId);
322
+            } catch (OCSNotFoundException $e) {
323
+                // We still want to return all other accounts, but this one was removed from the backends
324
+                // yet they are still in our database. Might be a LDAP remnant.
325
+                $userData = null;
326
+                $this->logger->warning('Found one account that was removed from its backend, but still exists in Nextcloud database', ['accountId' => $userId]);
327
+            }
328
+            // Do not insert empty entry
329
+            if ($userData !== null) {
330
+                $usersDetails[$userId] = $userData;
331
+            } else {
332
+                // Currently logged-in user does not have permissions to see this user
333
+                // only showing its id
334
+                $usersDetails[$userId] = ['id' => $userId];
335
+            }
336
+        }
337
+
338
+        return new DataResponse([
339
+            'users' => $usersDetails
340
+        ]);
341
+    }
342
+
343
+
344
+
345
+    /**
346
+     * @NoSubAdminRequired
347
+     *
348
+     * Search users by their phone numbers
349
+     *
350
+     * @param string $location Location of the phone number (for country code)
351
+     * @param array<string, list<string>> $search Phone numbers to search for
352
+     * @return DataResponse<Http::STATUS_OK, array<string, string>, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, list<empty>, array{}>
353
+     *
354
+     * 200: Users returned
355
+     * 400: Invalid location
356
+     */
357
+    #[NoAdminRequired]
358
+    public function searchByPhoneNumbers(string $location, array $search): DataResponse {
359
+        if ($this->phoneNumberUtil->getCountryCodeForRegion($location) === null) {
360
+            // Not a valid region code
361
+            return new DataResponse([], Http::STATUS_BAD_REQUEST);
362
+        }
363
+
364
+        /** @var IUser $user */
365
+        $user = $this->userSession->getUser();
366
+        $knownTo = $user->getUID();
367
+        $defaultPhoneRegion = $this->config->getSystemValueString('default_phone_region');
368
+
369
+        $normalizedNumberToKey = [];
370
+        foreach ($search as $key => $phoneNumbers) {
371
+            foreach ($phoneNumbers as $phone) {
372
+                $normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $location);
373
+                if ($normalizedNumber !== null) {
374
+                    $normalizedNumberToKey[$normalizedNumber] = (string)$key;
375
+                }
376
+
377
+                if ($defaultPhoneRegion !== '' && $defaultPhoneRegion !== $location && str_starts_with($phone, '0')) {
378
+                    // If the number has a leading zero (no country code),
379
+                    // we also check the default phone region of the instance,
380
+                    // when it's different to the user's given region.
381
+                    $normalizedNumber = $this->phoneNumberUtil->convertToStandardFormat($phone, $defaultPhoneRegion);
382
+                    if ($normalizedNumber !== null) {
383
+                        $normalizedNumberToKey[$normalizedNumber] = (string)$key;
384
+                    }
385
+                }
386
+            }
387
+        }
388
+
389
+        $phoneNumbers = array_keys($normalizedNumberToKey);
390
+
391
+        if (empty($phoneNumbers)) {
392
+            return new DataResponse();
393
+        }
394
+
395
+        // Cleanup all previous entries and only allow new matches
396
+        $this->knownUserService->deleteKnownTo($knownTo);
397
+
398
+        $userMatches = $this->accountManager->searchUsers(IAccountManager::PROPERTY_PHONE, $phoneNumbers);
399
+
400
+        if (empty($userMatches)) {
401
+            return new DataResponse();
402
+        }
403
+
404
+        $cloudUrl = rtrim($this->urlGenerator->getAbsoluteURL('/'), '/');
405
+        if (strpos($cloudUrl, 'http://') === 0) {
406
+            $cloudUrl = substr($cloudUrl, strlen('http://'));
407
+        } elseif (strpos($cloudUrl, 'https://') === 0) {
408
+            $cloudUrl = substr($cloudUrl, strlen('https://'));
409
+        }
410
+
411
+        $matches = [];
412
+        foreach ($userMatches as $phone => $userId) {
413
+            // Not using the ICloudIdManager as that would run a search for each contact to find the display name in the address book
414
+            $matches[$normalizedNumberToKey[$phone]] = $userId . '@' . $cloudUrl;
415
+            $this->knownUserService->storeIsKnownToUser($knownTo, $userId);
416
+        }
417
+
418
+        return new DataResponse($matches);
419
+    }
420
+
421
+    /**
422
+     * @throws OCSException
423
+     */
424
+    private function createNewUserId(): string {
425
+        $attempts = 0;
426
+        do {
427
+            $uidCandidate = $this->secureRandom->generate(10, ISecureRandom::CHAR_HUMAN_READABLE);
428
+            if (!$this->userManager->userExists($uidCandidate)) {
429
+                return $uidCandidate;
430
+            }
431
+            $attempts++;
432
+        } while ($attempts < 10);
433
+        throw new OCSException($this->l10n->t('Could not create non-existing user ID'), 111);
434
+    }
435
+
436
+    /**
437
+     * Create a new user
438
+     *
439
+     * @param string $userid ID of the user
440
+     * @param string $password Password of the user
441
+     * @param string $displayName Display name of the user
442
+     * @param string $email Email of the user
443
+     * @param list<string> $groups Groups of the user
444
+     * @param list<string> $subadmin Groups where the user is subadmin
445
+     * @param string $quota Quota of the user
446
+     * @param string $language Language of the user
447
+     * @param ?string $manager Manager of the user
448
+     * @return DataResponse<Http::STATUS_OK, array{id: string}, array{}>
449
+     * @throws OCSException
450
+     * @throws OCSForbiddenException Missing permissions to make user subadmin
451
+     *
452
+     * 200: User added successfully
453
+     */
454
+    #[PasswordConfirmationRequired]
455
+    #[NoAdminRequired]
456
+    public function addUser(
457
+        string $userid,
458
+        string $password = '',
459
+        string $displayName = '',
460
+        string $email = '',
461
+        array $groups = [],
462
+        array $subadmin = [],
463
+        string $quota = '',
464
+        string $language = '',
465
+        ?string $manager = null,
466
+    ): DataResponse {
467
+        $user = $this->userSession->getUser();
468
+        $isAdmin = $this->groupManager->isAdmin($user->getUID());
469
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($user->getUID());
470
+        $subAdminManager = $this->groupManager->getSubAdmin();
471
+
472
+        if (empty($userid) && $this->config->getAppValue('core', 'newUser.generateUserID', 'no') === 'yes') {
473
+            $userid = $this->createNewUserId();
474
+        }
475
+
476
+        if ($this->userManager->userExists($userid)) {
477
+            $this->logger->error('Failed addUser attempt: User already exists.', ['app' => 'ocs_api']);
478
+            throw new OCSException($this->l10n->t('User already exists'), 102);
479
+        }
480
+
481
+        if ($groups !== []) {
482
+            foreach ($groups as $group) {
483
+                if (!$this->groupManager->groupExists($group)) {
484
+                    throw new OCSException($this->l10n->t('Group %1$s does not exist', [$group]), 104);
485
+                }
486
+                if (!$isAdmin && !($isDelegatedAdmin && $group !== 'admin') && !$subAdminManager->isSubAdminOfGroup($user, $this->groupManager->get($group))) {
487
+                    throw new OCSException($this->l10n->t('Insufficient privileges for group %1$s', [$group]), 105);
488
+                }
489
+            }
490
+        } else {
491
+            if (!$isAdmin && !$isDelegatedAdmin) {
492
+                throw new OCSException($this->l10n->t('No group specified (required for sub-admins)'), 106);
493
+            }
494
+        }
495
+
496
+        $subadminGroups = [];
497
+        if ($subadmin !== []) {
498
+            foreach ($subadmin as $groupid) {
499
+                $group = $this->groupManager->get($groupid);
500
+                // Check if group exists
501
+                if ($group === null) {
502
+                    throw new OCSException($this->l10n->t('Sub-admin group does not exist'), 109);
503
+                }
504
+                // Check if trying to make subadmin of admin group
505
+                if ($group->getGID() === 'admin') {
506
+                    throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
507
+                }
508
+                // Check if has permission to promote subadmins
509
+                if (!$subAdminManager->isSubAdminOfGroup($user, $group) && !$isAdmin && !$isDelegatedAdmin) {
510
+                    throw new OCSForbiddenException($this->l10n->t('No permissions to promote sub-admins'));
511
+                }
512
+                $subadminGroups[] = $group;
513
+            }
514
+        }
515
+
516
+        $generatePasswordResetToken = false;
517
+        if (strlen($password) > IUserManager::MAX_PASSWORD_LENGTH) {
518
+            throw new OCSException($this->l10n->t('Invalid password value'), 101);
519
+        }
520
+        if ($password === '') {
521
+            if ($email === '') {
522
+                throw new OCSException($this->l10n->t('An email address is required, to send a password link to the user.'), 108);
523
+            }
524
+
525
+            $passwordEvent = new GenerateSecurePasswordEvent();
526
+            $this->eventDispatcher->dispatchTyped($passwordEvent);
527
+
528
+            $password = $passwordEvent->getPassword();
529
+            if ($password === null) {
530
+                // Fallback: ensure to pass password_policy in any case
531
+                $password = $this->secureRandom->generate(10)
532
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_UPPER)
533
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_LOWER)
534
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_DIGITS)
535
+                    . $this->secureRandom->generate(1, ISecureRandom::CHAR_SYMBOLS);
536
+            }
537
+            $generatePasswordResetToken = true;
538
+        }
539
+
540
+        if ($email === '' && $this->config->getAppValue('core', 'newUser.requireEmail', 'no') === 'yes') {
541
+            throw new OCSException($this->l10n->t('Required email address was not provided'), 110);
542
+        }
543
+
544
+        try {
545
+            $newUser = $this->userManager->createUser($userid, $password);
546
+            $this->logger->info('Successful addUser call with userid: ' . $userid, ['app' => 'ocs_api']);
547
+
548
+            foreach ($groups as $group) {
549
+                $this->groupManager->get($group)->addUser($newUser);
550
+                $this->logger->info('Added userid ' . $userid . ' to group ' . $group, ['app' => 'ocs_api']);
551
+            }
552
+            foreach ($subadminGroups as $group) {
553
+                $subAdminManager->createSubAdmin($newUser, $group);
554
+            }
555
+
556
+            if ($displayName !== '') {
557
+                try {
558
+                    $this->editUser($userid, self::USER_FIELD_DISPLAYNAME, $displayName);
559
+                } catch (OCSException $e) {
560
+                    if ($newUser instanceof IUser) {
561
+                        $newUser->delete();
562
+                    }
563
+                    throw $e;
564
+                }
565
+            }
566
+
567
+            if ($quota !== '') {
568
+                $this->editUser($userid, self::USER_FIELD_QUOTA, $quota);
569
+            }
570
+
571
+            if ($language !== '') {
572
+                $this->editUser($userid, self::USER_FIELD_LANGUAGE, $language);
573
+            }
574
+
575
+            /**
576
+             * null -> nothing sent
577
+             * '' -> unset manager
578
+             * else -> set manager
579
+             */
580
+            if ($manager !== null) {
581
+                $this->editUser($userid, self::USER_FIELD_MANAGER, $manager);
582
+            }
583
+
584
+            // Send new user mail only if a mail is set
585
+            if ($email !== '') {
586
+                $newUser->setEMailAddress($email);
587
+                if ($this->config->getAppValue('core', 'newUser.sendEmail', 'yes') === 'yes') {
588
+                    try {
589
+                        $emailTemplate = $this->newUserMailHelper->generateTemplate($newUser, $generatePasswordResetToken);
590
+                        $this->newUserMailHelper->sendMail($newUser, $emailTemplate);
591
+                    } catch (\Exception $e) {
592
+                        // Mail could be failing hard or just be plain not configured
593
+                        // Logging error as it is the hardest of the two
594
+                        $this->logger->error(
595
+                            "Unable to send the invitation mail to $email",
596
+                            [
597
+                                'app' => 'ocs_api',
598
+                                'exception' => $e,
599
+                            ]
600
+                        );
601
+                    }
602
+                }
603
+            }
604
+
605
+            return new DataResponse(['id' => $userid]);
606
+        } catch (HintException $e) {
607
+            $this->logger->warning(
608
+                'Failed addUser attempt with hint exception.',
609
+                [
610
+                    'app' => 'ocs_api',
611
+                    'exception' => $e,
612
+                ]
613
+            );
614
+            throw new OCSException($e->getHint(), 107);
615
+        } catch (OCSException $e) {
616
+            $this->logger->warning(
617
+                'Failed addUser attempt with ocs exception.',
618
+                [
619
+                    'app' => 'ocs_api',
620
+                    'exception' => $e,
621
+                ]
622
+            );
623
+            throw $e;
624
+        } catch (InvalidArgumentException $e) {
625
+            $this->logger->error(
626
+                'Failed addUser attempt with invalid argument exception.',
627
+                [
628
+                    'app' => 'ocs_api',
629
+                    'exception' => $e,
630
+                ]
631
+            );
632
+            throw new OCSException($e->getMessage(), 101);
633
+        } catch (\Exception $e) {
634
+            $this->logger->error(
635
+                'Failed addUser attempt with exception.',
636
+                [
637
+                    'app' => 'ocs_api',
638
+                    'exception' => $e
639
+                ]
640
+            );
641
+            throw new OCSException('Bad request', 101);
642
+        }
643
+    }
644
+
645
+    /**
646
+     * @NoSubAdminRequired
647
+     *
648
+     * Get the details of a user
649
+     *
650
+     * @param string $userId ID of the user
651
+     * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
652
+     * @throws OCSException
653
+     *
654
+     * 200: User returned
655
+     */
656
+    #[NoAdminRequired]
657
+    public function getUser(string $userId): DataResponse {
658
+        $includeScopes = false;
659
+        $currentUser = $this->userSession->getUser();
660
+        if ($currentUser && $currentUser->getUID() === $userId) {
661
+            $includeScopes = true;
662
+        }
663
+
664
+        $data = $this->getUserData($userId, $includeScopes);
665
+        // getUserData returns null if not enough permissions
666
+        if ($data === null) {
667
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
668
+        }
669
+        return new DataResponse($data);
670
+    }
671
+
672
+    /**
673
+     * @NoSubAdminRequired
674
+     *
675
+     * Get the details of the current user
676
+     *
677
+     * @return DataResponse<Http::STATUS_OK, Provisioning_APIUserDetails, array{}>
678
+     * @throws OCSException
679
+     *
680
+     * 200: Current user returned
681
+     */
682
+    #[NoAdminRequired]
683
+    public function getCurrentUser(): DataResponse {
684
+        $user = $this->userSession->getUser();
685
+        if ($user) {
686
+            /** @var Provisioning_APIUserDetails $data */
687
+            $data = $this->getUserData($user->getUID(), true);
688
+            return new DataResponse($data);
689
+        }
690
+
691
+        throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
692
+    }
693
+
694
+    /**
695
+     * @NoSubAdminRequired
696
+     *
697
+     * Get a list of fields that are editable for the current user
698
+     *
699
+     * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
700
+     * @throws OCSException
701
+     *
702
+     * 200: Editable fields returned
703
+     */
704
+    #[NoAdminRequired]
705
+    public function getEditableFields(): DataResponse {
706
+        $currentLoggedInUser = $this->userSession->getUser();
707
+        if (!$currentLoggedInUser instanceof IUser) {
708
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
709
+        }
710
+
711
+        return $this->getEditableFieldsForUser($currentLoggedInUser->getUID());
712
+    }
713
+
714
+    /**
715
+     * Get a list of enabled apps for the current user
716
+     *
717
+     * @return DataResponse<Http::STATUS_OK, array{apps: list<string>}, array{}>
718
+     *
719
+     * 200: Enabled apps returned
720
+     */
721
+    #[NoAdminRequired]
722
+    public function getEnabledApps(): DataResponse {
723
+        $currentLoggedInUser = $this->userSession->getUser();
724
+        return new DataResponse(['apps' => $this->appManager->getEnabledAppsForUser($currentLoggedInUser)]);
725
+    }
726
+
727
+    /**
728
+     * @NoSubAdminRequired
729
+     *
730
+     * Get a list of fields that are editable for a user
731
+     *
732
+     * @param string $userId ID of the user
733
+     * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
734
+     * @throws OCSException
735
+     *
736
+     * 200: Editable fields for user returned
737
+     */
738
+    #[NoAdminRequired]
739
+    public function getEditableFieldsForUser(string $userId): DataResponse {
740
+        $currentLoggedInUser = $this->userSession->getUser();
741
+        if (!$currentLoggedInUser instanceof IUser) {
742
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
743
+        }
744
+
745
+        $permittedFields = [];
746
+
747
+        if ($userId !== $currentLoggedInUser->getUID()) {
748
+            $targetUser = $this->userManager->get($userId);
749
+            if (!$targetUser instanceof IUser) {
750
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
751
+            }
752
+
753
+            $subAdminManager = $this->groupManager->getSubAdmin();
754
+            $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
755
+            $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
756
+            if (
757
+                !($isAdmin || $isDelegatedAdmin)
758
+                && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
759
+            ) {
760
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
761
+            }
762
+        } else {
763
+            $targetUser = $currentLoggedInUser;
764
+        }
765
+
766
+        $allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
767
+        if ($allowDisplayNameChange === true && (
768
+            $targetUser->getBackend() instanceof ISetDisplayNameBackend
769
+            || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
770
+        )) {
771
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
772
+        }
773
+
774
+        // Fallback to display name value to avoid changing behavior with the new option.
775
+        if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
776
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
777
+        }
778
+
779
+        $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
780
+        $permittedFields[] = IAccountManager::PROPERTY_PHONE;
781
+        $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
782
+        $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
783
+        $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
784
+        $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
785
+        $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
786
+        $permittedFields[] = IAccountManager::PROPERTY_ROLE;
787
+        $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
788
+        $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
789
+        $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
790
+        $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
791
+
792
+        return new DataResponse($permittedFields);
793
+    }
794
+
795
+    /**
796
+     * @NoSubAdminRequired
797
+     *
798
+     * Update multiple values of the user's details
799
+     *
800
+     * @param string $userId ID of the user
801
+     * @param string $collectionName Collection to update
802
+     * @param string $key Key that will be updated
803
+     * @param string $value New value for the key
804
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
805
+     * @throws OCSException
806
+     *
807
+     * 200: User values edited successfully
808
+     */
809
+    #[PasswordConfirmationRequired]
810
+    #[NoAdminRequired]
811
+    #[UserRateLimit(limit: 5, period: 60)]
812
+    public function editUserMultiValue(
813
+        string $userId,
814
+        string $collectionName,
815
+        string $key,
816
+        string $value,
817
+    ): DataResponse {
818
+        $currentLoggedInUser = $this->userSession->getUser();
819
+        if ($currentLoggedInUser === null) {
820
+            throw new OCSException('', OCSController::RESPOND_UNAUTHORISED);
821
+        }
822
+
823
+        $targetUser = $this->userManager->get($userId);
824
+        if ($targetUser === null) {
825
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
826
+        }
827
+
828
+        $subAdminManager = $this->groupManager->getSubAdmin();
829
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
830
+        $isAdminOrSubadmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID())
831
+            || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser);
832
+
833
+        $permittedFields = [];
834
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
835
+            // Editing self (display, email)
836
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
837
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX;
838
+        } else {
839
+            // Check if admin / subadmin
840
+            if ($isAdminOrSubadmin || $isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) {
841
+                // They have permissions over the user
842
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
843
+            } else {
844
+                // No rights
845
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
846
+            }
847
+        }
848
+
849
+        // Check if permitted to edit this field
850
+        if (!in_array($collectionName, $permittedFields)) {
851
+            throw new OCSException('', 103);
852
+        }
853
+
854
+        switch ($collectionName) {
855
+            case IAccountManager::COLLECTION_EMAIL:
856
+                $userAccount = $this->accountManager->getAccount($targetUser);
857
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
858
+                $mailCollection->removePropertyByValue($key);
859
+                if ($value !== '') {
860
+                    $mailCollection->addPropertyWithDefaults($value);
861
+                    $property = $mailCollection->getPropertyByValue($key);
862
+                    if ($isAdminOrSubadmin && $property) {
863
+                        // admin set mails are auto-verified
864
+                        $property->setLocallyVerified(IAccountManager::VERIFIED);
865
+                    }
866
+                }
867
+                $this->accountManager->updateAccount($userAccount);
868
+                if ($value === '' && $key === $targetUser->getPrimaryEMailAddress()) {
869
+                    $targetUser->setPrimaryEMailAddress('');
870
+                }
871
+                break;
872
+
873
+            case IAccountManager::COLLECTION_EMAIL . self::SCOPE_SUFFIX:
874
+                $userAccount = $this->accountManager->getAccount($targetUser);
875
+                $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
876
+                $targetProperty = null;
877
+                foreach ($mailCollection->getProperties() as $property) {
878
+                    if ($property->getValue() === $key) {
879
+                        $targetProperty = $property;
880
+                        break;
881
+                    }
882
+                }
883
+                if ($targetProperty instanceof IAccountProperty) {
884
+                    try {
885
+                        $targetProperty->setScope($value);
886
+                        $this->accountManager->updateAccount($userAccount);
887
+                    } catch (InvalidArgumentException $e) {
888
+                        throw new OCSException('', 102);
889
+                    }
890
+                } else {
891
+                    throw new OCSException('', 102);
892
+                }
893
+                break;
894
+
895
+            default:
896
+                throw new OCSException('', 103);
897
+        }
898
+        return new DataResponse();
899
+    }
900
+
901
+    /**
902
+     * @NoSubAdminRequired
903
+     *
904
+     * Update a value of the user's details
905
+     *
906
+     * @param string $userId ID of the user
907
+     * @param string $key Key that will be updated
908
+     * @param string $value New value for the key
909
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
910
+     * @throws OCSException
911
+     *
912
+     * 200: User value edited successfully
913
+     */
914
+    #[PasswordConfirmationRequired]
915
+    #[NoAdminRequired]
916
+    #[UserRateLimit(limit: 50, period: 600)]
917
+    public function editUser(string $userId, string $key, string $value): DataResponse {
918
+        $currentLoggedInUser = $this->userSession->getUser();
919
+
920
+        $targetUser = $this->userManager->get($userId);
921
+        if ($targetUser === null) {
922
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
923
+        }
924
+
925
+        $permittedFields = [];
926
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
927
+            $allowDisplayNameChange = $this->config->getSystemValue('allow_user_to_change_display_name', true);
928
+            if ($allowDisplayNameChange !== false && (
929
+                $targetUser->getBackend() instanceof ISetDisplayNameBackend
930
+                || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
931
+            )) {
932
+                $permittedFields[] = self::USER_FIELD_DISPLAYNAME;
933
+                $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
934
+            }
935
+
936
+            // Fallback to display name value to avoid changing behavior with the new option.
937
+            if ($this->config->getSystemValue('allow_user_to_change_email', $allowDisplayNameChange)) {
938
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
939
+            }
940
+
941
+            $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX;
942
+            $permittedFields[] = IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX;
943
+
944
+            $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
945
+
946
+            $permittedFields[] = self::USER_FIELD_PASSWORD;
947
+            $permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
948
+            if (
949
+                $this->config->getSystemValue('force_language', false) === false ||
950
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
951
+                $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
952
+            ) {
953
+                $permittedFields[] = self::USER_FIELD_LANGUAGE;
954
+            }
955
+
956
+            if (
957
+                $this->config->getSystemValue('force_locale', false) === false ||
958
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
959
+                $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID())
960
+            ) {
961
+                $permittedFields[] = self::USER_FIELD_LOCALE;
962
+                $permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
963
+            }
964
+
965
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE;
966
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
967
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
968
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
969
+            $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
970
+            $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
971
+            $permittedFields[] = IAccountManager::PROPERTY_ROLE;
972
+            $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
973
+            $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
974
+            $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
975
+            $permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE;
976
+            $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
977
+
978
+            $permittedFields[] = IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX;
979
+            $permittedFields[] = IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX;
980
+            $permittedFields[] = IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX;
981
+            $permittedFields[] = IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX;
982
+            $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX;
983
+            $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX;
984
+            $permittedFields[] = IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX;
985
+            $permittedFields[] = IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX;
986
+            $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX;
987
+            $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX;
988
+            $permittedFields[] = IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX;
989
+            $permittedFields[] = IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX;
990
+            $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX;
991
+
992
+            // If admin they can edit their own quota and manager
993
+            $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
994
+            $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
995
+            if ($isAdmin || $isDelegatedAdmin) {
996
+                $permittedFields[] = self::USER_FIELD_QUOTA;
997
+                $permittedFields[] = self::USER_FIELD_MANAGER;
998
+            }
999
+        } else {
1000
+            // Check if admin / subadmin
1001
+            $subAdminManager = $this->groupManager->getSubAdmin();
1002
+            if (
1003
+                $this->groupManager->isAdmin($currentLoggedInUser->getUID()) ||
1004
+                $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID()) && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')
1005
+                || $subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1006
+            ) {
1007
+                // They have permissions over the user
1008
+                if (
1009
+                    $targetUser->getBackend() instanceof ISetDisplayNameBackend
1010
+                    || $targetUser->getBackend()->implementsActions(Backend::SET_DISPLAYNAME)
1011
+                ) {
1012
+                    $permittedFields[] = self::USER_FIELD_DISPLAYNAME;
1013
+                    $permittedFields[] = IAccountManager::PROPERTY_DISPLAYNAME;
1014
+                }
1015
+                $permittedFields[] = IAccountManager::PROPERTY_EMAIL;
1016
+                $permittedFields[] = IAccountManager::COLLECTION_EMAIL;
1017
+                $permittedFields[] = self::USER_FIELD_PASSWORD;
1018
+                $permittedFields[] = self::USER_FIELD_LANGUAGE;
1019
+                $permittedFields[] = self::USER_FIELD_LOCALE;
1020
+                $permittedFields[] = self::USER_FIELD_FIRST_DAY_OF_WEEK;
1021
+                $permittedFields[] = IAccountManager::PROPERTY_PHONE;
1022
+                $permittedFields[] = IAccountManager::PROPERTY_ADDRESS;
1023
+                $permittedFields[] = IAccountManager::PROPERTY_WEBSITE;
1024
+                $permittedFields[] = IAccountManager::PROPERTY_TWITTER;
1025
+                $permittedFields[] = IAccountManager::PROPERTY_FEDIVERSE;
1026
+                $permittedFields[] = IAccountManager::PROPERTY_ORGANISATION;
1027
+                $permittedFields[] = IAccountManager::PROPERTY_ROLE;
1028
+                $permittedFields[] = IAccountManager::PROPERTY_HEADLINE;
1029
+                $permittedFields[] = IAccountManager::PROPERTY_BIOGRAPHY;
1030
+                $permittedFields[] = IAccountManager::PROPERTY_PROFILE_ENABLED;
1031
+                $permittedFields[] = IAccountManager::PROPERTY_PRONOUNS;
1032
+                $permittedFields[] = self::USER_FIELD_QUOTA;
1033
+                $permittedFields[] = self::USER_FIELD_NOTIFICATION_EMAIL;
1034
+                $permittedFields[] = self::USER_FIELD_MANAGER;
1035
+            } else {
1036
+                // No rights
1037
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1038
+            }
1039
+        }
1040
+        // Check if permitted to edit this field
1041
+        if (!in_array($key, $permittedFields)) {
1042
+            throw new OCSException('', 113);
1043
+        }
1044
+        // Process the edit
1045
+        switch ($key) {
1046
+            case self::USER_FIELD_DISPLAYNAME:
1047
+            case IAccountManager::PROPERTY_DISPLAYNAME:
1048
+                try {
1049
+                    $targetUser->setDisplayName($value);
1050
+                } catch (InvalidArgumentException $e) {
1051
+                    throw new OCSException($e->getMessage(), 101);
1052
+                }
1053
+                break;
1054
+            case self::USER_FIELD_QUOTA:
1055
+                $quota = $value;
1056
+                if ($quota !== 'none' && $quota !== 'default') {
1057
+                    if (is_numeric($quota)) {
1058
+                        $quota = (float)$quota;
1059
+                    } else {
1060
+                        $quota = Util::computerFileSize($quota);
1061
+                    }
1062
+                    if ($quota === false) {
1063
+                        throw new OCSException($this->l10n->t('Invalid quota value: %1$s', [$value]), 101);
1064
+                    }
1065
+                    if ($quota === -1) {
1066
+                        $quota = 'none';
1067
+                    } else {
1068
+                        $maxQuota = (int)$this->config->getAppValue('files', 'max_quota', '-1');
1069
+                        if ($maxQuota !== -1 && $quota > $maxQuota) {
1070
+                            throw new OCSException($this->l10n->t('Invalid quota value. %1$s is exceeding the maximum quota', [$value]), 101);
1071
+                        }
1072
+                        $quota = Util::humanFileSize($quota);
1073
+                    }
1074
+                }
1075
+                // no else block because quota can be set to 'none' in previous if
1076
+                if ($quota === 'none') {
1077
+                    $allowUnlimitedQuota = $this->config->getAppValue('files', 'allow_unlimited_quota', '1') === '1';
1078
+                    if (!$allowUnlimitedQuota) {
1079
+                        throw new OCSException($this->l10n->t('Unlimited quota is forbidden on this instance'), 101);
1080
+                    }
1081
+                }
1082
+                $targetUser->setQuota($quota);
1083
+                break;
1084
+            case self::USER_FIELD_MANAGER:
1085
+                $targetUser->setManagerUids([$value]);
1086
+                break;
1087
+            case self::USER_FIELD_PASSWORD:
1088
+                try {
1089
+                    if (strlen($value) > IUserManager::MAX_PASSWORD_LENGTH) {
1090
+                        throw new OCSException($this->l10n->t('Invalid password value'), 101);
1091
+                    }
1092
+                    if (!$targetUser->canChangePassword()) {
1093
+                        throw new OCSException($this->l10n->t('Setting the password is not supported by the users backend'), 112);
1094
+                    }
1095
+                    $targetUser->setPassword($value);
1096
+                } catch (HintException $e) { // password policy error
1097
+                    throw new OCSException($e->getHint(), 107);
1098
+                }
1099
+                break;
1100
+            case self::USER_FIELD_LANGUAGE:
1101
+                $languagesCodes = $this->l10nFactory->findAvailableLanguages();
1102
+                if (!in_array($value, $languagesCodes, true) && $value !== 'en') {
1103
+                    throw new OCSException($this->l10n->t('Invalid language'), 101);
1104
+                }
1105
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'lang', $value);
1106
+                break;
1107
+            case self::USER_FIELD_LOCALE:
1108
+                if (!$this->l10nFactory->localeExists($value)) {
1109
+                    throw new OCSException($this->l10n->t('Invalid locale'), 101);
1110
+                }
1111
+                $this->config->setUserValue($targetUser->getUID(), 'core', 'locale', $value);
1112
+                break;
1113
+            case self::USER_FIELD_FIRST_DAY_OF_WEEK:
1114
+                $intValue = (int)$value;
1115
+                if ($intValue < -1 || $intValue > 6) {
1116
+                    throw new OCSException($this->l10n->t('Invalid first day of week'), 101);
1117
+                }
1118
+                if ($intValue === -1) {
1119
+                    $this->config->deleteUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK);
1120
+                } else {
1121
+                    $this->config->setUserValue($targetUser->getUID(), 'core', AUserDataOCSController::USER_FIELD_FIRST_DAY_OF_WEEK, $value);
1122
+                }
1123
+                break;
1124
+            case self::USER_FIELD_NOTIFICATION_EMAIL:
1125
+                $success = false;
1126
+                if ($value === '' || filter_var($value, FILTER_VALIDATE_EMAIL)) {
1127
+                    try {
1128
+                        $targetUser->setPrimaryEMailAddress($value);
1129
+                        $success = true;
1130
+                    } catch (InvalidArgumentException $e) {
1131
+                        $this->logger->info(
1132
+                            'Cannot set primary email, because provided address is not verified',
1133
+                            [
1134
+                                'app' => 'provisioning_api',
1135
+                                'exception' => $e,
1136
+                            ]
1137
+                        );
1138
+                    }
1139
+                }
1140
+                if (!$success) {
1141
+                    throw new OCSException('', 101);
1142
+                }
1143
+                break;
1144
+            case IAccountManager::PROPERTY_EMAIL:
1145
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) || $value === '') {
1146
+                    $targetUser->setEMailAddress($value);
1147
+                } else {
1148
+                    throw new OCSException('', 101);
1149
+                }
1150
+                break;
1151
+            case IAccountManager::COLLECTION_EMAIL:
1152
+                if (filter_var($value, FILTER_VALIDATE_EMAIL) && $value !== $targetUser->getSystemEMailAddress()) {
1153
+                    $userAccount = $this->accountManager->getAccount($targetUser);
1154
+                    $mailCollection = $userAccount->getPropertyCollection(IAccountManager::COLLECTION_EMAIL);
1155
+
1156
+                    if ($mailCollection->getPropertyByValue($value)) {
1157
+                        throw new OCSException('', 101);
1158
+                    }
1159
+
1160
+                    $mailCollection->addPropertyWithDefaults($value);
1161
+                    $this->accountManager->updateAccount($userAccount);
1162
+                } else {
1163
+                    throw new OCSException('', 101);
1164
+                }
1165
+                break;
1166
+            case IAccountManager::PROPERTY_PHONE:
1167
+            case IAccountManager::PROPERTY_ADDRESS:
1168
+            case IAccountManager::PROPERTY_WEBSITE:
1169
+            case IAccountManager::PROPERTY_TWITTER:
1170
+            case IAccountManager::PROPERTY_FEDIVERSE:
1171
+            case IAccountManager::PROPERTY_ORGANISATION:
1172
+            case IAccountManager::PROPERTY_ROLE:
1173
+            case IAccountManager::PROPERTY_HEADLINE:
1174
+            case IAccountManager::PROPERTY_BIOGRAPHY:
1175
+            case IAccountManager::PROPERTY_BIRTHDATE:
1176
+            case IAccountManager::PROPERTY_PRONOUNS:
1177
+                $userAccount = $this->accountManager->getAccount($targetUser);
1178
+                try {
1179
+                    $userProperty = $userAccount->getProperty($key);
1180
+                    if ($userProperty->getValue() !== $value) {
1181
+                        try {
1182
+                            $userProperty->setValue($value);
1183
+                            if ($userProperty->getName() === IAccountManager::PROPERTY_PHONE) {
1184
+                                $this->knownUserService->deleteByContactUserId($targetUser->getUID());
1185
+                            }
1186
+                        } catch (InvalidArgumentException $e) {
1187
+                            throw new OCSException('Invalid ' . $e->getMessage(), 101);
1188
+                        }
1189
+                    }
1190
+                } catch (PropertyDoesNotExistException $e) {
1191
+                    $userAccount->setProperty($key, $value, IAccountManager::SCOPE_PRIVATE, IAccountManager::NOT_VERIFIED);
1192
+                }
1193
+                try {
1194
+                    $this->accountManager->updateAccount($userAccount);
1195
+                } catch (InvalidArgumentException $e) {
1196
+                    throw new OCSException('Invalid ' . $e->getMessage(), 101);
1197
+                }
1198
+                break;
1199
+            case IAccountManager::PROPERTY_PROFILE_ENABLED:
1200
+                $userAccount = $this->accountManager->getAccount($targetUser);
1201
+                try {
1202
+                    $userProperty = $userAccount->getProperty($key);
1203
+                    if ($userProperty->getValue() !== $value) {
1204
+                        $userProperty->setValue($value);
1205
+                    }
1206
+                } catch (PropertyDoesNotExistException $e) {
1207
+                    $userAccount->setProperty($key, $value, IAccountManager::SCOPE_LOCAL, IAccountManager::NOT_VERIFIED);
1208
+                }
1209
+                $this->accountManager->updateAccount($userAccount);
1210
+                break;
1211
+            case IAccountManager::PROPERTY_DISPLAYNAME . self::SCOPE_SUFFIX:
1212
+            case IAccountManager::PROPERTY_EMAIL . self::SCOPE_SUFFIX:
1213
+            case IAccountManager::PROPERTY_PHONE . self::SCOPE_SUFFIX:
1214
+            case IAccountManager::PROPERTY_ADDRESS . self::SCOPE_SUFFIX:
1215
+            case IAccountManager::PROPERTY_WEBSITE . self::SCOPE_SUFFIX:
1216
+            case IAccountManager::PROPERTY_TWITTER . self::SCOPE_SUFFIX:
1217
+            case IAccountManager::PROPERTY_FEDIVERSE . self::SCOPE_SUFFIX:
1218
+            case IAccountManager::PROPERTY_ORGANISATION . self::SCOPE_SUFFIX:
1219
+            case IAccountManager::PROPERTY_ROLE . self::SCOPE_SUFFIX:
1220
+            case IAccountManager::PROPERTY_HEADLINE . self::SCOPE_SUFFIX:
1221
+            case IAccountManager::PROPERTY_BIOGRAPHY . self::SCOPE_SUFFIX:
1222
+            case IAccountManager::PROPERTY_PROFILE_ENABLED . self::SCOPE_SUFFIX:
1223
+            case IAccountManager::PROPERTY_BIRTHDATE . self::SCOPE_SUFFIX:
1224
+            case IAccountManager::PROPERTY_AVATAR . self::SCOPE_SUFFIX:
1225
+            case IAccountManager::PROPERTY_PRONOUNS . self::SCOPE_SUFFIX:
1226
+                $propertyName = substr($key, 0, strlen($key) - strlen(self::SCOPE_SUFFIX));
1227
+                $userAccount = $this->accountManager->getAccount($targetUser);
1228
+                $userProperty = $userAccount->getProperty($propertyName);
1229
+                if ($userProperty->getScope() !== $value) {
1230
+                    try {
1231
+                        $userProperty->setScope($value);
1232
+                        $this->accountManager->updateAccount($userAccount);
1233
+                    } catch (InvalidArgumentException $e) {
1234
+                        throw new OCSException('Invalid ' . $e->getMessage(), 101);
1235
+                    }
1236
+                }
1237
+                break;
1238
+            default:
1239
+                throw new OCSException('', 113);
1240
+        }
1241
+        return new DataResponse();
1242
+    }
1243
+
1244
+    /**
1245
+     * Wipe all devices of a user
1246
+     *
1247
+     * @param string $userId ID of the user
1248
+     *
1249
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1250
+     *
1251
+     * @throws OCSException
1252
+     *
1253
+     * 200: Wiped all user devices successfully
1254
+     */
1255
+    #[PasswordConfirmationRequired]
1256
+    #[NoAdminRequired]
1257
+    public function wipeUserDevices(string $userId): DataResponse {
1258
+        /** @var IUser $currentLoggedInUser */
1259
+        $currentLoggedInUser = $this->userSession->getUser();
1260
+
1261
+        $targetUser = $this->userManager->get($userId);
1262
+
1263
+        if ($targetUser === null) {
1264
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1265
+        }
1266
+
1267
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1268
+            throw new OCSException('', 101);
1269
+        }
1270
+
1271
+        // If not permitted
1272
+        $subAdminManager = $this->groupManager->getSubAdmin();
1273
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1274
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1275
+        if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1276
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1277
+        }
1278
+
1279
+        $this->remoteWipe->markAllTokensForWipe($targetUser);
1280
+
1281
+        return new DataResponse();
1282
+    }
1283
+
1284
+    /**
1285
+     * Delete a user
1286
+     *
1287
+     * @param string $userId ID of the user
1288
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1289
+     * @throws OCSException
1290
+     *
1291
+     * 200: User deleted successfully
1292
+     */
1293
+    #[PasswordConfirmationRequired]
1294
+    #[NoAdminRequired]
1295
+    public function deleteUser(string $userId): DataResponse {
1296
+        $currentLoggedInUser = $this->userSession->getUser();
1297
+
1298
+        $targetUser = $this->userManager->get($userId);
1299
+
1300
+        if ($targetUser === null) {
1301
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1302
+        }
1303
+
1304
+        if ($targetUser->getUID() === $currentLoggedInUser->getUID()) {
1305
+            throw new OCSException('', 101);
1306
+        }
1307
+
1308
+        // If not permitted
1309
+        $subAdminManager = $this->groupManager->getSubAdmin();
1310
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1311
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1312
+        if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1313
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1314
+        }
1315
+
1316
+        // Go ahead with the delete
1317
+        if ($targetUser->delete()) {
1318
+            return new DataResponse();
1319
+        } else {
1320
+            throw new OCSException('', 101);
1321
+        }
1322
+    }
1323
+
1324
+    /**
1325
+     * Disable a user
1326
+     *
1327
+     * @param string $userId ID of the user
1328
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1329
+     * @throws OCSException
1330
+     *
1331
+     * 200: User disabled successfully
1332
+     */
1333
+    #[PasswordConfirmationRequired]
1334
+    #[NoAdminRequired]
1335
+    public function disableUser(string $userId): DataResponse {
1336
+        return $this->setEnabled($userId, false);
1337
+    }
1338
+
1339
+    /**
1340
+     * Enable a user
1341
+     *
1342
+     * @param string $userId ID of the user
1343
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1344
+     * @throws OCSException
1345
+     *
1346
+     * 200: User enabled successfully
1347
+     */
1348
+    #[PasswordConfirmationRequired]
1349
+    #[NoAdminRequired]
1350
+    public function enableUser(string $userId): DataResponse {
1351
+        return $this->setEnabled($userId, true);
1352
+    }
1353
+
1354
+    /**
1355
+     * @param string $userId
1356
+     * @param bool $value
1357
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1358
+     * @throws OCSException
1359
+     */
1360
+    private function setEnabled(string $userId, bool $value): DataResponse {
1361
+        $currentLoggedInUser = $this->userSession->getUser();
1362
+
1363
+        $targetUser = $this->userManager->get($userId);
1364
+        if ($targetUser === null || $targetUser->getUID() === $currentLoggedInUser->getUID()) {
1365
+            throw new OCSException('', 101);
1366
+        }
1367
+
1368
+        // If not permitted
1369
+        $subAdminManager = $this->groupManager->getSubAdmin();
1370
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1371
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1372
+        if (!$isAdmin && !($isDelegatedAdmin && !$this->groupManager->isInGroup($targetUser->getUID(), 'admin')) && !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)) {
1373
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1374
+        }
1375
+
1376
+        // enable/disable the user now
1377
+        $targetUser->setEnabled($value);
1378
+        return new DataResponse();
1379
+    }
1380
+
1381
+    /**
1382
+     * @NoSubAdminRequired
1383
+     *
1384
+     * Get a list of groups the user belongs to
1385
+     *
1386
+     * @param string $userId ID of the user
1387
+     * @return DataResponse<Http::STATUS_OK, array{groups: list<string>}, array{}>
1388
+     * @throws OCSException
1389
+     *
1390
+     * 200: Users groups returned
1391
+     */
1392
+    #[NoAdminRequired]
1393
+    public function getUsersGroups(string $userId): DataResponse {
1394
+        $loggedInUser = $this->userSession->getUser();
1395
+
1396
+        $targetUser = $this->userManager->get($userId);
1397
+        if ($targetUser === null) {
1398
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1399
+        }
1400
+
1401
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1402
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1403
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1404
+            // Self lookup or admin lookup
1405
+            return new DataResponse([
1406
+                'groups' => $this->groupManager->getUserGroupIds($targetUser)
1407
+            ]);
1408
+        } else {
1409
+            $subAdminManager = $this->groupManager->getSubAdmin();
1410
+
1411
+            // Looking up someone else
1412
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1413
+                // Return the group that the method caller is subadmin of for the user in question
1414
+                $groups = array_values(array_intersect(
1415
+                    array_map(static fn (IGroup $group) => $group->getGID(), $subAdminManager->getSubAdminsGroups($loggedInUser)),
1416
+                    $this->groupManager->getUserGroupIds($targetUser)
1417
+                ));
1418
+                return new DataResponse(['groups' => $groups]);
1419
+            } else {
1420
+                // Not permitted
1421
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1422
+            }
1423
+        }
1424
+    }
1425
+
1426
+    /**
1427
+     * @NoSubAdminRequired
1428
+     *
1429
+     * Get a list of groups with details
1430
+     *
1431
+     * @param string $userId ID of the user
1432
+     * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1433
+     * @throws OCSException
1434
+     *
1435
+     * 200: Users groups returned
1436
+     */
1437
+    #[NoAdminRequired]
1438
+    public function getUsersGroupsDetails(string $userId): DataResponse {
1439
+        $loggedInUser = $this->userSession->getUser();
1440
+
1441
+        $targetUser = $this->userManager->get($userId);
1442
+        if ($targetUser === null) {
1443
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1444
+        }
1445
+
1446
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1447
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1448
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1449
+            // Self lookup or admin lookup
1450
+            $groups = array_map(
1451
+                function (Group $group) {
1452
+                    return [
1453
+                        'id' => $group->getGID(),
1454
+                        'displayname' => $group->getDisplayName(),
1455
+                        'usercount' => $group->count(),
1456
+                        'disabled' => $group->countDisabled(),
1457
+                        'canAdd' => $group->canAddUser(),
1458
+                        'canRemove' => $group->canRemoveUser(),
1459
+                    ];
1460
+                },
1461
+                array_values($this->groupManager->getUserGroups($targetUser)),
1462
+            );
1463
+            return new DataResponse([
1464
+                'groups' => $groups,
1465
+            ]);
1466
+        } else {
1467
+            $subAdminManager = $this->groupManager->getSubAdmin();
1468
+
1469
+            // Looking up someone else
1470
+            if ($subAdminManager->isUserAccessible($loggedInUser, $targetUser)) {
1471
+                // Return the group that the method caller is subadmin of for the user in question
1472
+                $gids = array_values(array_intersect(
1473
+                    array_map(
1474
+                        static fn (IGroup $group) => $group->getGID(),
1475
+                        $subAdminManager->getSubAdminsGroups($loggedInUser),
1476
+                    ),
1477
+                    $this->groupManager->getUserGroupIds($targetUser)
1478
+                ));
1479
+                $groups = array_map(
1480
+                    function (string $gid) {
1481
+                        $group = $this->groupManager->get($gid);
1482
+                        return [
1483
+                            'id' => $group->getGID(),
1484
+                            'displayname' => $group->getDisplayName(),
1485
+                            'usercount' => $group->count(),
1486
+                            'disabled' => $group->countDisabled(),
1487
+                            'canAdd' => $group->canAddUser(),
1488
+                            'canRemove' => $group->canRemoveUser(),
1489
+                        ];
1490
+                    },
1491
+                    $gids,
1492
+                );
1493
+                return new DataResponse([
1494
+                    'groups' => $groups,
1495
+                ]);
1496
+            } else {
1497
+                // Not permitted
1498
+                throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1499
+            }
1500
+        }
1501
+    }
1502
+
1503
+    /**
1504
+     * @NoSubAdminRequired
1505
+     *
1506
+     * Get a list of the groups the user is a subadmin of, with details
1507
+     *
1508
+     * @param string $userId ID of the user
1509
+     * @return DataResponse<Http::STATUS_OK, array{groups: list<Provisioning_APIGroupDetails>}, array{}>
1510
+     * @throws OCSException
1511
+     *
1512
+     * 200: Users subadmin groups returned
1513
+     */
1514
+    #[NoAdminRequired]
1515
+    public function getUserSubAdminGroupsDetails(string $userId): DataResponse {
1516
+        $loggedInUser = $this->userSession->getUser();
1517
+
1518
+        $targetUser = $this->userManager->get($userId);
1519
+        if ($targetUser === null) {
1520
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1521
+        }
1522
+
1523
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1524
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1525
+        if ($targetUser->getUID() === $loggedInUser->getUID() || $isAdmin || $isDelegatedAdmin) {
1526
+            $subAdminManager = $this->groupManager->getSubAdmin();
1527
+            $groups = array_map(
1528
+                function (IGroup $group) {
1529
+                    return [
1530
+                        'id' => $group->getGID(),
1531
+                        'displayname' => $group->getDisplayName(),
1532
+                        'usercount' => $group->count(),
1533
+                        'disabled' => $group->countDisabled(),
1534
+                        'canAdd' => $group->canAddUser(),
1535
+                        'canRemove' => $group->canRemoveUser(),
1536
+                    ];
1537
+                },
1538
+                array_values($subAdminManager->getSubAdminsGroups($targetUser)),
1539
+            );
1540
+            return new DataResponse([
1541
+                'groups' => $groups,
1542
+            ]);
1543
+        }
1544
+        throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1545
+    }
1546
+
1547
+    /**
1548
+     * Add a user to a group
1549
+     *
1550
+     * @param string $userId ID of the user
1551
+     * @param string $groupid ID of the group
1552
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1553
+     * @throws OCSException
1554
+     *
1555
+     * 200: User added to group successfully
1556
+     */
1557
+    #[PasswordConfirmationRequired]
1558
+    #[NoAdminRequired]
1559
+    public function addToGroup(string $userId, string $groupid = ''): DataResponse {
1560
+        if ($groupid === '') {
1561
+            throw new OCSException('', 101);
1562
+        }
1563
+
1564
+        $group = $this->groupManager->get($groupid);
1565
+        $targetUser = $this->userManager->get($userId);
1566
+        if ($group === null) {
1567
+            throw new OCSException('', 102);
1568
+        }
1569
+        if ($targetUser === null) {
1570
+            throw new OCSException('', 103);
1571
+        }
1572
+
1573
+        // If they're not an admin, check they are a subadmin of the group in question
1574
+        $loggedInUser = $this->userSession->getUser();
1575
+        $subAdminManager = $this->groupManager->getSubAdmin();
1576
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1577
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1578
+        if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1579
+            throw new OCSException('', 104);
1580
+        }
1581
+
1582
+        // Add user to group
1583
+        $group->addUser($targetUser);
1584
+        return new DataResponse();
1585
+    }
1586
+
1587
+    /**
1588
+     * Remove a user from a group
1589
+     *
1590
+     * @param string $userId ID of the user
1591
+     * @param string $groupid ID of the group
1592
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1593
+     * @throws OCSException
1594
+     *
1595
+     * 200: User removed from group successfully
1596
+     */
1597
+    #[PasswordConfirmationRequired]
1598
+    #[NoAdminRequired]
1599
+    public function removeFromGroup(string $userId, string $groupid): DataResponse {
1600
+        $loggedInUser = $this->userSession->getUser();
1601
+
1602
+        if ($groupid === null || trim($groupid) === '') {
1603
+            throw new OCSException('', 101);
1604
+        }
1605
+
1606
+        $group = $this->groupManager->get($groupid);
1607
+        if ($group === null) {
1608
+            throw new OCSException('', 102);
1609
+        }
1610
+
1611
+        $targetUser = $this->userManager->get($userId);
1612
+        if ($targetUser === null) {
1613
+            throw new OCSException('', 103);
1614
+        }
1615
+
1616
+        // If they're not an admin, check they are a subadmin of the group in question
1617
+        $subAdminManager = $this->groupManager->getSubAdmin();
1618
+        $isAdmin = $this->groupManager->isAdmin($loggedInUser->getUID());
1619
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($loggedInUser->getUID());
1620
+        if (!$isAdmin && !($isDelegatedAdmin && $groupid !== 'admin') && !$subAdminManager->isSubAdminOfGroup($loggedInUser, $group)) {
1621
+            throw new OCSException('', 104);
1622
+        }
1623
+
1624
+        // Check they aren't removing themselves from 'admin' or their 'subadmin; group
1625
+        if ($targetUser->getUID() === $loggedInUser->getUID()) {
1626
+            if ($isAdmin || $isDelegatedAdmin) {
1627
+                if ($group->getGID() === 'admin') {
1628
+                    throw new OCSException($this->l10n->t('Cannot remove yourself from the admin group'), 105);
1629
+                }
1630
+            } else {
1631
+                // Not an admin, so the user must be a subadmin of this group, but that is not allowed.
1632
+                throw new OCSException($this->l10n->t('Cannot remove yourself from this group as you are a sub-admin'), 105);
1633
+            }
1634
+        } elseif (!($isAdmin || $isDelegatedAdmin)) {
1635
+            /** @var IGroup[] $subAdminGroups */
1636
+            $subAdminGroups = $subAdminManager->getSubAdminsGroups($loggedInUser);
1637
+            $subAdminGroups = array_map(function (IGroup $subAdminGroup) {
1638
+                return $subAdminGroup->getGID();
1639
+            }, $subAdminGroups);
1640
+            $userGroups = $this->groupManager->getUserGroupIds($targetUser);
1641
+            $userSubAdminGroups = array_intersect($subAdminGroups, $userGroups);
1642
+
1643
+            if (count($userSubAdminGroups) <= 1) {
1644
+                // Subadmin must not be able to remove a user from all their subadmin groups.
1645
+                throw new OCSException($this->l10n->t('Not viable to remove user from the last group you are sub-admin of'), 105);
1646
+            }
1647
+        }
1648
+
1649
+        // Remove user from group
1650
+        $group->removeUser($targetUser);
1651
+        return new DataResponse();
1652
+    }
1653
+
1654
+    /**
1655
+     * Make a user a subadmin of a group
1656
+     *
1657
+     * @param string $userId ID of the user
1658
+     * @param string $groupid ID of the group
1659
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1660
+     * @throws OCSException
1661
+     *
1662
+     * 200: User added as group subadmin successfully
1663
+     */
1664
+    #[AuthorizedAdminSetting(settings:Users::class)]
1665
+    #[PasswordConfirmationRequired]
1666
+    public function addSubAdmin(string $userId, string $groupid): DataResponse {
1667
+        $group = $this->groupManager->get($groupid);
1668
+        $user = $this->userManager->get($userId);
1669
+
1670
+        // Check if the user exists
1671
+        if ($user === null) {
1672
+            throw new OCSException($this->l10n->t('User does not exist'), 101);
1673
+        }
1674
+        // Check if group exists
1675
+        if ($group === null) {
1676
+            throw new OCSException($this->l10n->t('Group does not exist'), 102);
1677
+        }
1678
+        // Check if trying to make subadmin of admin group
1679
+        if ($group->getGID() === 'admin') {
1680
+            throw new OCSException($this->l10n->t('Cannot create sub-admins for admin group'), 103);
1681
+        }
1682
+
1683
+        $subAdminManager = $this->groupManager->getSubAdmin();
1684
+
1685
+        // We cannot be subadmin twice
1686
+        if ($subAdminManager->isSubAdminOfGroup($user, $group)) {
1687
+            return new DataResponse();
1688
+        }
1689
+        // Go
1690
+        $subAdminManager->createSubAdmin($user, $group);
1691
+        return new DataResponse();
1692
+    }
1693
+
1694
+    /**
1695
+     * Remove a user from the subadmins of a group
1696
+     *
1697
+     * @param string $userId ID of the user
1698
+     * @param string $groupid ID of the group
1699
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1700
+     * @throws OCSException
1701
+     *
1702
+     * 200: User removed as group subadmin successfully
1703
+     */
1704
+    #[AuthorizedAdminSetting(settings:Users::class)]
1705
+    #[PasswordConfirmationRequired]
1706
+    public function removeSubAdmin(string $userId, string $groupid): DataResponse {
1707
+        $group = $this->groupManager->get($groupid);
1708
+        $user = $this->userManager->get($userId);
1709
+        $subAdminManager = $this->groupManager->getSubAdmin();
1710
+
1711
+        // Check if the user exists
1712
+        if ($user === null) {
1713
+            throw new OCSException($this->l10n->t('User does not exist'), 101);
1714
+        }
1715
+        // Check if the group exists
1716
+        if ($group === null) {
1717
+            throw new OCSException($this->l10n->t('Group does not exist'), 101);
1718
+        }
1719
+        // Check if they are a subadmin of this said group
1720
+        if (!$subAdminManager->isSubAdminOfGroup($user, $group)) {
1721
+            throw new OCSException($this->l10n->t('User is not a sub-admin of this group'), 102);
1722
+        }
1723
+
1724
+        // Go
1725
+        $subAdminManager->deleteSubAdmin($user, $group);
1726
+        return new DataResponse();
1727
+    }
1728
+
1729
+    /**
1730
+     * Get the groups a user is a subadmin of
1731
+     *
1732
+     * @param string $userId ID if the user
1733
+     * @return DataResponse<Http::STATUS_OK, list<string>, array{}>
1734
+     * @throws OCSException
1735
+     *
1736
+     * 200: User subadmin groups returned
1737
+     */
1738
+    #[AuthorizedAdminSetting(settings:Users::class)]
1739
+    public function getUserSubAdminGroups(string $userId): DataResponse {
1740
+        $groups = $this->getUserSubAdminGroupsData($userId);
1741
+        return new DataResponse($groups);
1742
+    }
1743
+
1744
+    /**
1745
+     * Resend the welcome message
1746
+     *
1747
+     * @param string $userId ID if the user
1748
+     * @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
1749
+     * @throws OCSException
1750
+     *
1751
+     * 200: Resent welcome message successfully
1752
+     */
1753
+    #[PasswordConfirmationRequired]
1754
+    #[NoAdminRequired]
1755
+    public function resendWelcomeMessage(string $userId): DataResponse {
1756
+        $currentLoggedInUser = $this->userSession->getUser();
1757
+
1758
+        $targetUser = $this->userManager->get($userId);
1759
+        if ($targetUser === null) {
1760
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1761
+        }
1762
+
1763
+        // Check if admin / subadmin
1764
+        $subAdminManager = $this->groupManager->getSubAdmin();
1765
+        $isAdmin = $this->groupManager->isAdmin($currentLoggedInUser->getUID());
1766
+        $isDelegatedAdmin = $this->groupManager->isDelegatedAdmin($currentLoggedInUser->getUID());
1767
+        if (
1768
+            !$subAdminManager->isUserAccessible($currentLoggedInUser, $targetUser)
1769
+            && !($isAdmin || $isDelegatedAdmin)
1770
+        ) {
1771
+            // No rights
1772
+            throw new OCSException('', OCSController::RESPOND_NOT_FOUND);
1773
+        }
1774
+
1775
+        $email = $targetUser->getEMailAddress();
1776
+        if ($email === '' || $email === null) {
1777
+            throw new OCSException($this->l10n->t('Email address not available'), 101);
1778
+        }
1779
+
1780
+        try {
1781
+            if ($this->config->getUserValue($targetUser->getUID(), 'core', 'lostpassword')) {
1782
+                $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, true);
1783
+            } else {
1784
+                $emailTemplate = $this->newUserMailHelper->generateTemplate($targetUser, false);
1785
+            }
1786
+
1787
+            $this->newUserMailHelper->sendMail($targetUser, $emailTemplate);
1788
+        } catch (\Exception $e) {
1789
+            $this->logger->error(
1790
+                "Can't send new user mail to $email",
1791
+                [
1792
+                    'app' => 'settings',
1793
+                    'exception' => $e,
1794
+                ]
1795
+            );
1796
+            throw new OCSException($this->l10n->t('Sending email failed'), 102);
1797
+        }
1798
+
1799
+        return new DataResponse();
1800
+    }
1801 1801
 }
Please login to merge, or discard this patch.
lib/private/App/AppManager.php 1 patch
Indentation   +909 added lines, -909 removed lines patch added patch discarded remove patch
@@ -32,913 +32,913 @@
 block discarded – undo
32 32
 use Psr\Log\LoggerInterface;
33 33
 
34 34
 class AppManager implements IAppManager {
35
-	/**
36
-	 * Apps with these types can not be enabled for certain groups only
37
-	 * @var string[]
38
-	 */
39
-	protected $protectedAppTypes = [
40
-		'filesystem',
41
-		'prelogin',
42
-		'authentication',
43
-		'logging',
44
-		'prevent_group_restriction',
45
-	];
46
-
47
-	/** @var string[] $appId => $enabled */
48
-	private array $enabledAppsCache = [];
49
-
50
-	/** @var string[]|null */
51
-	private ?array $shippedApps = null;
52
-
53
-	private array $alwaysEnabled = [];
54
-	private array $defaultEnabled = [];
55
-
56
-	/** @var array */
57
-	private array $appInfos = [];
58
-
59
-	/** @var array */
60
-	private array $appVersions = [];
61
-
62
-	/** @var array */
63
-	private array $autoDisabledApps = [];
64
-	private array $appTypes = [];
65
-
66
-	/** @var array<string, true> */
67
-	private array $loadedApps = [];
68
-
69
-	private ?AppConfig $appConfig = null;
70
-	private ?IURLGenerator $urlGenerator = null;
71
-	private ?INavigationManager $navigationManager = null;
72
-
73
-	/**
74
-	 * Be extremely careful when injecting classes here. The AppManager is used by the installer,
75
-	 * so it needs to work before installation. See how AppConfig and IURLGenerator are injected for reference
76
-	 */
77
-	public function __construct(
78
-		private IUserSession $userSession,
79
-		private IConfig $config,
80
-		private IGroupManager $groupManager,
81
-		private ICacheFactory $memCacheFactory,
82
-		private IEventDispatcher $dispatcher,
83
-		private LoggerInterface $logger,
84
-		private ServerVersion $serverVersion,
85
-	) {
86
-	}
87
-
88
-	private function getNavigationManager(): INavigationManager {
89
-		if ($this->navigationManager === null) {
90
-			$this->navigationManager = \OCP\Server::get(INavigationManager::class);
91
-		}
92
-		return $this->navigationManager;
93
-	}
94
-
95
-	public function getAppIcon(string $appId, bool $dark = false): ?string {
96
-		$possibleIcons = $dark ? [$appId . '-dark.svg', 'app-dark.svg'] : [$appId . '.svg', 'app.svg'];
97
-		$icon = null;
98
-		foreach ($possibleIcons as $iconName) {
99
-			try {
100
-				$icon = $this->getUrlGenerator()->imagePath($appId, $iconName);
101
-				break;
102
-			} catch (\RuntimeException $e) {
103
-				// ignore
104
-			}
105
-		}
106
-		return $icon;
107
-	}
108
-
109
-	private function getAppConfig(): AppConfig {
110
-		if ($this->appConfig !== null) {
111
-			return $this->appConfig;
112
-		}
113
-		if (!$this->config->getSystemValueBool('installed', false)) {
114
-			throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
115
-		}
116
-		$this->appConfig = \OCP\Server::get(AppConfig::class);
117
-		return $this->appConfig;
118
-	}
119
-
120
-	private function getUrlGenerator(): IURLGenerator {
121
-		if ($this->urlGenerator !== null) {
122
-			return $this->urlGenerator;
123
-		}
124
-		if (!$this->config->getSystemValueBool('installed', false)) {
125
-			throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
126
-		}
127
-		$this->urlGenerator = \OCP\Server::get(IURLGenerator::class);
128
-		return $this->urlGenerator;
129
-	}
130
-
131
-	/**
132
-	 * For all enabled apps, return the value of their 'enabled' config key.
133
-	 *
134
-	 * @return array<string,string> appId => enabled (may be 'yes', or a json encoded list of group ids)
135
-	 */
136
-	private function getEnabledAppsValues(): array {
137
-		if (!$this->enabledAppsCache) {
138
-			/** @var array<string,string> */
139
-			$values = $this->getAppConfig()->searchValues('enabled', false, IAppConfig::VALUE_STRING);
140
-
141
-			$alwaysEnabledApps = $this->getAlwaysEnabledApps();
142
-			foreach ($alwaysEnabledApps as $appId) {
143
-				$values[$appId] = 'yes';
144
-			}
145
-
146
-			$this->enabledAppsCache = array_filter($values, function ($value) {
147
-				return $value !== 'no';
148
-			});
149
-			ksort($this->enabledAppsCache);
150
-		}
151
-		return $this->enabledAppsCache;
152
-	}
153
-
154
-	/**
155
-	 * Deprecated alias
156
-	 *
157
-	 * @return string[]
158
-	 */
159
-	public function getInstalledApps() {
160
-		return $this->getEnabledApps();
161
-	}
162
-
163
-	/**
164
-	 * List all enabled apps, either for everyone or for some groups
165
-	 *
166
-	 * @return list<string>
167
-	 */
168
-	public function getEnabledApps(): array {
169
-		return array_keys($this->getEnabledAppsValues());
170
-	}
171
-
172
-	/**
173
-	 * Get a list of all apps in the apps folder
174
-	 *
175
-	 * @return list<string> an array of app names (string IDs)
176
-	 */
177
-	public function getAllAppsInAppsFolders(): array {
178
-		$apps = [];
179
-
180
-		foreach (\OC::$APPSROOTS as $apps_dir) {
181
-			if (!is_readable($apps_dir['path'])) {
182
-				$this->logger->warning('unable to read app folder : ' . $apps_dir['path'], ['app' => 'core']);
183
-				continue;
184
-			}
185
-			$dh = opendir($apps_dir['path']);
186
-
187
-			if (is_resource($dh)) {
188
-				while (($file = readdir($dh)) !== false) {
189
-					if (
190
-						$file[0] != '.' &&
191
-						is_dir($apps_dir['path'] . '/' . $file) &&
192
-						is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')
193
-					) {
194
-						$apps[] = $file;
195
-					}
196
-				}
197
-			}
198
-		}
199
-
200
-		return array_values(array_unique($apps));
201
-	}
202
-
203
-	/**
204
-	 * List all apps enabled for a user
205
-	 *
206
-	 * @param \OCP\IUser $user
207
-	 * @return list<string>
208
-	 */
209
-	public function getEnabledAppsForUser(IUser $user) {
210
-		$apps = $this->getEnabledAppsValues();
211
-		$appsForUser = array_filter($apps, function ($enabled) use ($user) {
212
-			return $this->checkAppForUser($enabled, $user);
213
-		});
214
-		return array_keys($appsForUser);
215
-	}
216
-
217
-	public function getEnabledAppsForGroup(IGroup $group): array {
218
-		$apps = $this->getEnabledAppsValues();
219
-		$appsForGroups = array_filter($apps, function ($enabled) use ($group) {
220
-			return $this->checkAppForGroups($enabled, $group);
221
-		});
222
-		return array_keys($appsForGroups);
223
-	}
224
-
225
-	/**
226
-	 * Loads all apps
227
-	 *
228
-	 * @param string[] $types
229
-	 * @return bool
230
-	 *
231
-	 * This function walks through the Nextcloud directory and loads all apps
232
-	 * it can find. A directory contains an app if the file /appinfo/info.xml
233
-	 * exists.
234
-	 *
235
-	 * if $types is set to non-empty array, only apps of those types will be loaded
236
-	 */
237
-	public function loadApps(array $types = []): bool {
238
-		if ($this->config->getSystemValueBool('maintenance', false)) {
239
-			return false;
240
-		}
241
-		// Load the enabled apps here
242
-		$apps = \OC_App::getEnabledApps();
243
-
244
-		// Add each apps' folder as allowed class path
245
-		foreach ($apps as $app) {
246
-			// If the app is already loaded then autoloading it makes no sense
247
-			if (!$this->isAppLoaded($app)) {
248
-				$path = \OC_App::getAppPath($app);
249
-				if ($path !== false) {
250
-					\OC_App::registerAutoloading($app, $path);
251
-				}
252
-			}
253
-		}
254
-
255
-		// prevent app loading from printing output
256
-		ob_start();
257
-		foreach ($apps as $app) {
258
-			if (!$this->isAppLoaded($app) && ($types === [] || $this->isType($app, $types))) {
259
-				try {
260
-					$this->loadApp($app);
261
-				} catch (\Throwable $e) {
262
-					$this->logger->emergency('Error during app loading: ' . $e->getMessage(), [
263
-						'exception' => $e,
264
-						'app' => $app,
265
-					]);
266
-				}
267
-			}
268
-		}
269
-		ob_end_clean();
270
-
271
-		return true;
272
-	}
273
-
274
-	/**
275
-	 * check if an app is of a specific type
276
-	 *
277
-	 * @param string $app
278
-	 * @param array $types
279
-	 * @return bool
280
-	 */
281
-	public function isType(string $app, array $types): bool {
282
-		$appTypes = $this->getAppTypes($app);
283
-		foreach ($types as $type) {
284
-			if (in_array($type, $appTypes, true)) {
285
-				return true;
286
-			}
287
-		}
288
-		return false;
289
-	}
290
-
291
-	/**
292
-	 * get the types of an app
293
-	 *
294
-	 * @param string $app
295
-	 * @return string[]
296
-	 */
297
-	private function getAppTypes(string $app): array {
298
-		//load the cache
299
-		if (count($this->appTypes) === 0) {
300
-			$this->appTypes = $this->getAppConfig()->getValues(false, 'types') ?: [];
301
-		}
302
-
303
-		if (isset($this->appTypes[$app])) {
304
-			return explode(',', $this->appTypes[$app]);
305
-		}
306
-
307
-		return [];
308
-	}
309
-
310
-	/**
311
-	 * @return array
312
-	 */
313
-	public function getAutoDisabledApps(): array {
314
-		return $this->autoDisabledApps;
315
-	}
316
-
317
-	public function getAppRestriction(string $appId): array {
318
-		$values = $this->getEnabledAppsValues();
319
-
320
-		if (!isset($values[$appId])) {
321
-			return [];
322
-		}
323
-
324
-		if ($values[$appId] === 'yes' || $values[$appId] === 'no') {
325
-			return [];
326
-		}
327
-		return json_decode($values[$appId], true);
328
-	}
329
-
330
-	/**
331
-	 * Check if an app is enabled for user
332
-	 *
333
-	 * @param string $appId
334
-	 * @param \OCP\IUser|null $user (optional) if not defined, the currently logged in user will be used
335
-	 * @return bool
336
-	 */
337
-	public function isEnabledForUser($appId, $user = null) {
338
-		if ($this->isAlwaysEnabled($appId)) {
339
-			return true;
340
-		}
341
-		if ($user === null) {
342
-			$user = $this->userSession->getUser();
343
-		}
344
-		$enabledAppsValues = $this->getEnabledAppsValues();
345
-		if (isset($enabledAppsValues[$appId])) {
346
-			return $this->checkAppForUser($enabledAppsValues[$appId], $user);
347
-		} else {
348
-			return false;
349
-		}
350
-	}
351
-
352
-	private function checkAppForUser(string $enabled, ?IUser $user): bool {
353
-		if ($enabled === 'yes') {
354
-			return true;
355
-		} elseif ($user === null) {
356
-			return false;
357
-		} else {
358
-			if (empty($enabled)) {
359
-				return false;
360
-			}
361
-
362
-			$groupIds = json_decode($enabled);
363
-
364
-			if (!is_array($groupIds)) {
365
-				$jsonError = json_last_error();
366
-				$jsonErrorMsg = json_last_error_msg();
367
-				// this really should never happen (if it does, the admin should check the `enabled` key value via `occ config:list` because it's bogus for some reason)
368
-				$this->logger->warning('AppManager::checkAppForUser - can\'t decode group IDs listed in app\'s enabled config key: ' . print_r($enabled, true) . ' - JSON error (' . $jsonError . ') ' . $jsonErrorMsg);
369
-				return false;
370
-			}
371
-
372
-			$userGroups = $this->groupManager->getUserGroupIds($user);
373
-			foreach ($userGroups as $groupId) {
374
-				if (in_array($groupId, $groupIds, true)) {
375
-					return true;
376
-				}
377
-			}
378
-			return false;
379
-		}
380
-	}
381
-
382
-	private function checkAppForGroups(string $enabled, IGroup $group): bool {
383
-		if ($enabled === 'yes') {
384
-			return true;
385
-		} else {
386
-			if (empty($enabled)) {
387
-				return false;
388
-			}
389
-
390
-			$groupIds = json_decode($enabled);
391
-
392
-			if (!is_array($groupIds)) {
393
-				$jsonError = json_last_error();
394
-				$jsonErrorMsg = json_last_error_msg();
395
-				// this really should never happen (if it does, the admin should check the `enabled` key value via `occ config:list` because it's bogus for some reason)
396
-				$this->logger->warning('AppManager::checkAppForGroups - can\'t decode group IDs listed in app\'s enabled config key: ' . print_r($enabled, true) . ' - JSON error (' . $jsonError . ') ' . $jsonErrorMsg);
397
-				return false;
398
-			}
399
-
400
-			return in_array($group->getGID(), $groupIds);
401
-		}
402
-	}
403
-
404
-	/**
405
-	 * Check if an app is enabled in the instance
406
-	 *
407
-	 * Notice: This actually checks if the app is enabled and not only if it is installed.
408
-	 *
409
-	 * @param string $appId
410
-	 */
411
-	public function isInstalled($appId): bool {
412
-		return $this->isEnabledForAnyone($appId);
413
-	}
414
-
415
-	public function isEnabledForAnyone(string $appId): bool {
416
-		$enabledAppsValues = $this->getEnabledAppsValues();
417
-		return isset($enabledAppsValues[$appId]);
418
-	}
419
-
420
-	/**
421
-	 * Overwrite the `max-version` requirement for this app.
422
-	 */
423
-	public function overwriteNextcloudRequirement(string $appId): void {
424
-		$ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
425
-		if (!in_array($appId, $ignoreMaxApps, true)) {
426
-			$ignoreMaxApps[] = $appId;
427
-		}
428
-		$this->config->setSystemValue('app_install_overwrite', $ignoreMaxApps);
429
-	}
430
-
431
-	/**
432
-	 * Remove the `max-version` overwrite for this app.
433
-	 * This means this app now again can not be enabled if the `max-version` is smaller than the current Nextcloud version.
434
-	 */
435
-	public function removeOverwriteNextcloudRequirement(string $appId): void {
436
-		$ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
437
-		$ignoreMaxApps = array_filter($ignoreMaxApps, fn (string $id) => $id !== $appId);
438
-		$this->config->setSystemValue('app_install_overwrite', $ignoreMaxApps);
439
-	}
440
-
441
-	public function loadApp(string $app): void {
442
-		if (isset($this->loadedApps[$app])) {
443
-			return;
444
-		}
445
-		$this->loadedApps[$app] = true;
446
-		$appPath = \OC_App::getAppPath($app);
447
-		if ($appPath === false) {
448
-			return;
449
-		}
450
-		$eventLogger = \OC::$server->get(IEventLogger::class);
451
-		$eventLogger->start("bootstrap:load_app:$app", "Load app: $app");
452
-
453
-		// in case someone calls loadApp() directly
454
-		\OC_App::registerAutoloading($app, $appPath);
455
-
456
-		if (is_file($appPath . '/appinfo/app.php')) {
457
-			$this->logger->error('/appinfo/app.php is not supported anymore, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [
458
-				'app' => $app,
459
-			]);
460
-		}
461
-
462
-		$coordinator = \OCP\Server::get(Coordinator::class);
463
-		$coordinator->bootApp($app);
464
-
465
-		$eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it");
466
-		$info = $this->getAppInfo($app);
467
-		if (!empty($info['activity'])) {
468
-			$activityManager = \OC::$server->get(IActivityManager::class);
469
-			if (!empty($info['activity']['filters'])) {
470
-				foreach ($info['activity']['filters'] as $filter) {
471
-					$activityManager->registerFilter($filter);
472
-				}
473
-			}
474
-			if (!empty($info['activity']['settings'])) {
475
-				foreach ($info['activity']['settings'] as $setting) {
476
-					$activityManager->registerSetting($setting);
477
-				}
478
-			}
479
-			if (!empty($info['activity']['providers'])) {
480
-				foreach ($info['activity']['providers'] as $provider) {
481
-					$activityManager->registerProvider($provider);
482
-				}
483
-			}
484
-		}
485
-
486
-		if (!empty($info['settings'])) {
487
-			$settingsManager = \OC::$server->get(ISettingsManager::class);
488
-			if (!empty($info['settings']['admin'])) {
489
-				foreach ($info['settings']['admin'] as $setting) {
490
-					$settingsManager->registerSetting('admin', $setting);
491
-				}
492
-			}
493
-			if (!empty($info['settings']['admin-section'])) {
494
-				foreach ($info['settings']['admin-section'] as $section) {
495
-					$settingsManager->registerSection('admin', $section);
496
-				}
497
-			}
498
-			if (!empty($info['settings']['personal'])) {
499
-				foreach ($info['settings']['personal'] as $setting) {
500
-					$settingsManager->registerSetting('personal', $setting);
501
-				}
502
-			}
503
-			if (!empty($info['settings']['personal-section'])) {
504
-				foreach ($info['settings']['personal-section'] as $section) {
505
-					$settingsManager->registerSection('personal', $section);
506
-				}
507
-			}
508
-		}
509
-
510
-		if (!empty($info['collaboration']['plugins'])) {
511
-			// deal with one or many plugin entries
512
-			$plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
513
-				[$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
514
-			$collaboratorSearch = null;
515
-			$autoCompleteManager = null;
516
-			foreach ($plugins as $plugin) {
517
-				if ($plugin['@attributes']['type'] === 'collaborator-search') {
518
-					$pluginInfo = [
519
-						'shareType' => $plugin['@attributes']['share-type'],
520
-						'class' => $plugin['@value'],
521
-					];
522
-					$collaboratorSearch ??= \OC::$server->get(ICollaboratorSearch::class);
523
-					$collaboratorSearch->registerPlugin($pluginInfo);
524
-				} elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') {
525
-					$autoCompleteManager ??= \OC::$server->get(IAutoCompleteManager::class);
526
-					$autoCompleteManager->registerSorter($plugin['@value']);
527
-				}
528
-			}
529
-		}
530
-		$eventLogger->end("bootstrap:load_app:$app:info");
531
-
532
-		$eventLogger->end("bootstrap:load_app:$app");
533
-	}
534
-
535
-	/**
536
-	 * Check if an app is loaded
537
-	 * @param string $app app id
538
-	 * @since 26.0.0
539
-	 */
540
-	public function isAppLoaded(string $app): bool {
541
-		return isset($this->loadedApps[$app]);
542
-	}
543
-
544
-	/**
545
-	 * Enable an app for every user
546
-	 *
547
-	 * @param string $appId
548
-	 * @param bool $forceEnable
549
-	 * @throws AppPathNotFoundException
550
-	 * @throws \InvalidArgumentException if the application is not installed yet
551
-	 */
552
-	public function enableApp(string $appId, bool $forceEnable = false): void {
553
-		// Check if app exists
554
-		$this->getAppPath($appId);
555
-
556
-		if ($this->config->getAppValue($appId, 'installed_version', '') === '') {
557
-			throw new \InvalidArgumentException("$appId is not installed, cannot be enabled.");
558
-		}
559
-
560
-		if ($forceEnable) {
561
-			$this->overwriteNextcloudRequirement($appId);
562
-		}
563
-
564
-		$this->enabledAppsCache[$appId] = 'yes';
565
-		$this->getAppConfig()->setValue($appId, 'enabled', 'yes');
566
-		$this->dispatcher->dispatchTyped(new AppEnableEvent($appId));
567
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent(
568
-			ManagerEvent::EVENT_APP_ENABLE, $appId
569
-		));
570
-		$this->clearAppsCache();
571
-	}
572
-
573
-	/**
574
-	 * Whether a list of types contains a protected app type
575
-	 *
576
-	 * @param string[] $types
577
-	 * @return bool
578
-	 */
579
-	public function hasProtectedAppType($types) {
580
-		if (empty($types)) {
581
-			return false;
582
-		}
583
-
584
-		$protectedTypes = array_intersect($this->protectedAppTypes, $types);
585
-		return !empty($protectedTypes);
586
-	}
587
-
588
-	/**
589
-	 * Enable an app only for specific groups
590
-	 *
591
-	 * @param string $appId
592
-	 * @param IGroup[] $groups
593
-	 * @param bool $forceEnable
594
-	 * @throws \InvalidArgumentException if app can't be enabled for groups
595
-	 * @throws AppPathNotFoundException
596
-	 */
597
-	public function enableAppForGroups(string $appId, array $groups, bool $forceEnable = false): void {
598
-		// Check if app exists
599
-		$this->getAppPath($appId);
600
-
601
-		$info = $this->getAppInfo($appId);
602
-		if (!empty($info['types']) && $this->hasProtectedAppType($info['types'])) {
603
-			throw new \InvalidArgumentException("$appId can't be enabled for groups.");
604
-		}
605
-
606
-		if ($this->config->getAppValue($appId, 'installed_version', '') === '') {
607
-			throw new \InvalidArgumentException("$appId is not installed, cannot be enabled.");
608
-		}
609
-
610
-		if ($forceEnable) {
611
-			$this->overwriteNextcloudRequirement($appId);
612
-		}
613
-
614
-		/** @var string[] $groupIds */
615
-		$groupIds = array_map(function ($group) {
616
-			/** @var IGroup $group */
617
-			return ($group instanceof IGroup)
618
-				? $group->getGID()
619
-				: $group;
620
-		}, $groups);
621
-
622
-		$this->enabledAppsCache[$appId] = json_encode($groupIds);
623
-		$this->getAppConfig()->setValue($appId, 'enabled', json_encode($groupIds));
624
-		$this->dispatcher->dispatchTyped(new AppEnableEvent($appId, $groupIds));
625
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent(
626
-			ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
627
-		));
628
-		$this->clearAppsCache();
629
-	}
630
-
631
-	/**
632
-	 * Disable an app for every user
633
-	 *
634
-	 * @param string $appId
635
-	 * @param bool $automaticDisabled
636
-	 * @throws \Exception if app can't be disabled
637
-	 */
638
-	public function disableApp($appId, $automaticDisabled = false): void {
639
-		if ($this->isAlwaysEnabled($appId)) {
640
-			throw new \Exception("$appId can't be disabled.");
641
-		}
642
-
643
-		if ($automaticDisabled) {
644
-			$previousSetting = $this->getAppConfig()->getValue($appId, 'enabled', 'yes');
645
-			if ($previousSetting !== 'yes' && $previousSetting !== 'no') {
646
-				$previousSetting = json_decode($previousSetting, true);
647
-			}
648
-			$this->autoDisabledApps[$appId] = $previousSetting;
649
-		}
650
-
651
-		unset($this->enabledAppsCache[$appId]);
652
-		$this->getAppConfig()->setValue($appId, 'enabled', 'no');
653
-
654
-		// run uninstall steps
655
-		$appData = $this->getAppInfo($appId);
656
-		if (!is_null($appData)) {
657
-			\OC_App::executeRepairSteps($appId, $appData['repair-steps']['uninstall']);
658
-		}
659
-
660
-		$this->dispatcher->dispatchTyped(new AppDisableEvent($appId));
661
-		$this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent(
662
-			ManagerEvent::EVENT_APP_DISABLE, $appId
663
-		));
664
-		$this->clearAppsCache();
665
-	}
666
-
667
-	/**
668
-	 * Get the directory for the given app.
669
-	 *
670
-	 * @throws AppPathNotFoundException if app folder can't be found
671
-	 */
672
-	public function getAppPath(string $appId): string {
673
-		$appPath = \OC_App::getAppPath($appId);
674
-		if ($appPath === false) {
675
-			throw new AppPathNotFoundException('Could not find path for ' . $appId);
676
-		}
677
-		return $appPath;
678
-	}
679
-
680
-	/**
681
-	 * Get the web path for the given app.
682
-	 *
683
-	 * @param string $appId
684
-	 * @return string
685
-	 * @throws AppPathNotFoundException if app path can't be found
686
-	 */
687
-	public function getAppWebPath(string $appId): string {
688
-		$appWebPath = \OC_App::getAppWebPath($appId);
689
-		if ($appWebPath === false) {
690
-			throw new AppPathNotFoundException('Could not find web path for ' . $appId);
691
-		}
692
-		return $appWebPath;
693
-	}
694
-
695
-	/**
696
-	 * Clear the cached list of apps when enabling/disabling an app
697
-	 */
698
-	public function clearAppsCache(): void {
699
-		$this->appInfos = [];
700
-	}
701
-
702
-	/**
703
-	 * Returns a list of apps that need upgrade
704
-	 *
705
-	 * @param string $version Nextcloud version as array of version components
706
-	 * @return array list of app info from apps that need an upgrade
707
-	 *
708
-	 * @internal
709
-	 */
710
-	public function getAppsNeedingUpgrade($version) {
711
-		$appsToUpgrade = [];
712
-		$apps = $this->getEnabledApps();
713
-		foreach ($apps as $appId) {
714
-			$appInfo = $this->getAppInfo($appId);
715
-			$appDbVersion = $this->getAppConfig()->getValue($appId, 'installed_version');
716
-			if ($appDbVersion
717
-				&& isset($appInfo['version'])
718
-				&& version_compare($appInfo['version'], $appDbVersion, '>')
719
-				&& \OC_App::isAppCompatible($version, $appInfo)
720
-			) {
721
-				$appsToUpgrade[] = $appInfo;
722
-			}
723
-		}
724
-
725
-		return $appsToUpgrade;
726
-	}
727
-
728
-	/**
729
-	 * Returns the app information from "appinfo/info.xml".
730
-	 *
731
-	 * @param string|null $lang
732
-	 * @return array|null app info
733
-	 */
734
-	public function getAppInfo(string $appId, bool $path = false, $lang = null) {
735
-		if ($path) {
736
-			throw new \InvalidArgumentException('Calling IAppManager::getAppInfo() with a path is no longer supported. Please call IAppManager::getAppInfoByPath() instead and verify that the path is good before calling.');
737
-		}
738
-		if ($lang === null && isset($this->appInfos[$appId])) {
739
-			return $this->appInfos[$appId];
740
-		}
741
-		try {
742
-			$appPath = $this->getAppPath($appId);
743
-		} catch (AppPathNotFoundException) {
744
-			return null;
745
-		}
746
-		$file = $appPath . '/appinfo/info.xml';
747
-
748
-		$data = $this->getAppInfoByPath($file, $lang);
749
-
750
-		if ($lang === null) {
751
-			$this->appInfos[$appId] = $data;
752
-		}
753
-
754
-		return $data;
755
-	}
756
-
757
-	public function getAppInfoByPath(string $path, ?string $lang = null): ?array {
758
-		if (!str_ends_with($path, '/appinfo/info.xml')) {
759
-			return null;
760
-		}
761
-
762
-		$parser = new InfoParser($this->memCacheFactory->createLocal('core.appinfo'));
763
-		$data = $parser->parse($path);
764
-
765
-		if (is_array($data)) {
766
-			$data = \OC_App::parseAppInfo($data, $lang);
767
-		}
768
-
769
-		return $data;
770
-	}
771
-
772
-	public function getAppVersion(string $appId, bool $useCache = true): string {
773
-		if (!$useCache || !isset($this->appVersions[$appId])) {
774
-			if ($appId === 'core') {
775
-				$this->appVersions[$appId] = $this->serverVersion->getVersionString();
776
-			} else {
777
-				$appInfo = $this->getAppInfo($appId);
778
-				$this->appVersions[$appId] = ($appInfo !== null && isset($appInfo['version'])) ? $appInfo['version'] : '0';
779
-			}
780
-		}
781
-		return $this->appVersions[$appId];
782
-	}
783
-
784
-	/**
785
-	 * Returns the installed versions of all apps
786
-	 *
787
-	 * @return array<string, string>
788
-	 */
789
-	public function getAppInstalledVersions(bool $onlyEnabled = false): array {
790
-		return $this->getAppConfig()->getAppInstalledVersions($onlyEnabled);
791
-	}
792
-
793
-	/**
794
-	 * Returns a list of apps incompatible with the given version
795
-	 *
796
-	 * @param string $version Nextcloud version as array of version components
797
-	 *
798
-	 * @return array list of app info from incompatible apps
799
-	 *
800
-	 * @internal
801
-	 */
802
-	public function getIncompatibleApps(string $version): array {
803
-		$apps = $this->getEnabledApps();
804
-		$incompatibleApps = [];
805
-		foreach ($apps as $appId) {
806
-			$info = $this->getAppInfo($appId);
807
-			if ($info === null) {
808
-				$incompatibleApps[] = ['id' => $appId, 'name' => $appId];
809
-			} elseif (!\OC_App::isAppCompatible($version, $info)) {
810
-				$incompatibleApps[] = $info;
811
-			}
812
-		}
813
-		return $incompatibleApps;
814
-	}
815
-
816
-	/**
817
-	 * @inheritdoc
818
-	 * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::isShipped()
819
-	 */
820
-	public function isShipped($appId) {
821
-		$this->loadShippedJson();
822
-		return in_array($appId, $this->shippedApps, true);
823
-	}
824
-
825
-	private function isAlwaysEnabled(string $appId): bool {
826
-		if ($appId === 'core') {
827
-			return true;
828
-		}
829
-
830
-		$alwaysEnabled = $this->getAlwaysEnabledApps();
831
-		return in_array($appId, $alwaysEnabled, true);
832
-	}
833
-
834
-	/**
835
-	 * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson()
836
-	 * @throws \Exception
837
-	 */
838
-	private function loadShippedJson(): void {
839
-		if ($this->shippedApps === null) {
840
-			$shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
841
-			if (!file_exists($shippedJson)) {
842
-				throw new \Exception("File not found: $shippedJson");
843
-			}
844
-			$content = json_decode(file_get_contents($shippedJson), true);
845
-			$this->shippedApps = $content['shippedApps'];
846
-			$this->alwaysEnabled = $content['alwaysEnabled'];
847
-			$this->defaultEnabled = $content['defaultEnabled'];
848
-		}
849
-	}
850
-
851
-	/**
852
-	 * @inheritdoc
853
-	 */
854
-	public function getAlwaysEnabledApps() {
855
-		$this->loadShippedJson();
856
-		return $this->alwaysEnabled;
857
-	}
858
-
859
-	/**
860
-	 * @inheritdoc
861
-	 */
862
-	public function isDefaultEnabled(string $appId): bool {
863
-		return (in_array($appId, $this->getDefaultEnabledApps()));
864
-	}
865
-
866
-	/**
867
-	 * @inheritdoc
868
-	 */
869
-	public function getDefaultEnabledApps(): array {
870
-		$this->loadShippedJson();
871
-
872
-		return $this->defaultEnabled;
873
-	}
874
-
875
-	/**
876
-	 * @inheritdoc
877
-	 */
878
-	public function getDefaultAppForUser(?IUser $user = null, bool $withFallbacks = true): string {
879
-		$id = $this->getNavigationManager()->getDefaultEntryIdForUser($user, $withFallbacks);
880
-		$entry = $this->getNavigationManager()->get($id);
881
-		return (string)$entry['app'];
882
-	}
883
-
884
-	/**
885
-	 * @inheritdoc
886
-	 */
887
-	public function getDefaultApps(): array {
888
-		$ids = $this->getNavigationManager()->getDefaultEntryIds();
889
-
890
-		return array_values(array_unique(array_map(function (string $id) {
891
-			$entry = $this->getNavigationManager()->get($id);
892
-			return (string)$entry['app'];
893
-		}, $ids)));
894
-	}
895
-
896
-	/**
897
-	 * @inheritdoc
898
-	 */
899
-	public function setDefaultApps(array $defaultApps): void {
900
-		$entries = $this->getNavigationManager()->getAll();
901
-		$ids = [];
902
-		foreach ($defaultApps as $defaultApp) {
903
-			foreach ($entries as $entry) {
904
-				if ((string)$entry['app'] === $defaultApp) {
905
-					$ids[] = (string)$entry['id'];
906
-					break;
907
-				}
908
-			}
909
-		}
910
-		$this->getNavigationManager()->setDefaultEntryIds($ids);
911
-	}
912
-
913
-	public function isBackendRequired(string $backend): bool {
914
-		foreach ($this->appInfos as $appInfo) {
915
-			foreach ($appInfo['dependencies']['backend'] as $appBackend) {
916
-				if ($backend === $appBackend) {
917
-					return true;
918
-				}
919
-			}
920
-		}
921
-
922
-		return false;
923
-	}
924
-
925
-	/**
926
-	 * Clean the appId from forbidden characters
927
-	 *
928
-	 * @psalm-taint-escape callable
929
-	 * @psalm-taint-escape cookie
930
-	 * @psalm-taint-escape file
931
-	 * @psalm-taint-escape has_quotes
932
-	 * @psalm-taint-escape header
933
-	 * @psalm-taint-escape html
934
-	 * @psalm-taint-escape include
935
-	 * @psalm-taint-escape ldap
936
-	 * @psalm-taint-escape shell
937
-	 * @psalm-taint-escape sql
938
-	 * @psalm-taint-escape unserialize
939
-	 */
940
-	public function cleanAppId(string $app): string {
941
-		/* Only lowercase alphanumeric is allowed */
942
-		return preg_replace('/(^[0-9_]|[^a-z0-9_]+|_$)/', '', $app);
943
-	}
35
+    /**
36
+     * Apps with these types can not be enabled for certain groups only
37
+     * @var string[]
38
+     */
39
+    protected $protectedAppTypes = [
40
+        'filesystem',
41
+        'prelogin',
42
+        'authentication',
43
+        'logging',
44
+        'prevent_group_restriction',
45
+    ];
46
+
47
+    /** @var string[] $appId => $enabled */
48
+    private array $enabledAppsCache = [];
49
+
50
+    /** @var string[]|null */
51
+    private ?array $shippedApps = null;
52
+
53
+    private array $alwaysEnabled = [];
54
+    private array $defaultEnabled = [];
55
+
56
+    /** @var array */
57
+    private array $appInfos = [];
58
+
59
+    /** @var array */
60
+    private array $appVersions = [];
61
+
62
+    /** @var array */
63
+    private array $autoDisabledApps = [];
64
+    private array $appTypes = [];
65
+
66
+    /** @var array<string, true> */
67
+    private array $loadedApps = [];
68
+
69
+    private ?AppConfig $appConfig = null;
70
+    private ?IURLGenerator $urlGenerator = null;
71
+    private ?INavigationManager $navigationManager = null;
72
+
73
+    /**
74
+     * Be extremely careful when injecting classes here. The AppManager is used by the installer,
75
+     * so it needs to work before installation. See how AppConfig and IURLGenerator are injected for reference
76
+     */
77
+    public function __construct(
78
+        private IUserSession $userSession,
79
+        private IConfig $config,
80
+        private IGroupManager $groupManager,
81
+        private ICacheFactory $memCacheFactory,
82
+        private IEventDispatcher $dispatcher,
83
+        private LoggerInterface $logger,
84
+        private ServerVersion $serverVersion,
85
+    ) {
86
+    }
87
+
88
+    private function getNavigationManager(): INavigationManager {
89
+        if ($this->navigationManager === null) {
90
+            $this->navigationManager = \OCP\Server::get(INavigationManager::class);
91
+        }
92
+        return $this->navigationManager;
93
+    }
94
+
95
+    public function getAppIcon(string $appId, bool $dark = false): ?string {
96
+        $possibleIcons = $dark ? [$appId . '-dark.svg', 'app-dark.svg'] : [$appId . '.svg', 'app.svg'];
97
+        $icon = null;
98
+        foreach ($possibleIcons as $iconName) {
99
+            try {
100
+                $icon = $this->getUrlGenerator()->imagePath($appId, $iconName);
101
+                break;
102
+            } catch (\RuntimeException $e) {
103
+                // ignore
104
+            }
105
+        }
106
+        return $icon;
107
+    }
108
+
109
+    private function getAppConfig(): AppConfig {
110
+        if ($this->appConfig !== null) {
111
+            return $this->appConfig;
112
+        }
113
+        if (!$this->config->getSystemValueBool('installed', false)) {
114
+            throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
115
+        }
116
+        $this->appConfig = \OCP\Server::get(AppConfig::class);
117
+        return $this->appConfig;
118
+    }
119
+
120
+    private function getUrlGenerator(): IURLGenerator {
121
+        if ($this->urlGenerator !== null) {
122
+            return $this->urlGenerator;
123
+        }
124
+        if (!$this->config->getSystemValueBool('installed', false)) {
125
+            throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
126
+        }
127
+        $this->urlGenerator = \OCP\Server::get(IURLGenerator::class);
128
+        return $this->urlGenerator;
129
+    }
130
+
131
+    /**
132
+     * For all enabled apps, return the value of their 'enabled' config key.
133
+     *
134
+     * @return array<string,string> appId => enabled (may be 'yes', or a json encoded list of group ids)
135
+     */
136
+    private function getEnabledAppsValues(): array {
137
+        if (!$this->enabledAppsCache) {
138
+            /** @var array<string,string> */
139
+            $values = $this->getAppConfig()->searchValues('enabled', false, IAppConfig::VALUE_STRING);
140
+
141
+            $alwaysEnabledApps = $this->getAlwaysEnabledApps();
142
+            foreach ($alwaysEnabledApps as $appId) {
143
+                $values[$appId] = 'yes';
144
+            }
145
+
146
+            $this->enabledAppsCache = array_filter($values, function ($value) {
147
+                return $value !== 'no';
148
+            });
149
+            ksort($this->enabledAppsCache);
150
+        }
151
+        return $this->enabledAppsCache;
152
+    }
153
+
154
+    /**
155
+     * Deprecated alias
156
+     *
157
+     * @return string[]
158
+     */
159
+    public function getInstalledApps() {
160
+        return $this->getEnabledApps();
161
+    }
162
+
163
+    /**
164
+     * List all enabled apps, either for everyone or for some groups
165
+     *
166
+     * @return list<string>
167
+     */
168
+    public function getEnabledApps(): array {
169
+        return array_keys($this->getEnabledAppsValues());
170
+    }
171
+
172
+    /**
173
+     * Get a list of all apps in the apps folder
174
+     *
175
+     * @return list<string> an array of app names (string IDs)
176
+     */
177
+    public function getAllAppsInAppsFolders(): array {
178
+        $apps = [];
179
+
180
+        foreach (\OC::$APPSROOTS as $apps_dir) {
181
+            if (!is_readable($apps_dir['path'])) {
182
+                $this->logger->warning('unable to read app folder : ' . $apps_dir['path'], ['app' => 'core']);
183
+                continue;
184
+            }
185
+            $dh = opendir($apps_dir['path']);
186
+
187
+            if (is_resource($dh)) {
188
+                while (($file = readdir($dh)) !== false) {
189
+                    if (
190
+                        $file[0] != '.' &&
191
+                        is_dir($apps_dir['path'] . '/' . $file) &&
192
+                        is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')
193
+                    ) {
194
+                        $apps[] = $file;
195
+                    }
196
+                }
197
+            }
198
+        }
199
+
200
+        return array_values(array_unique($apps));
201
+    }
202
+
203
+    /**
204
+     * List all apps enabled for a user
205
+     *
206
+     * @param \OCP\IUser $user
207
+     * @return list<string>
208
+     */
209
+    public function getEnabledAppsForUser(IUser $user) {
210
+        $apps = $this->getEnabledAppsValues();
211
+        $appsForUser = array_filter($apps, function ($enabled) use ($user) {
212
+            return $this->checkAppForUser($enabled, $user);
213
+        });
214
+        return array_keys($appsForUser);
215
+    }
216
+
217
+    public function getEnabledAppsForGroup(IGroup $group): array {
218
+        $apps = $this->getEnabledAppsValues();
219
+        $appsForGroups = array_filter($apps, function ($enabled) use ($group) {
220
+            return $this->checkAppForGroups($enabled, $group);
221
+        });
222
+        return array_keys($appsForGroups);
223
+    }
224
+
225
+    /**
226
+     * Loads all apps
227
+     *
228
+     * @param string[] $types
229
+     * @return bool
230
+     *
231
+     * This function walks through the Nextcloud directory and loads all apps
232
+     * it can find. A directory contains an app if the file /appinfo/info.xml
233
+     * exists.
234
+     *
235
+     * if $types is set to non-empty array, only apps of those types will be loaded
236
+     */
237
+    public function loadApps(array $types = []): bool {
238
+        if ($this->config->getSystemValueBool('maintenance', false)) {
239
+            return false;
240
+        }
241
+        // Load the enabled apps here
242
+        $apps = \OC_App::getEnabledApps();
243
+
244
+        // Add each apps' folder as allowed class path
245
+        foreach ($apps as $app) {
246
+            // If the app is already loaded then autoloading it makes no sense
247
+            if (!$this->isAppLoaded($app)) {
248
+                $path = \OC_App::getAppPath($app);
249
+                if ($path !== false) {
250
+                    \OC_App::registerAutoloading($app, $path);
251
+                }
252
+            }
253
+        }
254
+
255
+        // prevent app loading from printing output
256
+        ob_start();
257
+        foreach ($apps as $app) {
258
+            if (!$this->isAppLoaded($app) && ($types === [] || $this->isType($app, $types))) {
259
+                try {
260
+                    $this->loadApp($app);
261
+                } catch (\Throwable $e) {
262
+                    $this->logger->emergency('Error during app loading: ' . $e->getMessage(), [
263
+                        'exception' => $e,
264
+                        'app' => $app,
265
+                    ]);
266
+                }
267
+            }
268
+        }
269
+        ob_end_clean();
270
+
271
+        return true;
272
+    }
273
+
274
+    /**
275
+     * check if an app is of a specific type
276
+     *
277
+     * @param string $app
278
+     * @param array $types
279
+     * @return bool
280
+     */
281
+    public function isType(string $app, array $types): bool {
282
+        $appTypes = $this->getAppTypes($app);
283
+        foreach ($types as $type) {
284
+            if (in_array($type, $appTypes, true)) {
285
+                return true;
286
+            }
287
+        }
288
+        return false;
289
+    }
290
+
291
+    /**
292
+     * get the types of an app
293
+     *
294
+     * @param string $app
295
+     * @return string[]
296
+     */
297
+    private function getAppTypes(string $app): array {
298
+        //load the cache
299
+        if (count($this->appTypes) === 0) {
300
+            $this->appTypes = $this->getAppConfig()->getValues(false, 'types') ?: [];
301
+        }
302
+
303
+        if (isset($this->appTypes[$app])) {
304
+            return explode(',', $this->appTypes[$app]);
305
+        }
306
+
307
+        return [];
308
+    }
309
+
310
+    /**
311
+     * @return array
312
+     */
313
+    public function getAutoDisabledApps(): array {
314
+        return $this->autoDisabledApps;
315
+    }
316
+
317
+    public function getAppRestriction(string $appId): array {
318
+        $values = $this->getEnabledAppsValues();
319
+
320
+        if (!isset($values[$appId])) {
321
+            return [];
322
+        }
323
+
324
+        if ($values[$appId] === 'yes' || $values[$appId] === 'no') {
325
+            return [];
326
+        }
327
+        return json_decode($values[$appId], true);
328
+    }
329
+
330
+    /**
331
+     * Check if an app is enabled for user
332
+     *
333
+     * @param string $appId
334
+     * @param \OCP\IUser|null $user (optional) if not defined, the currently logged in user will be used
335
+     * @return bool
336
+     */
337
+    public function isEnabledForUser($appId, $user = null) {
338
+        if ($this->isAlwaysEnabled($appId)) {
339
+            return true;
340
+        }
341
+        if ($user === null) {
342
+            $user = $this->userSession->getUser();
343
+        }
344
+        $enabledAppsValues = $this->getEnabledAppsValues();
345
+        if (isset($enabledAppsValues[$appId])) {
346
+            return $this->checkAppForUser($enabledAppsValues[$appId], $user);
347
+        } else {
348
+            return false;
349
+        }
350
+    }
351
+
352
+    private function checkAppForUser(string $enabled, ?IUser $user): bool {
353
+        if ($enabled === 'yes') {
354
+            return true;
355
+        } elseif ($user === null) {
356
+            return false;
357
+        } else {
358
+            if (empty($enabled)) {
359
+                return false;
360
+            }
361
+
362
+            $groupIds = json_decode($enabled);
363
+
364
+            if (!is_array($groupIds)) {
365
+                $jsonError = json_last_error();
366
+                $jsonErrorMsg = json_last_error_msg();
367
+                // this really should never happen (if it does, the admin should check the `enabled` key value via `occ config:list` because it's bogus for some reason)
368
+                $this->logger->warning('AppManager::checkAppForUser - can\'t decode group IDs listed in app\'s enabled config key: ' . print_r($enabled, true) . ' - JSON error (' . $jsonError . ') ' . $jsonErrorMsg);
369
+                return false;
370
+            }
371
+
372
+            $userGroups = $this->groupManager->getUserGroupIds($user);
373
+            foreach ($userGroups as $groupId) {
374
+                if (in_array($groupId, $groupIds, true)) {
375
+                    return true;
376
+                }
377
+            }
378
+            return false;
379
+        }
380
+    }
381
+
382
+    private function checkAppForGroups(string $enabled, IGroup $group): bool {
383
+        if ($enabled === 'yes') {
384
+            return true;
385
+        } else {
386
+            if (empty($enabled)) {
387
+                return false;
388
+            }
389
+
390
+            $groupIds = json_decode($enabled);
391
+
392
+            if (!is_array($groupIds)) {
393
+                $jsonError = json_last_error();
394
+                $jsonErrorMsg = json_last_error_msg();
395
+                // this really should never happen (if it does, the admin should check the `enabled` key value via `occ config:list` because it's bogus for some reason)
396
+                $this->logger->warning('AppManager::checkAppForGroups - can\'t decode group IDs listed in app\'s enabled config key: ' . print_r($enabled, true) . ' - JSON error (' . $jsonError . ') ' . $jsonErrorMsg);
397
+                return false;
398
+            }
399
+
400
+            return in_array($group->getGID(), $groupIds);
401
+        }
402
+    }
403
+
404
+    /**
405
+     * Check if an app is enabled in the instance
406
+     *
407
+     * Notice: This actually checks if the app is enabled and not only if it is installed.
408
+     *
409
+     * @param string $appId
410
+     */
411
+    public function isInstalled($appId): bool {
412
+        return $this->isEnabledForAnyone($appId);
413
+    }
414
+
415
+    public function isEnabledForAnyone(string $appId): bool {
416
+        $enabledAppsValues = $this->getEnabledAppsValues();
417
+        return isset($enabledAppsValues[$appId]);
418
+    }
419
+
420
+    /**
421
+     * Overwrite the `max-version` requirement for this app.
422
+     */
423
+    public function overwriteNextcloudRequirement(string $appId): void {
424
+        $ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
425
+        if (!in_array($appId, $ignoreMaxApps, true)) {
426
+            $ignoreMaxApps[] = $appId;
427
+        }
428
+        $this->config->setSystemValue('app_install_overwrite', $ignoreMaxApps);
429
+    }
430
+
431
+    /**
432
+     * Remove the `max-version` overwrite for this app.
433
+     * This means this app now again can not be enabled if the `max-version` is smaller than the current Nextcloud version.
434
+     */
435
+    public function removeOverwriteNextcloudRequirement(string $appId): void {
436
+        $ignoreMaxApps = $this->config->getSystemValue('app_install_overwrite', []);
437
+        $ignoreMaxApps = array_filter($ignoreMaxApps, fn (string $id) => $id !== $appId);
438
+        $this->config->setSystemValue('app_install_overwrite', $ignoreMaxApps);
439
+    }
440
+
441
+    public function loadApp(string $app): void {
442
+        if (isset($this->loadedApps[$app])) {
443
+            return;
444
+        }
445
+        $this->loadedApps[$app] = true;
446
+        $appPath = \OC_App::getAppPath($app);
447
+        if ($appPath === false) {
448
+            return;
449
+        }
450
+        $eventLogger = \OC::$server->get(IEventLogger::class);
451
+        $eventLogger->start("bootstrap:load_app:$app", "Load app: $app");
452
+
453
+        // in case someone calls loadApp() directly
454
+        \OC_App::registerAutoloading($app, $appPath);
455
+
456
+        if (is_file($appPath . '/appinfo/app.php')) {
457
+            $this->logger->error('/appinfo/app.php is not supported anymore, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [
458
+                'app' => $app,
459
+            ]);
460
+        }
461
+
462
+        $coordinator = \OCP\Server::get(Coordinator::class);
463
+        $coordinator->bootApp($app);
464
+
465
+        $eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it");
466
+        $info = $this->getAppInfo($app);
467
+        if (!empty($info['activity'])) {
468
+            $activityManager = \OC::$server->get(IActivityManager::class);
469
+            if (!empty($info['activity']['filters'])) {
470
+                foreach ($info['activity']['filters'] as $filter) {
471
+                    $activityManager->registerFilter($filter);
472
+                }
473
+            }
474
+            if (!empty($info['activity']['settings'])) {
475
+                foreach ($info['activity']['settings'] as $setting) {
476
+                    $activityManager->registerSetting($setting);
477
+                }
478
+            }
479
+            if (!empty($info['activity']['providers'])) {
480
+                foreach ($info['activity']['providers'] as $provider) {
481
+                    $activityManager->registerProvider($provider);
482
+                }
483
+            }
484
+        }
485
+
486
+        if (!empty($info['settings'])) {
487
+            $settingsManager = \OC::$server->get(ISettingsManager::class);
488
+            if (!empty($info['settings']['admin'])) {
489
+                foreach ($info['settings']['admin'] as $setting) {
490
+                    $settingsManager->registerSetting('admin', $setting);
491
+                }
492
+            }
493
+            if (!empty($info['settings']['admin-section'])) {
494
+                foreach ($info['settings']['admin-section'] as $section) {
495
+                    $settingsManager->registerSection('admin', $section);
496
+                }
497
+            }
498
+            if (!empty($info['settings']['personal'])) {
499
+                foreach ($info['settings']['personal'] as $setting) {
500
+                    $settingsManager->registerSetting('personal', $setting);
501
+                }
502
+            }
503
+            if (!empty($info['settings']['personal-section'])) {
504
+                foreach ($info['settings']['personal-section'] as $section) {
505
+                    $settingsManager->registerSection('personal', $section);
506
+                }
507
+            }
508
+        }
509
+
510
+        if (!empty($info['collaboration']['plugins'])) {
511
+            // deal with one or many plugin entries
512
+            $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
513
+                [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
514
+            $collaboratorSearch = null;
515
+            $autoCompleteManager = null;
516
+            foreach ($plugins as $plugin) {
517
+                if ($plugin['@attributes']['type'] === 'collaborator-search') {
518
+                    $pluginInfo = [
519
+                        'shareType' => $plugin['@attributes']['share-type'],
520
+                        'class' => $plugin['@value'],
521
+                    ];
522
+                    $collaboratorSearch ??= \OC::$server->get(ICollaboratorSearch::class);
523
+                    $collaboratorSearch->registerPlugin($pluginInfo);
524
+                } elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') {
525
+                    $autoCompleteManager ??= \OC::$server->get(IAutoCompleteManager::class);
526
+                    $autoCompleteManager->registerSorter($plugin['@value']);
527
+                }
528
+            }
529
+        }
530
+        $eventLogger->end("bootstrap:load_app:$app:info");
531
+
532
+        $eventLogger->end("bootstrap:load_app:$app");
533
+    }
534
+
535
+    /**
536
+     * Check if an app is loaded
537
+     * @param string $app app id
538
+     * @since 26.0.0
539
+     */
540
+    public function isAppLoaded(string $app): bool {
541
+        return isset($this->loadedApps[$app]);
542
+    }
543
+
544
+    /**
545
+     * Enable an app for every user
546
+     *
547
+     * @param string $appId
548
+     * @param bool $forceEnable
549
+     * @throws AppPathNotFoundException
550
+     * @throws \InvalidArgumentException if the application is not installed yet
551
+     */
552
+    public function enableApp(string $appId, bool $forceEnable = false): void {
553
+        // Check if app exists
554
+        $this->getAppPath($appId);
555
+
556
+        if ($this->config->getAppValue($appId, 'installed_version', '') === '') {
557
+            throw new \InvalidArgumentException("$appId is not installed, cannot be enabled.");
558
+        }
559
+
560
+        if ($forceEnable) {
561
+            $this->overwriteNextcloudRequirement($appId);
562
+        }
563
+
564
+        $this->enabledAppsCache[$appId] = 'yes';
565
+        $this->getAppConfig()->setValue($appId, 'enabled', 'yes');
566
+        $this->dispatcher->dispatchTyped(new AppEnableEvent($appId));
567
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent(
568
+            ManagerEvent::EVENT_APP_ENABLE, $appId
569
+        ));
570
+        $this->clearAppsCache();
571
+    }
572
+
573
+    /**
574
+     * Whether a list of types contains a protected app type
575
+     *
576
+     * @param string[] $types
577
+     * @return bool
578
+     */
579
+    public function hasProtectedAppType($types) {
580
+        if (empty($types)) {
581
+            return false;
582
+        }
583
+
584
+        $protectedTypes = array_intersect($this->protectedAppTypes, $types);
585
+        return !empty($protectedTypes);
586
+    }
587
+
588
+    /**
589
+     * Enable an app only for specific groups
590
+     *
591
+     * @param string $appId
592
+     * @param IGroup[] $groups
593
+     * @param bool $forceEnable
594
+     * @throws \InvalidArgumentException if app can't be enabled for groups
595
+     * @throws AppPathNotFoundException
596
+     */
597
+    public function enableAppForGroups(string $appId, array $groups, bool $forceEnable = false): void {
598
+        // Check if app exists
599
+        $this->getAppPath($appId);
600
+
601
+        $info = $this->getAppInfo($appId);
602
+        if (!empty($info['types']) && $this->hasProtectedAppType($info['types'])) {
603
+            throw new \InvalidArgumentException("$appId can't be enabled for groups.");
604
+        }
605
+
606
+        if ($this->config->getAppValue($appId, 'installed_version', '') === '') {
607
+            throw new \InvalidArgumentException("$appId is not installed, cannot be enabled.");
608
+        }
609
+
610
+        if ($forceEnable) {
611
+            $this->overwriteNextcloudRequirement($appId);
612
+        }
613
+
614
+        /** @var string[] $groupIds */
615
+        $groupIds = array_map(function ($group) {
616
+            /** @var IGroup $group */
617
+            return ($group instanceof IGroup)
618
+                ? $group->getGID()
619
+                : $group;
620
+        }, $groups);
621
+
622
+        $this->enabledAppsCache[$appId] = json_encode($groupIds);
623
+        $this->getAppConfig()->setValue($appId, 'enabled', json_encode($groupIds));
624
+        $this->dispatcher->dispatchTyped(new AppEnableEvent($appId, $groupIds));
625
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, new ManagerEvent(
626
+            ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
627
+        ));
628
+        $this->clearAppsCache();
629
+    }
630
+
631
+    /**
632
+     * Disable an app for every user
633
+     *
634
+     * @param string $appId
635
+     * @param bool $automaticDisabled
636
+     * @throws \Exception if app can't be disabled
637
+     */
638
+    public function disableApp($appId, $automaticDisabled = false): void {
639
+        if ($this->isAlwaysEnabled($appId)) {
640
+            throw new \Exception("$appId can't be disabled.");
641
+        }
642
+
643
+        if ($automaticDisabled) {
644
+            $previousSetting = $this->getAppConfig()->getValue($appId, 'enabled', 'yes');
645
+            if ($previousSetting !== 'yes' && $previousSetting !== 'no') {
646
+                $previousSetting = json_decode($previousSetting, true);
647
+            }
648
+            $this->autoDisabledApps[$appId] = $previousSetting;
649
+        }
650
+
651
+        unset($this->enabledAppsCache[$appId]);
652
+        $this->getAppConfig()->setValue($appId, 'enabled', 'no');
653
+
654
+        // run uninstall steps
655
+        $appData = $this->getAppInfo($appId);
656
+        if (!is_null($appData)) {
657
+            \OC_App::executeRepairSteps($appId, $appData['repair-steps']['uninstall']);
658
+        }
659
+
660
+        $this->dispatcher->dispatchTyped(new AppDisableEvent($appId));
661
+        $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent(
662
+            ManagerEvent::EVENT_APP_DISABLE, $appId
663
+        ));
664
+        $this->clearAppsCache();
665
+    }
666
+
667
+    /**
668
+     * Get the directory for the given app.
669
+     *
670
+     * @throws AppPathNotFoundException if app folder can't be found
671
+     */
672
+    public function getAppPath(string $appId): string {
673
+        $appPath = \OC_App::getAppPath($appId);
674
+        if ($appPath === false) {
675
+            throw new AppPathNotFoundException('Could not find path for ' . $appId);
676
+        }
677
+        return $appPath;
678
+    }
679
+
680
+    /**
681
+     * Get the web path for the given app.
682
+     *
683
+     * @param string $appId
684
+     * @return string
685
+     * @throws AppPathNotFoundException if app path can't be found
686
+     */
687
+    public function getAppWebPath(string $appId): string {
688
+        $appWebPath = \OC_App::getAppWebPath($appId);
689
+        if ($appWebPath === false) {
690
+            throw new AppPathNotFoundException('Could not find web path for ' . $appId);
691
+        }
692
+        return $appWebPath;
693
+    }
694
+
695
+    /**
696
+     * Clear the cached list of apps when enabling/disabling an app
697
+     */
698
+    public function clearAppsCache(): void {
699
+        $this->appInfos = [];
700
+    }
701
+
702
+    /**
703
+     * Returns a list of apps that need upgrade
704
+     *
705
+     * @param string $version Nextcloud version as array of version components
706
+     * @return array list of app info from apps that need an upgrade
707
+     *
708
+     * @internal
709
+     */
710
+    public function getAppsNeedingUpgrade($version) {
711
+        $appsToUpgrade = [];
712
+        $apps = $this->getEnabledApps();
713
+        foreach ($apps as $appId) {
714
+            $appInfo = $this->getAppInfo($appId);
715
+            $appDbVersion = $this->getAppConfig()->getValue($appId, 'installed_version');
716
+            if ($appDbVersion
717
+                && isset($appInfo['version'])
718
+                && version_compare($appInfo['version'], $appDbVersion, '>')
719
+                && \OC_App::isAppCompatible($version, $appInfo)
720
+            ) {
721
+                $appsToUpgrade[] = $appInfo;
722
+            }
723
+        }
724
+
725
+        return $appsToUpgrade;
726
+    }
727
+
728
+    /**
729
+     * Returns the app information from "appinfo/info.xml".
730
+     *
731
+     * @param string|null $lang
732
+     * @return array|null app info
733
+     */
734
+    public function getAppInfo(string $appId, bool $path = false, $lang = null) {
735
+        if ($path) {
736
+            throw new \InvalidArgumentException('Calling IAppManager::getAppInfo() with a path is no longer supported. Please call IAppManager::getAppInfoByPath() instead and verify that the path is good before calling.');
737
+        }
738
+        if ($lang === null && isset($this->appInfos[$appId])) {
739
+            return $this->appInfos[$appId];
740
+        }
741
+        try {
742
+            $appPath = $this->getAppPath($appId);
743
+        } catch (AppPathNotFoundException) {
744
+            return null;
745
+        }
746
+        $file = $appPath . '/appinfo/info.xml';
747
+
748
+        $data = $this->getAppInfoByPath($file, $lang);
749
+
750
+        if ($lang === null) {
751
+            $this->appInfos[$appId] = $data;
752
+        }
753
+
754
+        return $data;
755
+    }
756
+
757
+    public function getAppInfoByPath(string $path, ?string $lang = null): ?array {
758
+        if (!str_ends_with($path, '/appinfo/info.xml')) {
759
+            return null;
760
+        }
761
+
762
+        $parser = new InfoParser($this->memCacheFactory->createLocal('core.appinfo'));
763
+        $data = $parser->parse($path);
764
+
765
+        if (is_array($data)) {
766
+            $data = \OC_App::parseAppInfo($data, $lang);
767
+        }
768
+
769
+        return $data;
770
+    }
771
+
772
+    public function getAppVersion(string $appId, bool $useCache = true): string {
773
+        if (!$useCache || !isset($this->appVersions[$appId])) {
774
+            if ($appId === 'core') {
775
+                $this->appVersions[$appId] = $this->serverVersion->getVersionString();
776
+            } else {
777
+                $appInfo = $this->getAppInfo($appId);
778
+                $this->appVersions[$appId] = ($appInfo !== null && isset($appInfo['version'])) ? $appInfo['version'] : '0';
779
+            }
780
+        }
781
+        return $this->appVersions[$appId];
782
+    }
783
+
784
+    /**
785
+     * Returns the installed versions of all apps
786
+     *
787
+     * @return array<string, string>
788
+     */
789
+    public function getAppInstalledVersions(bool $onlyEnabled = false): array {
790
+        return $this->getAppConfig()->getAppInstalledVersions($onlyEnabled);
791
+    }
792
+
793
+    /**
794
+     * Returns a list of apps incompatible with the given version
795
+     *
796
+     * @param string $version Nextcloud version as array of version components
797
+     *
798
+     * @return array list of app info from incompatible apps
799
+     *
800
+     * @internal
801
+     */
802
+    public function getIncompatibleApps(string $version): array {
803
+        $apps = $this->getEnabledApps();
804
+        $incompatibleApps = [];
805
+        foreach ($apps as $appId) {
806
+            $info = $this->getAppInfo($appId);
807
+            if ($info === null) {
808
+                $incompatibleApps[] = ['id' => $appId, 'name' => $appId];
809
+            } elseif (!\OC_App::isAppCompatible($version, $info)) {
810
+                $incompatibleApps[] = $info;
811
+            }
812
+        }
813
+        return $incompatibleApps;
814
+    }
815
+
816
+    /**
817
+     * @inheritdoc
818
+     * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::isShipped()
819
+     */
820
+    public function isShipped($appId) {
821
+        $this->loadShippedJson();
822
+        return in_array($appId, $this->shippedApps, true);
823
+    }
824
+
825
+    private function isAlwaysEnabled(string $appId): bool {
826
+        if ($appId === 'core') {
827
+            return true;
828
+        }
829
+
830
+        $alwaysEnabled = $this->getAlwaysEnabledApps();
831
+        return in_array($appId, $alwaysEnabled, true);
832
+    }
833
+
834
+    /**
835
+     * In case you change this method, also change \OC\App\CodeChecker\InfoChecker::loadShippedJson()
836
+     * @throws \Exception
837
+     */
838
+    private function loadShippedJson(): void {
839
+        if ($this->shippedApps === null) {
840
+            $shippedJson = \OC::$SERVERROOT . '/core/shipped.json';
841
+            if (!file_exists($shippedJson)) {
842
+                throw new \Exception("File not found: $shippedJson");
843
+            }
844
+            $content = json_decode(file_get_contents($shippedJson), true);
845
+            $this->shippedApps = $content['shippedApps'];
846
+            $this->alwaysEnabled = $content['alwaysEnabled'];
847
+            $this->defaultEnabled = $content['defaultEnabled'];
848
+        }
849
+    }
850
+
851
+    /**
852
+     * @inheritdoc
853
+     */
854
+    public function getAlwaysEnabledApps() {
855
+        $this->loadShippedJson();
856
+        return $this->alwaysEnabled;
857
+    }
858
+
859
+    /**
860
+     * @inheritdoc
861
+     */
862
+    public function isDefaultEnabled(string $appId): bool {
863
+        return (in_array($appId, $this->getDefaultEnabledApps()));
864
+    }
865
+
866
+    /**
867
+     * @inheritdoc
868
+     */
869
+    public function getDefaultEnabledApps(): array {
870
+        $this->loadShippedJson();
871
+
872
+        return $this->defaultEnabled;
873
+    }
874
+
875
+    /**
876
+     * @inheritdoc
877
+     */
878
+    public function getDefaultAppForUser(?IUser $user = null, bool $withFallbacks = true): string {
879
+        $id = $this->getNavigationManager()->getDefaultEntryIdForUser($user, $withFallbacks);
880
+        $entry = $this->getNavigationManager()->get($id);
881
+        return (string)$entry['app'];
882
+    }
883
+
884
+    /**
885
+     * @inheritdoc
886
+     */
887
+    public function getDefaultApps(): array {
888
+        $ids = $this->getNavigationManager()->getDefaultEntryIds();
889
+
890
+        return array_values(array_unique(array_map(function (string $id) {
891
+            $entry = $this->getNavigationManager()->get($id);
892
+            return (string)$entry['app'];
893
+        }, $ids)));
894
+    }
895
+
896
+    /**
897
+     * @inheritdoc
898
+     */
899
+    public function setDefaultApps(array $defaultApps): void {
900
+        $entries = $this->getNavigationManager()->getAll();
901
+        $ids = [];
902
+        foreach ($defaultApps as $defaultApp) {
903
+            foreach ($entries as $entry) {
904
+                if ((string)$entry['app'] === $defaultApp) {
905
+                    $ids[] = (string)$entry['id'];
906
+                    break;
907
+                }
908
+            }
909
+        }
910
+        $this->getNavigationManager()->setDefaultEntryIds($ids);
911
+    }
912
+
913
+    public function isBackendRequired(string $backend): bool {
914
+        foreach ($this->appInfos as $appInfo) {
915
+            foreach ($appInfo['dependencies']['backend'] as $appBackend) {
916
+                if ($backend === $appBackend) {
917
+                    return true;
918
+                }
919
+            }
920
+        }
921
+
922
+        return false;
923
+    }
924
+
925
+    /**
926
+     * Clean the appId from forbidden characters
927
+     *
928
+     * @psalm-taint-escape callable
929
+     * @psalm-taint-escape cookie
930
+     * @psalm-taint-escape file
931
+     * @psalm-taint-escape has_quotes
932
+     * @psalm-taint-escape header
933
+     * @psalm-taint-escape html
934
+     * @psalm-taint-escape include
935
+     * @psalm-taint-escape ldap
936
+     * @psalm-taint-escape shell
937
+     * @psalm-taint-escape sql
938
+     * @psalm-taint-escape unserialize
939
+     */
940
+    public function cleanAppId(string $app): string {
941
+        /* Only lowercase alphanumeric is allowed */
942
+        return preg_replace('/(^[0-9_]|[^a-z0-9_]+|_$)/', '', $app);
943
+    }
944 944
 }
Please login to merge, or discard this patch.
lib/public/App/IAppManager.php 1 patch
Indentation   +321 added lines, -321 removed lines patch added patch discarded remove patch
@@ -19,325 +19,325 @@
 block discarded – undo
19 19
  * @since 8.0.0
20 20
  */
21 21
 interface IAppManager {
22
-	/**
23
-	 * @since 30.0.0
24
-	 */
25
-	public const BACKEND_CALDAV = 'caldav';
26
-
27
-	/**
28
-	 * Returns the app information from "appinfo/info.xml" for an app
29
-	 *
30
-	 * @param string|null $lang
31
-	 * @return array|null
32
-	 * @since 14.0.0
33
-	 * @since 31.0.0 Usage of $path is discontinued and throws an \InvalidArgumentException, use {@see self::getAppInfoByPath} instead.
34
-	 */
35
-	public function getAppInfo(string $appId, bool $path = false, $lang = null);
36
-
37
-	/**
38
-	 * Returns the app information from a given path ending with "/appinfo/info.xml"
39
-	 *
40
-	 * @since 31.0.0
41
-	 */
42
-	public function getAppInfoByPath(string $path, ?string $lang = null): ?array;
43
-
44
-	/**
45
-	 * Returns the app information from "appinfo/info.xml".
46
-	 *
47
-	 * @param string $appId
48
-	 * @param bool $useCache
49
-	 * @return string
50
-	 * @since 14.0.0
51
-	 */
52
-	public function getAppVersion(string $appId, bool $useCache = true): string;
53
-
54
-	/**
55
-	 * Returns the installed version of all apps
56
-	 *
57
-	 * @return array<string, string>
58
-	 * @since 32.0.0
59
-	 */
60
-	public function getAppInstalledVersions(bool $onlyEnabled = false): array;
61
-
62
-	/**
63
-	 * Returns the app icon or null if none is found
64
-	 *
65
-	 * @param string $appId
66
-	 * @param bool $dark Enable to request a dark icon variant, default is a white icon
67
-	 * @return string|null
68
-	 * @since 29.0.0
69
-	 */
70
-	public function getAppIcon(string $appId, bool $dark = false): ?string;
71
-
72
-	/**
73
-	 * Check if an app is enabled for user
74
-	 *
75
-	 * @param string $appId
76
-	 * @param \OCP\IUser|null $user (optional) if not defined, the currently loggedin user will be used
77
-	 * @return bool
78
-	 * @since 8.0.0
79
-	 */
80
-	public function isEnabledForUser($appId, $user = null);
81
-
82
-	/**
83
-	 * Check if an app is enabled in the instance
84
-	 *
85
-	 * Notice: This actually checks if the app is enabled and not only if it is installed.
86
-	 *
87
-	 * @param string $appId
88
-	 * @return bool
89
-	 * @since 8.0.0
90
-	 * @deprecated 32.0.0 Use either {@see self::isEnabledForUser} or {@see self::isEnabledForAnyone}
91
-	 */
92
-	public function isInstalled($appId);
93
-
94
-	/**
95
-	 * Check if an app is enabled in the instance, either for everyone or for specific groups
96
-	 *
97
-	 * @since 32.0.0
98
-	 */
99
-	public function isEnabledForAnyone(string $appId): bool;
100
-
101
-	/**
102
-	 * Check if an app should be enabled by default
103
-	 *
104
-	 * Notice: This actually checks if the app should be enabled by default
105
-	 * and not if currently installed/enabled
106
-	 *
107
-	 * @param string $appId ID of the app
108
-	 * @since 25.0.0
109
-	 */
110
-	public function isDefaultEnabled(string $appId):bool;
111
-
112
-	/**
113
-	 * Load an app, if not already loaded
114
-	 * @param string $app app id
115
-	 * @since 27.0.0
116
-	 */
117
-	public function loadApp(string $app): void;
118
-
119
-	/**
120
-	 * Check if an app is loaded
121
-	 * @param string $app app id
122
-	 * @since 27.0.0
123
-	 */
124
-	public function isAppLoaded(string $app): bool;
125
-
126
-	/**
127
-	 * Enable an app for every user
128
-	 *
129
-	 * @param string $appId
130
-	 * @param bool $forceEnable
131
-	 * @throws AppPathNotFoundException
132
-	 * @since 8.0.0
133
-	 */
134
-	public function enableApp(string $appId, bool $forceEnable = false): void;
135
-
136
-	/**
137
-	 * Whether a list of types contains a protected app type
138
-	 *
139
-	 * @param string[] $types
140
-	 * @return bool
141
-	 * @since 12.0.0
142
-	 */
143
-	public function hasProtectedAppType($types);
144
-
145
-	/**
146
-	 * Enable an app only for specific groups
147
-	 *
148
-	 * @param string $appId
149
-	 * @param \OCP\IGroup[] $groups
150
-	 * @param bool $forceEnable
151
-	 * @throws \Exception
152
-	 * @since 8.0.0
153
-	 */
154
-	public function enableAppForGroups(string $appId, array $groups, bool $forceEnable = false): void;
155
-
156
-	/**
157
-	 * Disable an app for every user
158
-	 *
159
-	 * @param string $appId
160
-	 * @param bool $automaticDisabled
161
-	 * @since 8.0.0
162
-	 */
163
-	public function disableApp($appId, $automaticDisabled = false): void;
164
-
165
-	/**
166
-	 * Get the directory for the given app.
167
-	 *
168
-	 * @since 11.0.0
169
-	 * @throws AppPathNotFoundException
170
-	 */
171
-	public function getAppPath(string $appId): string;
172
-
173
-	/**
174
-	 * Get the web path for the given app.
175
-	 *
176
-	 * @param string $appId
177
-	 * @return string
178
-	 * @since 18.0.0
179
-	 * @throws AppPathNotFoundException
180
-	 */
181
-	public function getAppWebPath(string $appId): string;
182
-
183
-	/**
184
-	 * List all apps enabled for a user
185
-	 *
186
-	 * @param \OCP\IUser $user
187
-	 * @return list<string>
188
-	 * @since 8.1.0
189
-	 */
190
-	public function getEnabledAppsForUser(IUser $user);
191
-
192
-	/**
193
-	 * List all installed apps
194
-	 *
195
-	 * @return string[]
196
-	 * @since 8.1.0
197
-	 * @deprecated 32.0.0 Use either {@see self::getEnabledApps} or {@see self::getEnabledAppsForUser}
198
-	 */
199
-	public function getInstalledApps();
200
-
201
-	/**
202
-	 * List all apps enabled, either for everyone or for specific groups only
203
-	 *
204
-	 * @return list<string>
205
-	 * @since 32.0.0
206
-	 */
207
-	public function getEnabledApps(): array;
208
-
209
-	/**
210
-	 * Clear the cached list of apps when enabling/disabling an app
211
-	 * @since 8.1.0
212
-	 */
213
-	public function clearAppsCache(): void;
214
-
215
-	/**
216
-	 * @param string $appId
217
-	 * @return boolean
218
-	 * @since 9.0.0
219
-	 */
220
-	public function isShipped($appId);
221
-
222
-	/**
223
-	 * Loads all apps
224
-	 *
225
-	 * @param string[] $types
226
-	 * @return bool
227
-	 *
228
-	 * This function walks through the Nextcloud directory and loads all apps
229
-	 * it can find. A directory contains an app if the file `/appinfo/info.xml`
230
-	 * exists.
231
-	 *
232
-	 * if $types is set to non-empty array, only apps of those types will be loaded
233
-	 * @since 27.0.0
234
-	 */
235
-	public function loadApps(array $types = []): bool;
236
-
237
-	/**
238
-	 * Check if an app is of a specific type
239
-	 * @since 27.0.0
240
-	 */
241
-	public function isType(string $app, array $types): bool;
242
-
243
-	/**
244
-	 * @return string[]
245
-	 * @since 9.0.0
246
-	 */
247
-	public function getAlwaysEnabledApps();
248
-
249
-	/**
250
-	 * @return string[] app IDs
251
-	 * @since 25.0.0
252
-	 */
253
-	public function getDefaultEnabledApps(): array;
254
-
255
-	/**
256
-	 * @param \OCP\IGroup $group
257
-	 * @return String[]
258
-	 * @since 17.0.0
259
-	 */
260
-	public function getEnabledAppsForGroup(IGroup $group): array;
261
-
262
-	/**
263
-	 * @param String $appId
264
-	 * @return string[]
265
-	 * @since 17.0.0
266
-	 */
267
-	public function getAppRestriction(string $appId): array;
268
-
269
-	/**
270
-	 * Returns the id of the user's default app
271
-	 *
272
-	 * If `user` is not passed, the currently logged in user will be used
273
-	 *
274
-	 * @param ?IUser $user User to query default app for
275
-	 * @param bool $withFallbacks Include fallback values if no default app was configured manually
276
-	 *                            Before falling back to predefined default apps,
277
-	 *                            the user defined app order is considered and the first app would be used as the fallback.
278
-	 *
279
-	 * @since 25.0.6
280
-	 * @since 28.0.0 Added optional $withFallbacks parameter
281
-	 * @deprecated 31.0.0
282
-	 * Use @see \OCP\INavigationManager::getDefaultEntryIdForUser() instead
283
-	 */
284
-	public function getDefaultAppForUser(?IUser $user = null, bool $withFallbacks = true): string;
285
-
286
-	/**
287
-	 * Get the global default apps with fallbacks
288
-	 *
289
-	 * @return string[] The default applications
290
-	 * @since 28.0.0
291
-	 * @deprecated 31.0.0
292
-	 * Use @see \OCP\INavigationManager::getDefaultEntryIds() instead
293
-	 */
294
-	public function getDefaultApps(): array;
295
-
296
-	/**
297
-	 * Set the global default apps with fallbacks
298
-	 *
299
-	 * @param string[] $defaultApps
300
-	 * @throws \InvalidArgumentException If any of the apps is not installed
301
-	 * @since 28.0.0
302
-	 * @deprecated 31.0.0
303
-	 * Use @see \OCP\INavigationManager::setDefaultEntryIds() instead
304
-	 */
305
-	public function setDefaultApps(array $defaultApps): void;
306
-
307
-	/**
308
-	 * Check whether the given backend is required by at least one app.
309
-	 *
310
-	 * @param self::BACKEND_* $backend Name of the backend, one of `self::BACKEND_*`
311
-	 * @return bool True if at least one app requires the backend
312
-	 *
313
-	 * @since 30.0.0
314
-	 */
315
-	public function isBackendRequired(string $backend): bool;
316
-
317
-	/**
318
-	 * Clean the appId from forbidden characters
319
-	 *
320
-	 * @psalm-taint-escape callable
321
-	 * @psalm-taint-escape cookie
322
-	 * @psalm-taint-escape file
323
-	 * @psalm-taint-escape has_quotes
324
-	 * @psalm-taint-escape header
325
-	 * @psalm-taint-escape html
326
-	 * @psalm-taint-escape include
327
-	 * @psalm-taint-escape ldap
328
-	 * @psalm-taint-escape shell
329
-	 * @psalm-taint-escape sql
330
-	 * @psalm-taint-escape unserialize
331
-	 *
332
-	 * @since 31.0.0
333
-	 */
334
-	public function cleanAppId(string $app): string;
335
-
336
-	/**
337
-	 * Get a list of all apps in the apps folder
338
-	 *
339
-	 * @return list<string> an array of app names (string IDs)
340
-	 * @since 31.0.0
341
-	 */
342
-	public function getAllAppsInAppsFolders(): array;
22
+    /**
23
+     * @since 30.0.0
24
+     */
25
+    public const BACKEND_CALDAV = 'caldav';
26
+
27
+    /**
28
+     * Returns the app information from "appinfo/info.xml" for an app
29
+     *
30
+     * @param string|null $lang
31
+     * @return array|null
32
+     * @since 14.0.0
33
+     * @since 31.0.0 Usage of $path is discontinued and throws an \InvalidArgumentException, use {@see self::getAppInfoByPath} instead.
34
+     */
35
+    public function getAppInfo(string $appId, bool $path = false, $lang = null);
36
+
37
+    /**
38
+     * Returns the app information from a given path ending with "/appinfo/info.xml"
39
+     *
40
+     * @since 31.0.0
41
+     */
42
+    public function getAppInfoByPath(string $path, ?string $lang = null): ?array;
43
+
44
+    /**
45
+     * Returns the app information from "appinfo/info.xml".
46
+     *
47
+     * @param string $appId
48
+     * @param bool $useCache
49
+     * @return string
50
+     * @since 14.0.0
51
+     */
52
+    public function getAppVersion(string $appId, bool $useCache = true): string;
53
+
54
+    /**
55
+     * Returns the installed version of all apps
56
+     *
57
+     * @return array<string, string>
58
+     * @since 32.0.0
59
+     */
60
+    public function getAppInstalledVersions(bool $onlyEnabled = false): array;
61
+
62
+    /**
63
+     * Returns the app icon or null if none is found
64
+     *
65
+     * @param string $appId
66
+     * @param bool $dark Enable to request a dark icon variant, default is a white icon
67
+     * @return string|null
68
+     * @since 29.0.0
69
+     */
70
+    public function getAppIcon(string $appId, bool $dark = false): ?string;
71
+
72
+    /**
73
+     * Check if an app is enabled for user
74
+     *
75
+     * @param string $appId
76
+     * @param \OCP\IUser|null $user (optional) if not defined, the currently loggedin user will be used
77
+     * @return bool
78
+     * @since 8.0.0
79
+     */
80
+    public function isEnabledForUser($appId, $user = null);
81
+
82
+    /**
83
+     * Check if an app is enabled in the instance
84
+     *
85
+     * Notice: This actually checks if the app is enabled and not only if it is installed.
86
+     *
87
+     * @param string $appId
88
+     * @return bool
89
+     * @since 8.0.0
90
+     * @deprecated 32.0.0 Use either {@see self::isEnabledForUser} or {@see self::isEnabledForAnyone}
91
+     */
92
+    public function isInstalled($appId);
93
+
94
+    /**
95
+     * Check if an app is enabled in the instance, either for everyone or for specific groups
96
+     *
97
+     * @since 32.0.0
98
+     */
99
+    public function isEnabledForAnyone(string $appId): bool;
100
+
101
+    /**
102
+     * Check if an app should be enabled by default
103
+     *
104
+     * Notice: This actually checks if the app should be enabled by default
105
+     * and not if currently installed/enabled
106
+     *
107
+     * @param string $appId ID of the app
108
+     * @since 25.0.0
109
+     */
110
+    public function isDefaultEnabled(string $appId):bool;
111
+
112
+    /**
113
+     * Load an app, if not already loaded
114
+     * @param string $app app id
115
+     * @since 27.0.0
116
+     */
117
+    public function loadApp(string $app): void;
118
+
119
+    /**
120
+     * Check if an app is loaded
121
+     * @param string $app app id
122
+     * @since 27.0.0
123
+     */
124
+    public function isAppLoaded(string $app): bool;
125
+
126
+    /**
127
+     * Enable an app for every user
128
+     *
129
+     * @param string $appId
130
+     * @param bool $forceEnable
131
+     * @throws AppPathNotFoundException
132
+     * @since 8.0.0
133
+     */
134
+    public function enableApp(string $appId, bool $forceEnable = false): void;
135
+
136
+    /**
137
+     * Whether a list of types contains a protected app type
138
+     *
139
+     * @param string[] $types
140
+     * @return bool
141
+     * @since 12.0.0
142
+     */
143
+    public function hasProtectedAppType($types);
144
+
145
+    /**
146
+     * Enable an app only for specific groups
147
+     *
148
+     * @param string $appId
149
+     * @param \OCP\IGroup[] $groups
150
+     * @param bool $forceEnable
151
+     * @throws \Exception
152
+     * @since 8.0.0
153
+     */
154
+    public function enableAppForGroups(string $appId, array $groups, bool $forceEnable = false): void;
155
+
156
+    /**
157
+     * Disable an app for every user
158
+     *
159
+     * @param string $appId
160
+     * @param bool $automaticDisabled
161
+     * @since 8.0.0
162
+     */
163
+    public function disableApp($appId, $automaticDisabled = false): void;
164
+
165
+    /**
166
+     * Get the directory for the given app.
167
+     *
168
+     * @since 11.0.0
169
+     * @throws AppPathNotFoundException
170
+     */
171
+    public function getAppPath(string $appId): string;
172
+
173
+    /**
174
+     * Get the web path for the given app.
175
+     *
176
+     * @param string $appId
177
+     * @return string
178
+     * @since 18.0.0
179
+     * @throws AppPathNotFoundException
180
+     */
181
+    public function getAppWebPath(string $appId): string;
182
+
183
+    /**
184
+     * List all apps enabled for a user
185
+     *
186
+     * @param \OCP\IUser $user
187
+     * @return list<string>
188
+     * @since 8.1.0
189
+     */
190
+    public function getEnabledAppsForUser(IUser $user);
191
+
192
+    /**
193
+     * List all installed apps
194
+     *
195
+     * @return string[]
196
+     * @since 8.1.0
197
+     * @deprecated 32.0.0 Use either {@see self::getEnabledApps} or {@see self::getEnabledAppsForUser}
198
+     */
199
+    public function getInstalledApps();
200
+
201
+    /**
202
+     * List all apps enabled, either for everyone or for specific groups only
203
+     *
204
+     * @return list<string>
205
+     * @since 32.0.0
206
+     */
207
+    public function getEnabledApps(): array;
208
+
209
+    /**
210
+     * Clear the cached list of apps when enabling/disabling an app
211
+     * @since 8.1.0
212
+     */
213
+    public function clearAppsCache(): void;
214
+
215
+    /**
216
+     * @param string $appId
217
+     * @return boolean
218
+     * @since 9.0.0
219
+     */
220
+    public function isShipped($appId);
221
+
222
+    /**
223
+     * Loads all apps
224
+     *
225
+     * @param string[] $types
226
+     * @return bool
227
+     *
228
+     * This function walks through the Nextcloud directory and loads all apps
229
+     * it can find. A directory contains an app if the file `/appinfo/info.xml`
230
+     * exists.
231
+     *
232
+     * if $types is set to non-empty array, only apps of those types will be loaded
233
+     * @since 27.0.0
234
+     */
235
+    public function loadApps(array $types = []): bool;
236
+
237
+    /**
238
+     * Check if an app is of a specific type
239
+     * @since 27.0.0
240
+     */
241
+    public function isType(string $app, array $types): bool;
242
+
243
+    /**
244
+     * @return string[]
245
+     * @since 9.0.0
246
+     */
247
+    public function getAlwaysEnabledApps();
248
+
249
+    /**
250
+     * @return string[] app IDs
251
+     * @since 25.0.0
252
+     */
253
+    public function getDefaultEnabledApps(): array;
254
+
255
+    /**
256
+     * @param \OCP\IGroup $group
257
+     * @return String[]
258
+     * @since 17.0.0
259
+     */
260
+    public function getEnabledAppsForGroup(IGroup $group): array;
261
+
262
+    /**
263
+     * @param String $appId
264
+     * @return string[]
265
+     * @since 17.0.0
266
+     */
267
+    public function getAppRestriction(string $appId): array;
268
+
269
+    /**
270
+     * Returns the id of the user's default app
271
+     *
272
+     * If `user` is not passed, the currently logged in user will be used
273
+     *
274
+     * @param ?IUser $user User to query default app for
275
+     * @param bool $withFallbacks Include fallback values if no default app was configured manually
276
+     *                            Before falling back to predefined default apps,
277
+     *                            the user defined app order is considered and the first app would be used as the fallback.
278
+     *
279
+     * @since 25.0.6
280
+     * @since 28.0.0 Added optional $withFallbacks parameter
281
+     * @deprecated 31.0.0
282
+     * Use @see \OCP\INavigationManager::getDefaultEntryIdForUser() instead
283
+     */
284
+    public function getDefaultAppForUser(?IUser $user = null, bool $withFallbacks = true): string;
285
+
286
+    /**
287
+     * Get the global default apps with fallbacks
288
+     *
289
+     * @return string[] The default applications
290
+     * @since 28.0.0
291
+     * @deprecated 31.0.0
292
+     * Use @see \OCP\INavigationManager::getDefaultEntryIds() instead
293
+     */
294
+    public function getDefaultApps(): array;
295
+
296
+    /**
297
+     * Set the global default apps with fallbacks
298
+     *
299
+     * @param string[] $defaultApps
300
+     * @throws \InvalidArgumentException If any of the apps is not installed
301
+     * @since 28.0.0
302
+     * @deprecated 31.0.0
303
+     * Use @see \OCP\INavigationManager::setDefaultEntryIds() instead
304
+     */
305
+    public function setDefaultApps(array $defaultApps): void;
306
+
307
+    /**
308
+     * Check whether the given backend is required by at least one app.
309
+     *
310
+     * @param self::BACKEND_* $backend Name of the backend, one of `self::BACKEND_*`
311
+     * @return bool True if at least one app requires the backend
312
+     *
313
+     * @since 30.0.0
314
+     */
315
+    public function isBackendRequired(string $backend): bool;
316
+
317
+    /**
318
+     * Clean the appId from forbidden characters
319
+     *
320
+     * @psalm-taint-escape callable
321
+     * @psalm-taint-escape cookie
322
+     * @psalm-taint-escape file
323
+     * @psalm-taint-escape has_quotes
324
+     * @psalm-taint-escape header
325
+     * @psalm-taint-escape html
326
+     * @psalm-taint-escape include
327
+     * @psalm-taint-escape ldap
328
+     * @psalm-taint-escape shell
329
+     * @psalm-taint-escape sql
330
+     * @psalm-taint-escape unserialize
331
+     *
332
+     * @since 31.0.0
333
+     */
334
+    public function cleanAppId(string $app): string;
335
+
336
+    /**
337
+     * Get a list of all apps in the apps folder
338
+     *
339
+     * @return list<string> an array of app names (string IDs)
340
+     * @since 31.0.0
341
+     */
342
+    public function getAllAppsInAppsFolders(): array;
343 343
 }
Please login to merge, or discard this patch.