Skip to content

Commit 1bdb66e

Browse files
committed
Plugin: Azure: Register users as student and then update their status according defined groups - refs BT#21930
1 parent a600f8b commit 1bdb66e

File tree

5 files changed

+164
-96
lines changed

5 files changed

+164
-96
lines changed

main/inc/lib/usermanager.lib.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6250,7 +6250,7 @@ public static function get_favicon_from_url($url1, $url2 = null)
62506250
return $icon_link;
62516251
}
62526252

6253-
public static function addUserAsAdmin(User $user)
6253+
public static function addUserAsAdmin(User $user, bool $andFlush = true)
62546254
{
62556255
if ($user) {
62566256
$userId = $user->getId();
@@ -6261,19 +6261,19 @@ public static function addUserAsAdmin(User $user)
62616261
}
62626262

62636263
$user->addRole('ROLE_SUPER_ADMIN');
6264-
self::getManager()->updateUser($user, true);
6264+
self::getManager()->updateUser($user, $andFlush);
62656265
}
62666266
}
62676267

6268-
public static function removeUserAdmin(User $user)
6268+
public static function removeUserAdmin(User $user, bool $andFlush = true)
62696269
{
62706270
$userId = (int) $user->getId();
62716271
if (self::is_admin($userId)) {
62726272
$table = Database::get_main_table(TABLE_MAIN_ADMIN);
62736273
$sql = "DELETE FROM $table WHERE user_id = $userId";
62746274
Database::query($sql);
62756275
$user->removeRole('ROLE_SUPER_ADMIN');
6276-
self::getManager()->updateUser($user, true);
6276+
self::getManager()->updateUser($user, $andFlush);
62776277
}
62786278
}
62796279

plugin/azure_active_directory/src/AzureActiveDirectory.php

Lines changed: 33 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/* For license terms, see /license.txt */
33

4-
use League\OAuth2\Client\Token\AccessTokenInterface;
4+
use Chamilo\UserBundle\Entity\User;
55
use TheNetworg\OAuth2\Client\Provider\Azure;
66

77
/**
@@ -213,11 +213,7 @@ public function getUserIdByVerificationOrder(array $azureUserData, string $azure
213213
* @throws Exception
214214
*/
215215
public function registerUser(
216-
AccessTokenInterface &$token,
217-
Azure $provider,
218216
array $azureUserInfo,
219-
string $apiGroupsRef = 'me/memberOf',
220-
string $objectIdKey = 'objectId',
221217
string $azureUidKey = 'objectId'
222218
) {
223219
if (empty($azureUserInfo)) {
@@ -241,15 +237,13 @@ public function registerUser(
241237
$authSource,
242238
$active,
243239
$extra,
244-
$userRole,
245-
$isAdmin,
246-
] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
240+
] = $this->formatUserData($azureUserInfo, $azureUidKey);
247241

248242
// If the option is set to create users, create it
249243
$userId = UserManager::create_user(
250244
$firstNme,
251245
$lastName,
252-
$userRole,
246+
STUDENT,
253247
$email,
254248
$username,
255249
'',
@@ -263,8 +257,7 @@ public function registerUser(
263257
null,
264258
$extra,
265259
null,
266-
null,
267-
$isAdmin
260+
null
268261
);
269262

270263
if (!$userId) {
@@ -284,9 +277,7 @@ public function registerUser(
284277
$authSource,
285278
$active,
286279
$extra,
287-
$userRole,
288-
$isAdmin,
289-
] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
280+
] = $this->formatUserData($azureUserInfo, $azureUidKey);
290281

291282
$userId = UserManager::update_user(
292283
$userId,
@@ -296,7 +287,7 @@ public function registerUser(
296287
'',
297288
$authSource,
298289
$email,
299-
$userRole,
290+
STUDENT,
300291
null,
301292
$phone,
302293
null,
@@ -319,24 +310,9 @@ public function registerUser(
319310
* @throws Exception
320311
*/
321312
private function formatUserData(
322-
AccessTokenInterface &$token,
323-
Azure $provider,
324313
array $azureUserInfo,
325-
string $apiGroupsRef,
326-
string $groupObjectIdKey,
327314
string $azureUidKey
328315
): array {
329-
try {
330-
[$userRole, $isAdmin] = $this->getUserRoleAndCheckIsAdmin(
331-
$token,
332-
$provider,
333-
$apiGroupsRef,
334-
$groupObjectIdKey
335-
);
336-
} catch (Exception $e) {
337-
throw new Exception('Exception when formatting user '.$azureUserInfo[$azureUidKey].' data: '.$e->getMessage());
338-
}
339-
340316
$phone = null;
341317

342318
if (isset($azureUserInfo['telephoneNumber'])) {
@@ -369,52 +345,44 @@ private function formatUserData(
369345
$authSource,
370346
$active,
371347
$extra,
372-
$userRole,
373-
$isAdmin,
374348
];
375349
}
376350

377351
/**
378-
* @throws Exception
352+
* @return array<string, string|false>
379353
*/
380-
private function getUserRoleAndCheckIsAdmin(
381-
AccessTokenInterface &$token,
382-
Azure $provider,
383-
string $apiRef = 'me/memberOf',
384-
string $groupObjectIdKey = 'objectId'
385-
): array {
386-
// If any specific group ID has been defined for a specific role, use that
387-
// ID to give the user the right role
388-
$userRole = STUDENT;
389-
$isAdmin = false;
390-
391-
$groupRoles = [
354+
public function getGroupUidPerRole(): array
355+
{
356+
$groupUidList = [
392357
'admin' => $this->get(self::SETTING_GROUP_ID_ADMIN),
393358
'sessionAdmin' => $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN),
394359
'teacher' => $this->get(self::SETTING_GROUP_ID_TEACHER),
395360
];
396361

397-
if ($groupRoles = array_filter($groupRoles)) {
398-
try {
399-
$groups = $provider->get($apiRef, $token);
400-
} catch (Exception $e) {
401-
throw new Exception('Exception when requesting user groups from Azure: '.$e->getMessage());
402-
}
362+
return array_filter($groupUidList);
363+
}
403364

404-
foreach ($groups as $group) {
405-
$groupId = $group[$groupObjectIdKey];
406-
407-
if (isset($groupRoles['admin']) && $groupRoles['admin'] === $groupId) {
408-
$userRole = COURSEMANAGER;
409-
$isAdmin = true;
410-
} elseif (isset($groupRoles['sessionAdmin']) && $groupRoles['sessionAdmin'] === $groupId) {
411-
$userRole = SESSIONADMIN;
412-
} elseif (isset($groupRoles['teacher']) && $groupRoles['teacher'] === $groupId && $userRole !== SESSIONADMIN) {
413-
$userRole = COURSEMANAGER;
414-
}
415-
}
416-
}
365+
/**
366+
* @return array<string, callable>
367+
*/
368+
public function getActionPerRole(): array
369+
{
370+
return [
371+
'admin' => function (User $user) {
372+
$user->setStatus(COURSEMANAGER);
373+
374+
UserManager::addUserAsAdmin($user, false);
375+
},
376+
'sessionAdmin' => function (User $user) {
377+
$user->setStatus(SESSIONADMIN);
417378

418-
return [$userRole, $isAdmin];
379+
UserManager::removeUserAdmin($user, false);
380+
},
381+
'teacher' => function (User $user) {
382+
$user->setStatus(COURSEMANAGER);
383+
384+
UserManager::removeUserAdmin($user, false);
385+
},
386+
];
419387
}
420388
}

plugin/azure_active_directory/src/AzureCommand.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,17 @@ protected function getToken(?AccessTokenInterface $currentToken = null): AccessT
4242

4343
return $currentToken;
4444
}
45+
46+
/**
47+
* @throws IdentityProviderException
48+
*/
49+
protected function generateOrRefreshToken(?AccessTokenInterface &$token)
50+
{
51+
if (!$token || ($token->getExpires() && !$token->getRefreshToken())) {
52+
$token = $this->provider->getAccessToken(
53+
'client_credentials',
54+
['resource' => $this->provider->resource]
55+
);
56+
}
57+
}
4558
}

plugin/azure_active_directory/src/AzureSyncUsersCommand.php

Lines changed: 87 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
/* For license terms, see /license.txt */
44

5-
use League\OAuth2\Client\Token\AccessTokenInterface;
5+
use Chamilo\UserBundle\Entity\User;
66

77
class AzureSyncUsersCommand extends AzureCommand
88
{
@@ -15,33 +15,54 @@ public function __invoke(): Generator
1515
{
1616
yield 'Synchronizing users from Azure.';
1717

18-
$token = $this->getToken();
19-
18+
/** @var array<string, int> $existingUsers */
2019
$existingUsers = [];
2120

22-
foreach ($this->getAzureUsers($token) as $azureUserInfo) {
21+
foreach ($this->getAzureUsers() as $azureUserInfo) {
2322
try {
24-
$token = $this->getToken($token);
25-
26-
$userId = $this->plugin->registerUser(
27-
$token,
28-
$this->provider,
29-
$azureUserInfo,
30-
'users/'.$azureUserInfo['id'].'/memberOf',
31-
'id',
32-
'id'
33-
);
23+
$userId = $this->plugin->registerUser($azureUserInfo, 'id');
3424
} catch (Exception $e) {
3525
yield $e->getMessage();
3626

3727
continue;
3828
}
3929

40-
$existingUsers[] = $userId;
30+
$existingUsers[$azureUserInfo['id']] = $userId;
31+
32+
yield sprintf('User (ID %d) with received info: %s ', $userId, serialize($azureUserInfo));
33+
}
34+
35+
yield '----------------';
36+
yield 'Updating users status';
37+
38+
$roleGroups = $this->plugin->getGroupUidPerRole();
39+
$roleActions = $this->plugin->getActionPerRole();
40+
41+
$userManager = UserManager::getManager();
42+
$em = Database::getManager();
43+
44+
foreach ($roleGroups as $userRole => $groupUid) {
45+
$azureGroupMembersInfo = iterator_to_array($this->getAzureGroupMembers($groupUid));
46+
$azureGroupMembersUids = array_column($azureGroupMembersInfo, 'id');
47+
48+
foreach ($azureGroupMembersUids as $azureGroupMembersUid) {
49+
$userId = $existingUsers[$azureGroupMembersUid] ?? null;
50+
51+
if (!$userId) {
52+
continue;
53+
}
54+
55+
if (isset($roleActions[$userRole])) {
56+
/** @var User $user */
57+
$user = $userManager->find($userId);
58+
59+
$roleActions[$userRole]($user);
4160

42-
$userInfo = api_get_user_info($userId);
61+
yield sprintf('User (ID %d) status %s', $userId, $userRole);
62+
}
63+
}
4364

44-
yield sprintf('User info: %s', serialize($userInfo));
65+
$em->flush();
4566
}
4667

4768
if ('true' === $this->plugin->get(AzureActiveDirectory::SETTING_DEACTIVATE_NONEXISTING_USERS)) {
@@ -73,7 +94,7 @@ function ($user) {
7394
*
7495
* @return Generator<int, array<string, string>>
7596
*/
76-
private function getAzureUsers(AccessTokenInterface $token): Generator
97+
private function getAzureUsers(): Generator
7798
{
7899
$userFields = [
79100
'givenName',
@@ -93,8 +114,10 @@ private function getAzureUsers(AccessTokenInterface $token): Generator
93114
implode(',', $userFields)
94115
);
95116

117+
$token = null;
118+
96119
do {
97-
$token = $this->getToken($token);
120+
$this->generateOrRefreshToken($token);
98121

99122
try {
100123
$azureUsersRequest = $this->provider->request(
@@ -120,4 +143,49 @@ private function getAzureUsers(AccessTokenInterface $token): Generator
120143
}
121144
} while ($hasNextLink);
122145
}
146+
147+
/**
148+
* @throws Exception
149+
*/
150+
public function getAzureGroupMembers(string $groupUid): Generator
151+
{
152+
$userFields = [
153+
'id',
154+
];
155+
156+
$query = sprintf(
157+
'$top=%d&$select=%s',
158+
AzureActiveDirectory::API_PAGE_SIZE,
159+
implode(',', $userFields)
160+
);
161+
162+
$token = null;
163+
164+
do {
165+
$this->generateOrRefreshToken($token);
166+
167+
try {
168+
$azureGroupMembersRequest = $this->provider->request(
169+
'get',
170+
"groups/$groupUid/members?$query",
171+
$token
172+
);
173+
} catch (Exception $e) {
174+
throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage());
175+
}
176+
177+
$azureGroupMembers = $azureGroupMembersRequest['value'] ?? [];
178+
179+
foreach ($azureGroupMembers as $azureGroupMember) {
180+
yield $azureGroupMember;
181+
}
182+
183+
$hasNextLink = false;
184+
185+
if (!empty($azureGroupMembersRequest['@odata.nextLink'])) {
186+
$hasNextLink = true;
187+
$query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY);
188+
}
189+
} while ($hasNextLink);
190+
}
123191
}

0 commit comments

Comments
 (0)