From c492a14086f6dec4db19194973ec40ce1bfb970c Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Wed, 28 Aug 2024 18:12:36 -0500
Subject: [PATCH 01/22] Plugin: Azure: Allows the user to be verified based on
the internal Azure ID - refs BT#21930
---
main/auth/external_login/login.azure.php | 10 ++++++++++
plugin/azure_active_directory/lang/dutch.php | 1 +
.../azure_active_directory/lang/english.php | 1 +
plugin/azure_active_directory/lang/french.php | 1 +
.../azure_active_directory/lang/spanish.php | 1 +
.../src/AzureActiveDirectory.php | 7 +++++++
.../azure_active_directory/src/callback.php | 20 +++++++++++++++++--
7 files changed, 39 insertions(+), 2 deletions(-)
diff --git a/main/auth/external_login/login.azure.php b/main/auth/external_login/login.azure.php
index 95acea4530..e00da56c87 100644
--- a/main/auth/external_login/login.azure.php
+++ b/main/auth/external_login/login.azure.php
@@ -12,6 +12,16 @@ if ($uData['auth_source'] === 'azure') {
api_not_allowed(true);
}
+ $uidField = new ExtraFieldValue('user');
+ $uidValue = $uidField->get_values_by_handler_and_field_variable(
+ $uData['user_id'],
+ AzureActiveDirectory::EXTRA_FIELD_AZURE_UID
+ );
+
+ if (empty($uidValue) || empty($uidValue['value'])) {
+ api_not_allowed(true);
+ }
+
$azureIdField = new ExtraFieldValue('user');
$azureIdValue = $azureIdField->get_values_by_handler_and_field_variable(
$uData['user_id'],
diff --git a/plugin/azure_active_directory/lang/dutch.php b/plugin/azure_active_directory/lang/dutch.php
index 7f3e03e563..2e2dbb6d77 100644
--- a/plugin/azure_active_directory/lang/dutch.php
+++ b/plugin/azure_active_directory/lang/dutch.php
@@ -24,6 +24,7 @@ $strings['management_login_name'] = 'Naam voor de beheeraanmelding';
$strings['management_login_name_help'] = 'De standaardinstelling is "Beheer login".';
$strings['OrganisationEmail'] = 'Organisatie e-mail';
$strings['AzureId'] = 'Azure ID (mailNickname)';
+$strings['AzureUid'] = 'Azure UID (internal ID)';
$strings['ManagementLogin'] = 'Beheer Login';
$strings['InvalidId'] = 'Deze identificatie is niet geldig (verkeerde log-in of wachtwoord). Errocode: AZMNF';
$strings['provisioning'] = 'Geautomatiseerde inrichting';
diff --git a/plugin/azure_active_directory/lang/english.php b/plugin/azure_active_directory/lang/english.php
index 5faf822a74..61c4bf9b75 100644
--- a/plugin/azure_active_directory/lang/english.php
+++ b/plugin/azure_active_directory/lang/english.php
@@ -24,6 +24,7 @@ $strings['management_login_name'] = 'Name for the management login';
$strings['management_login_name_help'] = 'The default is "Management Login".';
$strings['OrganisationEmail'] = 'Organisation e-mail';
$strings['AzureId'] = 'Azure ID (mailNickname)';
+$strings['AzureUid'] = 'Azure UID (internal ID)';
$strings['ManagementLogin'] = 'Management Login';
$strings['InvalidId'] = 'Login failed - incorrect login or password. Errocode: AZMNF';
$strings['provisioning'] = 'Automated provisioning';
diff --git a/plugin/azure_active_directory/lang/french.php b/plugin/azure_active_directory/lang/french.php
index 2fdb548083..96887c70f3 100644
--- a/plugin/azure_active_directory/lang/french.php
+++ b/plugin/azure_active_directory/lang/french.php
@@ -24,6 +24,7 @@ $strings['management_login_name'] = 'Nom du login de gestion';
$strings['management_login_name_help'] = 'Le nom par défaut est "Login de gestion".';
$strings['OrganisationEmail'] = 'E-mail professionnel';
$strings['AzureId'] = 'ID Azure (mailNickname)';
+$strings['AzureUid'] = 'Azure UID (internal ID)';
$strings['ManagementLogin'] = 'Login de gestion';
$strings['InvalidId'] = 'Échec du login - nom d\'utilisateur ou mot de passe incorrect. Errocode: AZMNF';
$strings['provisioning'] = 'Création automatisée';
diff --git a/plugin/azure_active_directory/lang/spanish.php b/plugin/azure_active_directory/lang/spanish.php
index b6aef5cd36..ef1bc16db8 100644
--- a/plugin/azure_active_directory/lang/spanish.php
+++ b/plugin/azure_active_directory/lang/spanish.php
@@ -24,6 +24,7 @@ $strings['management_login_name'] = 'Nombre del bloque de login de gestión';
$strings['management_login_name_help'] = 'El nombre por defecto es "Login de gestión".';
$strings['OrganisationEmail'] = 'E-mail profesional';
$strings['AzureId'] = 'ID Azure (mailNickname)';
+$strings['AzureUid'] = 'UID Azure (ID interno)';
$strings['ManagementLogin'] = 'Login de gestión';
$strings['InvalidId'] = 'Problema en el login - nombre de usuario o contraseña incorrecto. Errocode: AZMNF';
$strings['provisioning'] = 'Creación automatizada';
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 8e0f33f3d3..279e2b694e 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -29,6 +29,7 @@ class AzureActiveDirectory extends Plugin
public const EXTRA_FIELD_ORGANISATION_EMAIL = 'organisationemail';
public const EXTRA_FIELD_AZURE_ID = 'azure_id';
+ public const EXTRA_FIELD_AZURE_UID = 'azure_uid';
/**
* AzureActiveDirectory constructor.
@@ -123,5 +124,11 @@ class AzureActiveDirectory extends Plugin
$this->get_lang('AzureId'),
''
);
+ UserManager::create_extra_field(
+ self::EXTRA_FIELD_AZURE_UID,
+ ExtraField::FIELD_TYPE_TEXT,
+ $this->get_lang('AzureUid'),
+ ''
+ );
}
}
diff --git a/plugin/azure_active_directory/src/callback.php b/plugin/azure_active_directory/src/callback.php
index 08e140676e..036b0d70a9 100644
--- a/plugin/azure_active_directory/src/callback.php
+++ b/plugin/azure_active_directory/src/callback.php
@@ -79,7 +79,10 @@ try {
throw new Exception('The mail field is empty in Azure AD and is needed to set the organisation email for this user.');
}
if (empty($me['mailNickname'])) {
- throw new Exception('The mailNickname field is empty in Azure AD and is needed to set the unique Azure ID for this user.');
+ throw new Exception('The mailNickname field is empty in Azure AD and is needed to set the unique username for this user.');
+ }
+ if (empty($me['objectId'])) {
+ throw new Exception('The id field is empty in Azure AD and is needed to set the unique Azure ID for this user.');
}
$extraFieldValue = new ExtraFieldValue('user');
@@ -91,6 +94,10 @@ try {
AzureActiveDirectory::EXTRA_FIELD_AZURE_ID,
$me['mailNickname']
);
+ $uidValue = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
+ AzureActiveDirectory::EXTRA_FIELD_AZURE_UID,
+ $me['objectId']
+ );
$userId = null;
// Get the user ID (if any) from the EXTRA_FIELD_ORGANISATION_EMAIL extra
@@ -107,6 +114,14 @@ try {
}
}
+ if (empty($userId)) {
+ // If the previous step didn't work, get the user ID from
+ // EXTRA_FIELD_AZURE_UID
+ if (!empty($uidValue) && isset($uidValue['item_id'])) {
+ $userId = $uidValue['item_id'];
+ }
+ }
+
if (empty($userId)) {
// If we didn't find the user
if ($plugin->get(AzureActiveDirectory::SETTING_PROVISION_USERS) === 'true') {
@@ -155,6 +170,7 @@ try {
[
'extra_'.AzureActiveDirectory::EXTRA_FIELD_ORGANISATION_EMAIL => $me['mail'],
'extra_'.AzureActiveDirectory::EXTRA_FIELD_AZURE_ID => $me['mailNickname'],
+ 'extra_'.AzureActiveDirectory::EXTRA_FIELD_AZURE_UID => $me['id'],
],
null,
null,
@@ -164,7 +180,7 @@ try {
throw new Exception(get_lang('UserNotAdded').' '.$me['mailNickname']);
}
} else {
- throw new Exception('User not found when checking the extra fields from '.$me['mail'].' or '.$me['mailNickname'].'.');
+ throw new Exception('User not found when checking the extra fields from '.$me['mail'].' or '.$me['mailNickname'].' or '.$me['id'].'.');
}
}
From 994244bb02ad5ffd861fefc42cd8d53d84bc49d6 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Thu, 29 Aug 2024 09:02:33 -0500
Subject: [PATCH 02/22] Plugin: Azure: Add option to set the verification order
por existing user - refs BT#21930
---
plugin/azure_active_directory/lang/dutch.php | 4 ++
.../azure_active_directory/lang/english.php | 4 ++
plugin/azure_active_directory/lang/french.php | 4 ++
.../azure_active_directory/lang/spanish.php | 4 ++
.../src/AzureActiveDirectory.php | 57 +++++++++++++++++++
.../azure_active_directory/src/callback.php | 37 +-----------
6 files changed, 74 insertions(+), 36 deletions(-)
diff --git a/plugin/azure_active_directory/lang/dutch.php b/plugin/azure_active_directory/lang/dutch.php
index 2e2dbb6d77..7cafd778dc 100644
--- a/plugin/azure_active_directory/lang/dutch.php
+++ b/plugin/azure_active_directory/lang/dutch.php
@@ -22,6 +22,10 @@ $strings['management_login_enable_help'] = 'Schakel de chamilo-login uit en scha
.'U zult moeten kopiëren de /plugin/azure_active_directory/layout/login_form.tpl bestand in het /main/template/overrides/layout/ dossier.';
$strings['management_login_name'] = 'Naam voor de beheeraanmelding';
$strings['management_login_name_help'] = 'De standaardinstelling is "Beheer login".';
+$strings['existing_user_verification_order'] = 'Existing user verification order';
+$strings['existing_user_verification_order_help'] = 'This value indicates the order in which the user will be searched in Chamilo to verify its existence. '
+ .'By default is 1, 2, 3.'
+ .'
- EXTRA_FIELD_ORGANISATION_EMAIL (
mail) - EXTRA_FIELD_AZURE_ID (
mailNickname) - EXTRA_FIELD_AZURE_UID (
id of objectId)
';
$strings['OrganisationEmail'] = 'Organisatie e-mail';
$strings['AzureId'] = 'Azure ID (mailNickname)';
$strings['AzureUid'] = 'Azure UID (internal ID)';
diff --git a/plugin/azure_active_directory/lang/english.php b/plugin/azure_active_directory/lang/english.php
index 61c4bf9b75..2dae53b180 100644
--- a/plugin/azure_active_directory/lang/english.php
+++ b/plugin/azure_active_directory/lang/english.php
@@ -22,6 +22,10 @@ $strings['management_login_enable_help'] = 'Disable the chamilo login and enable
.'You will need to copy the /plugin/azure_active_directory/layout/login_form.tpl file to /main/template/overrides/layout/ directory.';
$strings['management_login_name'] = 'Name for the management login';
$strings['management_login_name_help'] = 'The default is "Management Login".';
+$strings['existing_user_verification_order'] = 'Existing user verification order';
+$strings['existing_user_verification_order_help'] = 'This value indicates the order in which the user will be searched in Chamilo to verify its existence. '
+ .'By default is 1, 2, 3.'
+ .'- EXTRA_FIELD_ORGANISATION_EMAIL (
mail) - EXTRA_FIELD_AZURE_ID (
mailNickname) - EXTRA_FIELD_AZURE_UID (
id or objectId)
';
$strings['OrganisationEmail'] = 'Organisation e-mail';
$strings['AzureId'] = 'Azure ID (mailNickname)';
$strings['AzureUid'] = 'Azure UID (internal ID)';
diff --git a/plugin/azure_active_directory/lang/french.php b/plugin/azure_active_directory/lang/french.php
index 96887c70f3..0446518c43 100644
--- a/plugin/azure_active_directory/lang/french.php
+++ b/plugin/azure_active_directory/lang/french.php
@@ -22,6 +22,10 @@ $strings['management_login_enable_help'] = 'Désactiver le login de Chamilo et p
.'Vous devez, pour cela, copier le fichier /plugin/azure_active_directory/layout/login_form.tpl dans le répertoire /main/template/overrides/layout/.';
$strings['management_login_name'] = 'Nom du login de gestion';
$strings['management_login_name_help'] = 'Le nom par défaut est "Login de gestion".';
+$strings['existing_user_verification_order'] = 'Existing user verification order';
+$strings['existing_user_verification_order_help'] = 'This value indicates the order in which the user will be searched in Chamilo to verify its existence. '
+ .'By default is 1, 2, 3.'
+ .'- EXTRA_FIELD_ORGANISATION_EMAIL (
mail) - EXTRA_FIELD_AZURE_ID (
mailNickname) - EXTRA_FIELD_AZURE_UID (
id ou objectId)
';
$strings['OrganisationEmail'] = 'E-mail professionnel';
$strings['AzureId'] = 'ID Azure (mailNickname)';
$strings['AzureUid'] = 'Azure UID (internal ID)';
diff --git a/plugin/azure_active_directory/lang/spanish.php b/plugin/azure_active_directory/lang/spanish.php
index ef1bc16db8..e82a1775a4 100644
--- a/plugin/azure_active_directory/lang/spanish.php
+++ b/plugin/azure_active_directory/lang/spanish.php
@@ -22,6 +22,10 @@ $strings['management_login_enable_help'] = 'Desactivar el login de Chamilo y act
.'Para ello, tendrá que copiar el archivo /plugin/azure_active_directory/layout/login_form.tpl en la carpeta /main/template/overrides/layout/.';
$strings['management_login_name'] = 'Nombre del bloque de login de gestión';
$strings['management_login_name_help'] = 'El nombre por defecto es "Login de gestión".';
+$strings['existing_user_verification_order'] = 'Orden de verificación de usuario existente';
+$strings['existing_user_verification_order_help'] = 'Este valor indica el orden en que el usuario serña buscado en Chamilo para verificar su existencia. '
+ .'Por defecto es 1, 2, 3.'
+ .'- EXTRA_FIELD_ORGANISATION_EMAIL (
mail) - EXTRA_FIELD_AZURE_ID (
mailNickname) - EXTRA_FIELD_AZURE_UID (
id o objectId)
';
$strings['OrganisationEmail'] = 'E-mail profesional';
$strings['AzureId'] = 'ID Azure (mailNickname)';
$strings['AzureUid'] = 'UID Azure (ID interno)';
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 279e2b694e..75fd178206 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -23,6 +23,7 @@ class AzureActiveDirectory extends Plugin
public const SETTING_GROUP_ID_ADMIN = 'group_id_admin';
public const SETTING_GROUP_ID_SESSION_ADMIN = 'group_id_session_admin';
public const SETTING_GROUP_ID_TEACHER = 'group_id_teacher';
+ public const SETTING_EXISTING_USER_VERIFICATION_ORDER = 'existing_user_verification_order';
public const URL_TYPE_AUTHORIZE = 'login';
public const URL_TYPE_LOGOUT = 'logout';
@@ -48,6 +49,7 @@ class AzureActiveDirectory extends Plugin
self::SETTING_GROUP_ID_ADMIN => 'text',
self::SETTING_GROUP_ID_SESSION_ADMIN => 'text',
self::SETTING_GROUP_ID_TEACHER => 'text',
+ self::SETTING_EXISTING_USER_VERIFICATION_ORDER => 'text',
];
parent::__construct('2.3', 'Angel Fernando Quiroz Campos, Yannick Warnier', $settings);
@@ -131,4 +133,59 @@ class AzureActiveDirectory extends Plugin
''
);
}
+
+ public function getExistingUserVerificationOrder(): array
+ {
+ $defaultOrder = [1, 2, 3];
+
+ $settingValue = $this->get(self::SETTING_EXISTING_USER_VERIFICATION_ORDER);
+ $selectedOrder = array_filter(
+ array_map(
+ 'trim',
+ explode(',', $settingValue)
+ )
+ );
+ $selectedOrder = array_map('intval', $selectedOrder);
+ $selectedOrder = array_filter(
+ $selectedOrder,
+ function ($position) use ($defaultOrder): bool {
+ return in_array($position, $defaultOrder);
+ }
+ );
+
+ if ($selectedOrder) {
+ return $selectedOrder;
+ }
+
+ return $defaultOrder;
+ }
+
+ public function getUserIdByVerificationOrder(array $azureUserData): ?int
+ {
+ $selectedOrder = $this->getExistingUserVerificationOrder();
+
+ $extraFieldValue = new ExtraFieldValue('user');
+ $positionsAndFields = [
+ 1 => $extraFieldValue->get_item_id_from_field_variable_and_field_value(
+ AzureActiveDirectory::EXTRA_FIELD_ORGANISATION_EMAIL,
+ $azureUserData['mail']
+ ),
+ 2 => $extraFieldValue->get_item_id_from_field_variable_and_field_value(
+ AzureActiveDirectory::EXTRA_FIELD_AZURE_ID,
+ $azureUserData['mailNickname']
+ ),
+ 3 => $extraFieldValue->get_item_id_from_field_variable_and_field_value(
+ AzureActiveDirectory::EXTRA_FIELD_AZURE_UID,
+ $azureUserData['objectId']
+ ),
+ ];
+
+ foreach ($selectedOrder as $position) {
+ if (!empty($positionsAndFields[$position]) && isset($positionsAndFields[$position]['item_id'])) {
+ return (int) $positionsAndFields[$position]['item_id'];
+ }
+ }
+
+ return null;
+ }
}
diff --git a/plugin/azure_active_directory/src/callback.php b/plugin/azure_active_directory/src/callback.php
index 036b0d70a9..cb76aca6b1 100644
--- a/plugin/azure_active_directory/src/callback.php
+++ b/plugin/azure_active_directory/src/callback.php
@@ -85,42 +85,7 @@ try {
throw new Exception('The id field is empty in Azure AD and is needed to set the unique Azure ID for this user.');
}
- $extraFieldValue = new ExtraFieldValue('user');
- $organisationValue = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
- AzureActiveDirectory::EXTRA_FIELD_ORGANISATION_EMAIL,
- $me['mail']
- );
- $azureValue = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
- AzureActiveDirectory::EXTRA_FIELD_AZURE_ID,
- $me['mailNickname']
- );
- $uidValue = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
- AzureActiveDirectory::EXTRA_FIELD_AZURE_UID,
- $me['objectId']
- );
-
- $userId = null;
- // Get the user ID (if any) from the EXTRA_FIELD_ORGANISATION_EMAIL extra
- // field
- if (!empty($organisationValue) && isset($organisationValue['item_id'])) {
- $userId = $organisationValue['item_id'];
- }
-
- if (empty($userId)) {
- // If the previous step didn't work, get the user ID from
- // EXTRA_FIELD_AZURE_ID
- if (!empty($azureValue) && isset($azureValue['item_id'])) {
- $userId = $azureValue['item_id'];
- }
- }
-
- if (empty($userId)) {
- // If the previous step didn't work, get the user ID from
- // EXTRA_FIELD_AZURE_UID
- if (!empty($uidValue) && isset($uidValue['item_id'])) {
- $userId = $uidValue['item_id'];
- }
- }
+ $userId = $plugin->getUserIdByVerificationOrder($me);
if (empty($userId)) {
// If we didn't find the user
From 9cecd7bf71ec4bfc6a253e3f79dfac2efe8aab04 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Tue, 3 Sep 2024 01:37:35 -0500
Subject: [PATCH 03/22] Plugin: Azure: Move code to function - refs BT#21930
---
.../src/AzureActiveDirectory.php | 110 +++++++++++++++++-
.../azure_active_directory/src/callback.php | 68 +----------
2 files changed, 112 insertions(+), 66 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 75fd178206..788ba8b3bb 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -1,6 +1,7 @@
getExistingUserVerificationOrder();
$extraFieldValue = new ExtraFieldValue('user');
@@ -176,7 +176,7 @@ class AzureActiveDirectory extends Plugin
),
3 => $extraFieldValue->get_item_id_from_field_variable_and_field_value(
AzureActiveDirectory::EXTRA_FIELD_AZURE_UID,
- $azureUserData['objectId']
+ $azureUserData[$azureUidKey]
),
];
@@ -188,4 +188,108 @@ class AzureActiveDirectory extends Plugin
return null;
}
+
+ /**
+ * @throws Exception
+ */
+ public function registerUser(
+ AccessTokenInterface $token,
+ Azure $provider,
+ array $azureUserInfo,
+ string $apiGroupsRef = 'me/memberOf',
+ string $objectIdKey = 'objectId',
+ string $azureUidKey = 'objectId'
+ ) {
+ if (empty($azureUserInfo)) {
+ throw new Exception('Groups info not found.');
+ }
+
+ $userId = $this->getUserIdByVerificationOrder($azureUserInfo, $azureUidKey);
+
+ if (empty($userId)) {
+ // If we didn't find the user
+ if ($this->get(self::SETTING_PROVISION_USERS) === 'true') {
+ [$userRole, $isAdmin] = $this->getUserRoleAndCheckIsAdmin(
+ $token,
+ $provider,
+ $apiGroupsRef,
+ $objectIdKey
+ );
+
+ $phone = null;
+
+ if (isset($azureUserInfo['telephoneNumber'])) {
+ $phone = $azureUserInfo['telephoneNumber'];
+ } elseif (isset($azureUserInfo['businessPhones'][0])) {
+ $phone = $azureUserInfo['businessPhones'][0];
+ } elseif (isset($azureUserInfo['mobilePhone'])) {
+ $phone = $azureUserInfo['mobilePhone'];
+ }
+
+ // If the option is set to create users, create it
+ $userId = UserManager::create_user(
+ $azureUserInfo['givenName'],
+ $azureUserInfo['surname'],
+ $userRole,
+ $azureUserInfo['mail'],
+ $azureUserInfo['userPrincipalName'],
+ '',
+ null,
+ null,
+ $phone,
+ null,
+ 'azure',
+ null,
+ ($azureUserInfo['accountEnabled'] ? 1 : 0),
+ null,
+ [
+ 'extra_'.self::EXTRA_FIELD_ORGANISATION_EMAIL => $azureUserInfo['mail'],
+ 'extra_'.self::EXTRA_FIELD_AZURE_ID => $azureUserInfo['mailNickname'],
+ 'extra_'.self::EXTRA_FIELD_AZURE_UID => $azureUserInfo[$azureUidKey],
+ ],
+ null,
+ null,
+ $isAdmin
+ );
+ if (!$userId) {
+ throw new Exception(get_lang('UserNotAdded').' '.$azureUserInfo['userPrincipalName']);
+ }
+ } else {
+ throw new Exception('User not found when checking the extra fields from '.$azureUserInfo['mail'].' or '.$azureUserInfo['mailNickname'].' or '.$azureUserInfo[$azureUidKey].'.');
+ }
+ }
+
+ return $userId;
+ }
+
+ private function getUserRoleAndCheckIsAdmin(
+ AccessTokenInterface $token,
+ Azure $provider = null,
+ string $apiRef = 'me/memberOf',
+ string $objectIdKey = 'objectId'
+ ): array {
+ $provider = $provider ?: $this->getProvider();
+
+ $groups = $provider->get($apiRef, $token);
+
+ // If any specific group ID has been defined for a specific role, use that
+ // ID to give the user the right role
+ $givenAdminGroup = $this->get(self::SETTING_GROUP_ID_ADMIN);
+ $givenSessionAdminGroup = $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN);
+ $givenTeacherGroup = $this->get(self::SETTING_GROUP_ID_TEACHER);
+ $userRole = STUDENT;
+ $isAdmin = false;
+ foreach ($groups as $group) {
+ if ($givenAdminGroup == $group[$objectIdKey]) {
+ $userRole = COURSEMANAGER;
+ $isAdmin = true;
+ } elseif ($givenSessionAdminGroup == $group[$objectIdKey]) {
+ $userRole = SESSIONADMIN;
+ } elseif ($userRole != SESSIONADMIN && $givenTeacherGroup == $group[$objectIdKey]) {
+ $userRole = COURSEMANAGER;
+ }
+ }
+
+ return [$userRole, $isAdmin];
+ }
}
diff --git a/plugin/azure_active_directory/src/callback.php b/plugin/azure_active_directory/src/callback.php
index cb76aca6b1..09d0b363d6 100644
--- a/plugin/azure_active_directory/src/callback.php
+++ b/plugin/azure_active_directory/src/callback.php
@@ -85,69 +85,11 @@ try {
throw new Exception('The id field is empty in Azure AD and is needed to set the unique Azure ID for this user.');
}
- $userId = $plugin->getUserIdByVerificationOrder($me);
-
- if (empty($userId)) {
- // If we didn't find the user
- if ($plugin->get(AzureActiveDirectory::SETTING_PROVISION_USERS) === 'true') {
- // Get groups info, if any
- $groups = $provider->get('me/memberOf', $token);
- if (empty($me)) {
- throw new Exception('Groups info not found.');
- }
- // If any specific group ID has been defined for a specific role, use that
- // ID to give the user the right role
- $givenAdminGroup = $plugin->get(AzureActiveDirectory::SETTING_GROUP_ID_ADMIN);
- $givenSessionAdminGroup = $plugin->get(AzureActiveDirectory::SETTING_GROUP_ID_SESSION_ADMIN);
- $givenTeacherGroup = $plugin->get(AzureActiveDirectory::SETTING_GROUP_ID_TEACHER);
- $userRole = STUDENT;
- $isAdmin = false;
- foreach ($groups as $group) {
- if ($isAdmin) {
- break;
- }
- if ($givenAdminGroup == $group['objectId']) {
- $userRole = COURSEMANAGER;
- $isAdmin = true;
- } elseif (!$isAdmin && $givenSessionAdminGroup == $group['objectId']) {
- $userRole = SESSIONADMIN;
- } elseif (!$isAdmin && $userRole != SESSIONADMIN && $givenTeacherGroup == $group['objectId']) {
- $userRole = COURSEMANAGER;
- }
- }
-
- // If the option is set to create users, create it
- $userId = UserManager::create_user(
- $me['givenName'],
- $me['surname'],
- $userRole,
- $me['mail'],
- $me['mailNickname'],
- '',
- null,
- null,
- $me['telephoneNumber'],
- null,
- 'azure',
- null,
- ($me['accountEnabled'] ? 1 : 0),
- null,
- [
- 'extra_'.AzureActiveDirectory::EXTRA_FIELD_ORGANISATION_EMAIL => $me['mail'],
- 'extra_'.AzureActiveDirectory::EXTRA_FIELD_AZURE_ID => $me['mailNickname'],
- 'extra_'.AzureActiveDirectory::EXTRA_FIELD_AZURE_UID => $me['id'],
- ],
- null,
- null,
- $isAdmin
- );
- if (!$userId) {
- throw new Exception(get_lang('UserNotAdded').' '.$me['mailNickname']);
- }
- } else {
- throw new Exception('User not found when checking the extra fields from '.$me['mail'].' or '.$me['mailNickname'].' or '.$me['id'].'.');
- }
- }
+ $userId = $plugin->registerUser(
+ $token,
+ $provider,
+ $me
+ );
$userInfo = api_get_user_info($userId);
From dc27ce5f43499f3546cb57dd9b2a8e100146903c Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Tue, 3 Sep 2024 03:08:16 -0500
Subject: [PATCH 04/22] Plugin: Azure: Add option to update existing users -
refs BT#21930
---
plugin/azure_active_directory/lang/dutch.php | 2 +
.../azure_active_directory/lang/english.php | 2 +
plugin/azure_active_directory/lang/french.php | 2 +
.../azure_active_directory/lang/spanish.php | 2 +
.../src/AzureActiveDirectory.php | 138 ++++++++++++++----
5 files changed, 119 insertions(+), 27 deletions(-)
diff --git a/plugin/azure_active_directory/lang/dutch.php b/plugin/azure_active_directory/lang/dutch.php
index 7cafd778dc..151c039fd7 100644
--- a/plugin/azure_active_directory/lang/dutch.php
+++ b/plugin/azure_active_directory/lang/dutch.php
@@ -32,6 +32,8 @@ $strings['AzureUid'] = 'Azure UID (internal ID)';
$strings['ManagementLogin'] = 'Beheer Login';
$strings['InvalidId'] = 'Deze identificatie is niet geldig (verkeerde log-in of wachtwoord). Errocode: AZMNF';
$strings['provisioning'] = 'Geautomatiseerde inrichting';
+$strings['update_users'] = 'Update users';
+$strings['update_users_help'] = 'Allow user data to be updated at the start of the session.';
$strings['provisioning_help'] = 'Maak automatisch nieuwe gebruikers (als studenten) vanuit Azure wanneer ze niet in Chamilo zijn.';
$strings['group_id_admin'] = 'Groeps-ID voor platformbeheerders';
$strings['group_id_admin_help'] = 'De groeps-ID is te vinden in de details van de gebruikersgroep en ziet er ongeveer zo uit: ae134eef-cbd4-4a32-ba99-49898a1314b6. Indien leeg, wordt er automatisch geen gebruiker aangemaakt als admin.';
diff --git a/plugin/azure_active_directory/lang/english.php b/plugin/azure_active_directory/lang/english.php
index 2dae53b180..59f76d654f 100644
--- a/plugin/azure_active_directory/lang/english.php
+++ b/plugin/azure_active_directory/lang/english.php
@@ -33,6 +33,8 @@ $strings['ManagementLogin'] = 'Management Login';
$strings['InvalidId'] = 'Login failed - incorrect login or password. Errocode: AZMNF';
$strings['provisioning'] = 'Automated provisioning';
$strings['provisioning_help'] = 'Automatically create new users (as students) from Azure when they are not in Chamilo.';
+$strings['update_users'] = 'Update users';
+$strings['update_users_help'] = 'Allow user data to be updated at the start of the session.';
$strings['group_id_admin'] = 'Group ID for platform admins';
$strings['group_id_admin_help'] = 'The group ID can be found in the user group details, looking similar to this: ae134eef-cbd4-4a32-ba99-49898a1314b6. If empty, no user will be automatically created as admin.';
$strings['group_id_session_admin'] = 'Group ID for session admins';
diff --git a/plugin/azure_active_directory/lang/french.php b/plugin/azure_active_directory/lang/french.php
index 0446518c43..691c34a717 100644
--- a/plugin/azure_active_directory/lang/french.php
+++ b/plugin/azure_active_directory/lang/french.php
@@ -33,6 +33,8 @@ $strings['ManagementLogin'] = 'Login de gestion';
$strings['InvalidId'] = 'Échec du login - nom d\'utilisateur ou mot de passe incorrect. Errocode: AZMNF';
$strings['provisioning'] = 'Création automatisée';
$strings['provisioning_help'] = 'Créer les utilisateurs automatiquement (en tant qu\'apprenants) depuis Azure s\'ils n\'existent pas encore dans Chamilo.';
+$strings['update_users'] = 'Actualiser les utilisateurs';
+$strings['update_users_help'] = 'Permettre d\'actualiser les données de l\'utilisateur lors du démarrage de la session.';
$strings['group_id_admin'] = 'ID du groupe administrateur';
$strings['group_id_admin_help'] = 'L\'id du groupe peut être trouvé dans les détails du groupe, et ressemble à ceci : ae134eef-cbd4-4a32-ba99-49898a1314b6. Si ce champ est laissé vide, aucun utilisateur ne sera créé en tant qu\'administrateur.';
$strings['group_id_session_admin'] = 'ID du groupe administrateur de sessions';
diff --git a/plugin/azure_active_directory/lang/spanish.php b/plugin/azure_active_directory/lang/spanish.php
index e82a1775a4..03978fcf3e 100644
--- a/plugin/azure_active_directory/lang/spanish.php
+++ b/plugin/azure_active_directory/lang/spanish.php
@@ -33,6 +33,8 @@ $strings['ManagementLogin'] = 'Login de gestión';
$strings['InvalidId'] = 'Problema en el login - nombre de usuario o contraseña incorrecto. Errocode: AZMNF';
$strings['provisioning'] = 'Creación automatizada';
$strings['provisioning_help'] = 'Crear usuarios automáticamente (como alumnos) desde Azure si no existen en Chamilo todavía.';
+$strings['update_users'] = 'Actualizar los usuarios';
+$strings['update_users_help'] = 'Permite actualizar los datos del usuario al iniciar sesión.';
$strings['group_id_admin'] = 'ID de grupo administrador';
$strings['group_id_admin_help'] = 'El ID de grupo se encuentra en los detalles del grupo en Azure, y parece a: ae134eef-cbd4-4a32-ba99-49898a1314b6. Si deja este campo vacío, ningún usuario será creado como administrador.';
$strings['group_id_session_admin'] = 'ID de grupo admin de sesiones';
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 788ba8b3bb..eef9559ffb 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -21,6 +21,7 @@ class AzureActiveDirectory extends Plugin
public const SETTING_MANAGEMENT_LOGIN_ENABLE = 'management_login_enable';
public const SETTING_MANAGEMENT_LOGIN_NAME = 'management_login_name';
public const SETTING_PROVISION_USERS = 'provisioning';
+ public const SETTING_UPDATE_USERS = 'update_users';
public const SETTING_GROUP_ID_ADMIN = 'group_id_admin';
public const SETTING_GROUP_ID_SESSION_ADMIN = 'group_id_session_admin';
public const SETTING_GROUP_ID_TEACHER = 'group_id_teacher';
@@ -47,6 +48,7 @@ class AzureActiveDirectory extends Plugin
self::SETTING_MANAGEMENT_LOGIN_ENABLE => 'boolean',
self::SETTING_MANAGEMENT_LOGIN_NAME => 'text',
self::SETTING_PROVISION_USERS => 'boolean',
+ self::SETTING_UPDATE_USERS => 'boolean',
self::SETTING_GROUP_ID_ADMIN => 'text',
self::SETTING_GROUP_ID_SESSION_ADMIN => 'text',
self::SETTING_GROUP_ID_TEACHER => 'text',
@@ -209,44 +211,36 @@ class AzureActiveDirectory extends Plugin
if (empty($userId)) {
// If we didn't find the user
if ($this->get(self::SETTING_PROVISION_USERS) === 'true') {
- [$userRole, $isAdmin] = $this->getUserRoleAndCheckIsAdmin(
- $token,
- $provider,
- $apiGroupsRef,
- $objectIdKey
- );
-
- $phone = null;
-
- if (isset($azureUserInfo['telephoneNumber'])) {
- $phone = $azureUserInfo['telephoneNumber'];
- } elseif (isset($azureUserInfo['businessPhones'][0])) {
- $phone = $azureUserInfo['businessPhones'][0];
- } elseif (isset($azureUserInfo['mobilePhone'])) {
- $phone = $azureUserInfo['mobilePhone'];
- }
+ [
+ $firstNme,
+ $lastName,
+ $username,
+ $email,
+ $phone,
+ $authSource,
+ $active,
+ $extra,
+ $userRole,
+ $isAdmin,
+ ] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
// If the option is set to create users, create it
$userId = UserManager::create_user(
- $azureUserInfo['givenName'],
- $azureUserInfo['surname'],
+ $firstNme,
+ $lastName,
$userRole,
- $azureUserInfo['mail'],
- $azureUserInfo['userPrincipalName'],
+ $email,
+ $username,
'',
null,
null,
$phone,
null,
- 'azure',
+ $authSource,
null,
- ($azureUserInfo['accountEnabled'] ? 1 : 0),
+ $active,
null,
- [
- 'extra_'.self::EXTRA_FIELD_ORGANISATION_EMAIL => $azureUserInfo['mail'],
- 'extra_'.self::EXTRA_FIELD_AZURE_ID => $azureUserInfo['mailNickname'],
- 'extra_'.self::EXTRA_FIELD_AZURE_UID => $azureUserInfo[$azureUidKey],
- ],
+ $extra,
null,
null,
$isAdmin
@@ -257,11 +251,101 @@ class AzureActiveDirectory extends Plugin
} else {
throw new Exception('User not found when checking the extra fields from '.$azureUserInfo['mail'].' or '.$azureUserInfo['mailNickname'].' or '.$azureUserInfo[$azureUidKey].'.');
}
+ } else {
+ if ($this->get(self::SETTING_UPDATE_USERS) === 'true') {
+ [
+ $firstNme,
+ $lastName,
+ $username,
+ $email,
+ $phone,
+ $authSource,
+ $active,
+ $extra,
+ $userRole,
+ $isAdmin,
+ ] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
+
+ $userId = UserManager::update_user(
+ $userId,
+ $firstNme,
+ $lastName,
+ $username,
+ '',
+ $authSource,
+ $email,
+ $userRole,
+ null,
+ $phone,
+ null,
+ null,
+ $active,
+ null,
+ 0,
+ $extra
+ );
+
+ if (!$userId) {
+ throw new Exception(get_lang('CouldNotUpdateUser').' '.$azureUserInfo['userPrincipalName']);
+ }
+ }
}
return $userId;
}
+ private function formatUserData(
+ AccessTokenInterface $token,
+ Azure $provider,
+ array $azureUserInfo,
+ string $apiGroupsRef,
+ string $objectIdKey,
+ string $azureUidKey
+ ): array {
+ [$userRole, $isAdmin] = $this->getUserRoleAndCheckIsAdmin(
+ $token,
+ $provider,
+ $apiGroupsRef,
+ $objectIdKey
+ );
+
+ $phone = null;
+
+ if (isset($azureUserInfo['telephoneNumber'])) {
+ $phone = $azureUserInfo['telephoneNumber'];
+ } elseif (isset($azureUserInfo['businessPhones'][0])) {
+ $phone = $azureUserInfo['businessPhones'][0];
+ } elseif (isset($azureUserInfo['mobilePhone'])) {
+ $phone = $azureUserInfo['mobilePhone'];
+ }
+
+ // If the option is set to create users, create it
+ $firstNme = $azureUserInfo['givenName'];
+ $lastName = $azureUserInfo['surname'];
+ $email = $azureUserInfo['mail'];
+ $username = $azureUserInfo['userPrincipalName'];
+ $authSource = 'azure';
+ $active = ($azureUserInfo['accountEnabled'] ? 1 : 0);
+ $extra = [
+ 'extra_'.self::EXTRA_FIELD_ORGANISATION_EMAIL => $azureUserInfo['mail'],
+ 'extra_'.self::EXTRA_FIELD_AZURE_ID => $azureUserInfo['mailNickname'],
+ 'extra_'.self::EXTRA_FIELD_AZURE_UID => $azureUserInfo[$azureUidKey],
+ ];
+
+ return [
+ $firstNme,
+ $lastName,
+ $username,
+ $email,
+ $phone,
+ $authSource,
+ $active,
+ $extra,
+ $userRole,
+ $isAdmin,
+ ];
+ }
+
private function getUserRoleAndCheckIsAdmin(
AccessTokenInterface $token,
Azure $provider = null,
From 6949a076847ad71b9eb762dddc89468a96ab9664 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Tue, 3 Sep 2024 10:39:11 -0500
Subject: [PATCH 05/22] Plugin: Azure: Add script to sync users from Azure -
refs BT#21930
---
plugin/azure_active_directory/lang/dutch.php | 2 +
.../azure_active_directory/lang/english.php | 2 +
plugin/azure_active_directory/lang/french.php | 2 +
.../azure_active_directory/lang/spanish.php | 2 +
.../src/AzureActiveDirectory.php | 2 +
.../src/scripts/sync_users.php | 69 +++++++++++++++++++
6 files changed, 79 insertions(+)
create mode 100644 plugin/azure_active_directory/src/scripts/sync_users.php
diff --git a/plugin/azure_active_directory/lang/dutch.php b/plugin/azure_active_directory/lang/dutch.php
index 151c039fd7..3fa2927b09 100644
--- a/plugin/azure_active_directory/lang/dutch.php
+++ b/plugin/azure_active_directory/lang/dutch.php
@@ -42,3 +42,5 @@ $strings['group_id_session_admin_help'] = 'De groeps-ID voor sessiebeheerders. I
$strings['group_id_teacher'] = 'Groeps-ID voor docenten';
$strings['group_id_teacher_help'] = 'De groeps-ID voor docenten. Indien leeg, wordt er automatisch geen gebruiker aangemaakt als docent.';
$strings['additional_interaction_required'] = 'Er is aanvullende interactie vereist om u te authenticeren. Log rechtstreeks in via uw authenticatiesysteem en kom dan terug naar deze pagina om in te loggen.';
+$strings['tenant_id'] = 'Mandanten-ID';
+$strings['tenant_id_help'] = 'Required to run scripts.';
diff --git a/plugin/azure_active_directory/lang/english.php b/plugin/azure_active_directory/lang/english.php
index 59f76d654f..0295f093c4 100644
--- a/plugin/azure_active_directory/lang/english.php
+++ b/plugin/azure_active_directory/lang/english.php
@@ -42,3 +42,5 @@ $strings['group_id_session_admin_help'] = 'The group ID for session admins. If e
$strings['group_id_teacher'] = 'Group ID for teachers';
$strings['group_id_teacher_help'] = 'The group ID for teachers. If empty, no user will be automatically created as teacher.';
$strings['additional_interaction_required'] = 'Some additional interaction is required to authenticate you. Please login directly through your authentication system, then come back to this page to login.';
+$strings['tenant_id'] = 'Tenant ID';
+$strings['tenant_id_help'] = 'Required to run scripts.';
diff --git a/plugin/azure_active_directory/lang/french.php b/plugin/azure_active_directory/lang/french.php
index 691c34a717..77f195a80b 100644
--- a/plugin/azure_active_directory/lang/french.php
+++ b/plugin/azure_active_directory/lang/french.php
@@ -42,3 +42,5 @@ $strings['group_id_session_admin_help'] = 'The group ID for session admins. Si c
$strings['group_id_teacher'] = 'ID du groupe enseignant';
$strings['group_id_teacher_help'] = 'The group ID for teachers. Si ce champ est laissé vide, aucun utilisateur ne sera créé en tant qu\'enseignant.';
$strings['additional_interaction_required'] = 'Une interaction supplémentaire est nécessaire pour vous authentifier. Veuillez vous connecter directement auprès de votre système d\'authentification, puis revenir ici pour vous connecter.';
+$strings['tenant_id'] = 'ID du client';
+$strings['tenant_id_help'] = 'Nécessaire pour exécuter des scripts.';
diff --git a/plugin/azure_active_directory/lang/spanish.php b/plugin/azure_active_directory/lang/spanish.php
index 03978fcf3e..b657c5b775 100644
--- a/plugin/azure_active_directory/lang/spanish.php
+++ b/plugin/azure_active_directory/lang/spanish.php
@@ -42,3 +42,5 @@ $strings['group_id_session_admin_help'] = 'El ID de grupo para administradores d
$strings['group_id_teacher'] = 'ID de grupo profesor';
$strings['group_id_teacher_help'] = 'El ID de grupo para profesores. Si deja este campo vacío, ningún usuario será creado como profesor.';
$strings['additional_interaction_required'] = 'Alguna interacción adicional es necesaria para identificarlo/a. Por favor conéctese primero a través de su sistema de autenticación, luego regrese aquí para logearse.';
+$strings['tenant_id'] = 'Id. del inquilino';
+$strings['tenant_id_help'] = 'Necesario para ejecutar scripts.';
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index eef9559ffb..5daaad6cad 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -26,6 +26,7 @@ class AzureActiveDirectory extends Plugin
public const SETTING_GROUP_ID_SESSION_ADMIN = 'group_id_session_admin';
public const SETTING_GROUP_ID_TEACHER = 'group_id_teacher';
public const SETTING_EXISTING_USER_VERIFICATION_ORDER = 'existing_user_verification_order';
+ public const SETTING_TENANT_ID = 'tenant_id';
public const URL_TYPE_AUTHORIZE = 'login';
public const URL_TYPE_LOGOUT = 'logout';
@@ -53,6 +54,7 @@ class AzureActiveDirectory extends Plugin
self::SETTING_GROUP_ID_SESSION_ADMIN => 'text',
self::SETTING_GROUP_ID_TEACHER => 'text',
self::SETTING_EXISTING_USER_VERIFICATION_ORDER => 'text',
+ self::SETTING_TENANT_ID => 'text',
];
parent::__construct('2.3', 'Angel Fernando Quiroz Campos, Yannick Warnier', $settings);
diff --git a/plugin/azure_active_directory/src/scripts/sync_users.php b/plugin/azure_active_directory/src/scripts/sync_users.php
new file mode 100644
index 0000000000..58a65f475a
--- /dev/null
+++ b/plugin/azure_active_directory/src/scripts/sync_users.php
@@ -0,0 +1,69 @@
+getProvider();
+$provider->urlAPI = "https://graph.microsoft.com/v1.0/";
+$provider->resource = "https://graph.microsoft.com/";
+$provider->tenant = $plugin->get(AzureActiveDirectory::SETTING_TENANT_ID);
+$provider->authWithResource = false;
+
+echo 'Synchronizing users from Azure.'.PHP_EOL;
+
+try {
+ $token = $provider->getAccessToken(
+ 'client_credentials',
+ ['resource' => $provider->resource]
+ );
+
+ $userFields = [
+ 'givenName',
+ 'surname',
+ 'mail',
+ 'userPrincipalName',
+ 'businessPhones',
+ 'mobilePhone',
+ 'accountEnabled',
+ 'mailNickname',
+ 'id'
+ ];
+
+ $azureUsersInfo = $provider->get(
+ 'users?$select='.implode(',', $userFields),
+ $token
+ );
+} catch (Exception $e) {
+ printf("%s - %s".PHP_EOL, time(), $e->getMessage());
+ die;
+}
+
+printf("%s - Number of users obtained %d".PHP_EOL, time(), count($azureUsersInfo));
+
+/** @var array $user */
+foreach ($azureUsersInfo as $azureUserInfo) {
+ try {
+ $userId = $plugin->registerUser(
+ $token,
+ $provider,
+ $azureUserInfo,
+ 'users/' . $azureUserInfo['id'] . '/memberOf',
+ 'id',
+ 'id'
+ );
+
+ $userInfo = api_get_user_info($userId);
+
+ printf("%s - UserInfo %s".PHP_EOL, time(), serialize($userInfo));
+ } catch (Exception $e) {
+ printf("%s - %s".PHP_EOL, time(), $e->getMessage());
+
+ continue;
+ }
+}
From 331d9fac410755439da20706d0627d14ff977369 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Tue, 3 Sep 2024 12:07:26 -0500
Subject: [PATCH 06/22] Plugin: Azure: Add option to deactivate non-existing
users in Azure when running sync_users script - refs BT#21930
---
plugin/azure_active_directory/lang/dutch.php | 2 ++
.../azure_active_directory/lang/english.php | 2 ++
plugin/azure_active_directory/lang/french.php | 2 ++
.../azure_active_directory/lang/spanish.php | 2 ++
.../src/AzureActiveDirectory.php | 2 ++
.../src/scripts/sync_users.php | 26 +++++++++++++++++++
.../UserBundle/Repository/UserRepository.php | 5 ++++
7 files changed, 41 insertions(+)
diff --git a/plugin/azure_active_directory/lang/dutch.php b/plugin/azure_active_directory/lang/dutch.php
index 3fa2927b09..32885f3f68 100644
--- a/plugin/azure_active_directory/lang/dutch.php
+++ b/plugin/azure_active_directory/lang/dutch.php
@@ -44,3 +44,5 @@ $strings['group_id_teacher_help'] = 'De groeps-ID voor docenten. Indien leeg, wo
$strings['additional_interaction_required'] = 'Er is aanvullende interactie vereist om u te authenticeren. Log rechtstreeks in via uw authenticatiesysteem en kom dan terug naar deze pagina om in te loggen.';
$strings['tenant_id'] = 'Mandanten-ID';
$strings['tenant_id_help'] = 'Required to run scripts.';
+$strings['deactivate_nonexisting_users'] = 'Deactivate non-existing users';
+$strings['deactivate_nonexisting_users_help'] = 'Compare registered users in Chamilo with those in Azure and deactivate accounts in Chamilo that do not exist in Azure.';
diff --git a/plugin/azure_active_directory/lang/english.php b/plugin/azure_active_directory/lang/english.php
index 0295f093c4..0d000a7d79 100644
--- a/plugin/azure_active_directory/lang/english.php
+++ b/plugin/azure_active_directory/lang/english.php
@@ -44,3 +44,5 @@ $strings['group_id_teacher_help'] = 'The group ID for teachers. If empty, no use
$strings['additional_interaction_required'] = 'Some additional interaction is required to authenticate you. Please login directly through your authentication system, then come back to this page to login.';
$strings['tenant_id'] = 'Tenant ID';
$strings['tenant_id_help'] = 'Required to run scripts.';
+$strings['deactivate_nonexisting_users'] = 'Deactivate non-existing users';
+$strings['deactivate_nonexisting_users_help'] = 'Compare registered users in Chamilo with those in Azure and deactivate accounts in Chamilo that do not exist in Azure.';
diff --git a/plugin/azure_active_directory/lang/french.php b/plugin/azure_active_directory/lang/french.php
index 77f195a80b..e699c6d91d 100644
--- a/plugin/azure_active_directory/lang/french.php
+++ b/plugin/azure_active_directory/lang/french.php
@@ -44,3 +44,5 @@ $strings['group_id_teacher_help'] = 'The group ID for teachers. Si ce champ est
$strings['additional_interaction_required'] = 'Une interaction supplémentaire est nécessaire pour vous authentifier. Veuillez vous connecter directement auprès de votre système d\'authentification, puis revenir ici pour vous connecter.';
$strings['tenant_id'] = 'ID du client';
$strings['tenant_id_help'] = 'Nécessaire pour exécuter des scripts.';
+$strings['deactivate_nonexisting_users'] = 'Deactivate non-existing users';
+$strings['deactivate_nonexisting_users_help'] = 'Compare registered users in Chamilo with those in Azure and deactivate accounts in Chamilo that do not exist in Azure.';
diff --git a/plugin/azure_active_directory/lang/spanish.php b/plugin/azure_active_directory/lang/spanish.php
index b657c5b775..389a4912e4 100644
--- a/plugin/azure_active_directory/lang/spanish.php
+++ b/plugin/azure_active_directory/lang/spanish.php
@@ -44,3 +44,5 @@ $strings['group_id_teacher_help'] = 'El ID de grupo para profesores. Si deja est
$strings['additional_interaction_required'] = 'Alguna interacción adicional es necesaria para identificarlo/a. Por favor conéctese primero a través de su sistema de autenticación, luego regrese aquí para logearse.';
$strings['tenant_id'] = 'Id. del inquilino';
$strings['tenant_id_help'] = 'Necesario para ejecutar scripts.';
+$strings['deactivate_nonexisting_users'] = 'Desactivar usuarios no existentes';
+$strings['deactivate_nonexisting_users_help'] = 'Compara los usuarios registrados en Chamilo con los de Azure y desactiva las cuentas en Chamilo que no existan en Azure.';
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 5daaad6cad..7399344b19 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -27,6 +27,7 @@ class AzureActiveDirectory extends Plugin
public const SETTING_GROUP_ID_TEACHER = 'group_id_teacher';
public const SETTING_EXISTING_USER_VERIFICATION_ORDER = 'existing_user_verification_order';
public const SETTING_TENANT_ID = 'tenant_id';
+ public const SETTING_DEACTIVATE_NONEXISTING_USERS = 'deactivate_nonexisting_users';
public const URL_TYPE_AUTHORIZE = 'login';
public const URL_TYPE_LOGOUT = 'logout';
@@ -55,6 +56,7 @@ class AzureActiveDirectory extends Plugin
self::SETTING_GROUP_ID_TEACHER => 'text',
self::SETTING_EXISTING_USER_VERIFICATION_ORDER => 'text',
self::SETTING_TENANT_ID => 'text',
+ self::SETTING_DEACTIVATE_NONEXISTING_USERS => 'boolean',
];
parent::__construct('2.3', 'Angel Fernando Quiroz Campos, Yannick Warnier', $settings);
diff --git a/plugin/azure_active_directory/src/scripts/sync_users.php b/plugin/azure_active_directory/src/scripts/sync_users.php
index 58a65f475a..af9fc764fd 100644
--- a/plugin/azure_active_directory/src/scripts/sync_users.php
+++ b/plugin/azure_active_directory/src/scripts/sync_users.php
@@ -46,6 +46,8 @@ try {
printf("%s - Number of users obtained %d".PHP_EOL, time(), count($azureUsersInfo));
+$existingUsers = [];
+
/** @var array $user */
foreach ($azureUsersInfo as $azureUserInfo) {
try {
@@ -58,6 +60,8 @@ foreach ($azureUsersInfo as $azureUserInfo) {
'id'
);
+ $existingUsers[] = $userId;
+
$userInfo = api_get_user_info($userId);
printf("%s - UserInfo %s".PHP_EOL, time(), serialize($userInfo));
@@ -67,3 +71,25 @@ foreach ($azureUsersInfo as $azureUserInfo) {
continue;
}
}
+
+if ('true' === $plugin->get(AzureActiveDirectory::SETTING_DEACTIVATE_NONEXISTING_USERS)) {
+ echo '----------------'.PHP_EOL;
+ printf('Trying deactivate non-existing users in Azure.'.PHP_EOL, time());
+
+ $users = UserManager::getRepository()->findByAuthSource('azure');
+ $userIdList = array_map(
+ function ($user) {
+ return $user->getId();
+ },
+ $users
+ );
+
+ $nonExistingUsers = array_diff($userIdList, $existingUsers);
+
+ UserManager::deactivate_users($nonExistingUsers);
+ printf(
+ "%d - Deactivated users IDs: %s".PHP_EOL,
+ time(),
+ implode(', ', $nonExistingUsers)
+ );
+}
diff --git a/src/Chamilo/UserBundle/Repository/UserRepository.php b/src/Chamilo/UserBundle/Repository/UserRepository.php
index 8ddbf2ce42..dae382a02f 100644
--- a/src/Chamilo/UserBundle/Repository/UserRepository.php
+++ b/src/Chamilo/UserBundle/Repository/UserRepository.php
@@ -1382,4 +1382,9 @@ class UserRepository extends EntityRepository
->getQuery()
->getOneOrNullResult();
}
+
+ public function findByAuthSource(string $authSource): array
+ {
+ return $this->findBy(['authSource' => $authSource]);
+ }
}
From 7df539554beeccf94d18086c1fdec3aee70f2ec3 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Tue, 3 Sep 2024 14:21:03 -0500
Subject: [PATCH 07/22] Plugin: Azure: Add script to sync groups from Azure -
refs BT#21930
---
.../src/AzureActiveDirectory.php | 11 +++
.../src/scripts/sync_usergroups.php | 97 +++++++++++++++++++
.../src/scripts/sync_users.php | 6 +-
3 files changed, 109 insertions(+), 5 deletions(-)
create mode 100644 plugin/azure_active_directory/src/scripts/sync_usergroups.php
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 7399344b19..053e4aba4f 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -98,6 +98,17 @@ class AzureActiveDirectory extends Plugin
return $provider;
}
+ public function getProviderForApiGraph(): Azure
+ {
+ $provider = $this->getProvider();
+ $provider->urlAPI = "https://graph.microsoft.com/v1.0/";
+ $provider->resource = "https://graph.microsoft.com/";
+ $provider->tenant = $this->get(AzureActiveDirectory::SETTING_TENANT_ID);
+ $provider->authWithResource = false;
+
+ return $provider;
+ }
+
/**
* @param string $urlType Type of URL to generate
*
diff --git a/plugin/azure_active_directory/src/scripts/sync_usergroups.php b/plugin/azure_active_directory/src/scripts/sync_usergroups.php
new file mode 100644
index 0000000000..c91b7e823f
--- /dev/null
+++ b/plugin/azure_active_directory/src/scripts/sync_usergroups.php
@@ -0,0 +1,97 @@
+getProviderForApiGraph();
+
+echo 'Synchronizing groups from Azure.'.PHP_EOL;
+
+try {
+ $token = $provider->getAccessToken(
+ 'client_credentials',
+ ['resource' => $provider->resource]
+ );
+
+ $groupFields = [
+ 'id',
+ 'displayName',
+ 'description',
+ ];
+
+ $azureGroupsInfo = $provider->get(
+ 'groups?$select='.implode(',', $groupFields),
+ $token
+ );
+} catch (Exception $e) {
+ printf("%s - %s".PHP_EOL, time(), $e->getMessage());
+ die;
+}
+
+printf("%s - Number of groups obtained %d".PHP_EOL, time(), count($azureGroupsInfo));
+
+/** @var array $azureGroupInfo */
+foreach ($azureGroupsInfo as $azureGroupInfo) {
+ $usergroup = new UserGroup();
+
+ $exists = $usergroup->usergroup_exists($azureGroupInfo['displayName']);
+
+ if (!$exists) {
+ $groupId = $usergroup->save([
+ 'name' => $azureGroupInfo['displayName'],
+ 'description' => $azureGroupInfo['description'],
+ ]);
+
+ if ($groupId) {
+ printf('%d - Class created: %s'.PHP_EOL, time(), $azureGroupInfo['displayName']);
+ }
+ } else {
+ $groupId = $usergroup->getIdByName($azureGroupInfo['displayName']);
+
+ if ($groupId) {
+ $usergroup->subscribe_users_to_usergroup($groupId, []);
+
+ printf('%d - Class exists, all users unsubscribed: %s'.PHP_EOL, time(), $azureGroupInfo['displayName']);
+ }
+ }
+
+ try {
+ $userFields = [
+ 'mail',
+ 'mailNickname',
+ 'id'
+ ];
+ $azureGroupMembers = $provider->get(
+ sprintf('groups/%s/members?$select=%s', $azureGroupInfo['id'], implode(',', $userFields)),
+ $token
+ );
+ } catch (Exception $e) {
+ printf("%s - %s".PHP_EOL, time(), $e->getMessage());
+
+ continue;
+ }
+
+ $newGroupMembers = [];
+
+ foreach ($azureGroupMembers as $azureGroupMember) {
+ $userId = $plugin->getUserIdByVerificationOrder($azureGroupMember, 'id');
+
+ if ($userId) {
+ $newGroupMembers[] = $userId;
+ }
+ }
+
+ $usergroup->subscribe_users_to_usergroup($groupId, $newGroupMembers);
+ printf(
+ '%d - User IDs subscribed in class %s: %s'.PHP_EOL,
+ time(),
+ $azureGroupInfo['displayName'],
+ implode(', ', $newGroupMembers)
+ );
+}
diff --git a/plugin/azure_active_directory/src/scripts/sync_users.php b/plugin/azure_active_directory/src/scripts/sync_users.php
index af9fc764fd..82d9de6c93 100644
--- a/plugin/azure_active_directory/src/scripts/sync_users.php
+++ b/plugin/azure_active_directory/src/scripts/sync_users.php
@@ -9,11 +9,7 @@ if (PHP_SAPI !== 'cli') {
$plugin = AzureActiveDirectory::create();
-$provider = $plugin->getProvider();
-$provider->urlAPI = "https://graph.microsoft.com/v1.0/";
-$provider->resource = "https://graph.microsoft.com/";
-$provider->tenant = $plugin->get(AzureActiveDirectory::SETTING_TENANT_ID);
-$provider->authWithResource = false;
+$provider = $plugin->getProviderForApiGraph();
echo 'Synchronizing users from Azure.'.PHP_EOL;
From 22afc8eab5ac27d3edaee77826ca9f5c64e9504f Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Tue, 3 Sep 2024 14:42:15 -0500
Subject: [PATCH 08/22] Plugin: Azure: Bump version to v2.4 - refs BT#21930
---
plugin/azure_active_directory/CHANGELOG.md | 21 +++++++++++++++++++
.../src/AzureActiveDirectory.php | 2 +-
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/plugin/azure_active_directory/CHANGELOG.md b/plugin/azure_active_directory/CHANGELOG.md
index 8185067329..cb19ecdeb9 100644
--- a/plugin/azure_active_directory/CHANGELOG.md
+++ b/plugin/azure_active_directory/CHANGELOG.md
@@ -1,5 +1,26 @@
# Azure Active Directory Changelog
+## 2.4 - 2024-08-28
+
+* Added a new user extra field to save the unique Azure ID (internal UID).
+This requires manually doing the following changes to your database if you are upgrading from v2.3
+```sql
+INSERT INTO extra_field (extra_field_type, field_type, variable, display_text, default_value, field_order, visible_to_self, visible_to_others, changeable, filter, created_at) VALUES (1, 1, 'azure_uid', 'Azure UID (internal ID)', '', 1, null, null, null, null, '2024-08-28 00:00:00');
+```
+* Added a new option to set the order to verify the existing user in Chamilo
+```sql
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_existing_user_verification_order', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0);
+```
+* Added a new option to update user info during the login proccess.
+```sql
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_update_users', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0);
+```
+* Added new scripts to syncronize users and groups with users and usergroups (classes). And an option to deactivate accounts in Chamilo that do not exist in Azure.
+```sql
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_tenant_id', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0);
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url, access_url_changeable, access_url_locked) VALUES ('azure_active_directory_deactivate_nonexisting_users', 'azure_active_directory', 'setting', 'Plugins', '', 'azure_active_directory', '', '', '', 1, 1, 0);
+```
+
## 2.3 - 2021-03-30
* Added admin, session admin and teacher groups. This requires adding the following fields to your database if
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 053e4aba4f..454150986e 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -59,7 +59,7 @@ class AzureActiveDirectory extends Plugin
self::SETTING_DEACTIVATE_NONEXISTING_USERS => 'boolean',
];
- parent::__construct('2.3', 'Angel Fernando Quiroz Campos, Yannick Warnier', $settings);
+ parent::__construct('2.4', 'Angel Fernando Quiroz Campos, Yannick Warnier', $settings);
}
/**
From ca32e13288747ff58d113b9975d84fa29704dfbf Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Wed, 4 Sep 2024 15:25:38 -0500
Subject: [PATCH 09/22] Plugin: Azure: Refactor to get paginated results when
syncing users - refs BT#21930
---
.../src/AzureActiveDirectory.php | 2 +
.../src/AzureCommand.php | 23 ++++
.../src/AzureSyncUsersCommand.php | 116 ++++++++++++++++++
.../src/scripts/sync_users.php | 85 +------------
4 files changed, 147 insertions(+), 79 deletions(-)
create mode 100644 plugin/azure_active_directory/src/AzureCommand.php
create mode 100644 plugin/azure_active_directory/src/AzureSyncUsersCommand.php
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 454150986e..4bc55054a4 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -36,6 +36,8 @@ class AzureActiveDirectory extends Plugin
public const EXTRA_FIELD_AZURE_ID = 'azure_id';
public const EXTRA_FIELD_AZURE_UID = 'azure_uid';
+ public const API_PAGE_SIZE = 100;
+
/**
* AzureActiveDirectory constructor.
*/
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
new file mode 100644
index 0000000000..60e6f44f97
--- /dev/null
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -0,0 +1,23 @@
+plugin = AzureActiveDirectory::create();
+ $this->provider = $this->plugin->getProviderForApiGraph();
+ }
+}
diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
new file mode 100644
index 0000000000..826c22eb3b
--- /dev/null
+++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
@@ -0,0 +1,116 @@
+
+ * @throws Exception
+ */
+ public function __invoke(): Generator
+ {
+ yield 'Synchronizing users from Azure.';
+
+ $token = $this->provider->getAccessToken(
+ 'client_credentials',
+ ['resource' => $this->provider->resource]
+ );
+
+ $existingUsers = [];
+
+ foreach ($this->getAzureUsers($token) as $azureUserInfo) {
+ try {
+ $userId = $this->plugin->registerUser(
+ $token,
+ $this->provider,
+ $azureUserInfo,
+ 'users/' . $azureUserInfo['id'] . '/memberOf',
+ 'id',
+ 'id'
+ );
+ } catch (Exception $e) {
+ yield $e->getMessage();
+
+ continue;
+ }
+
+ $existingUsers[] = $userId;
+
+ $userInfo = api_get_user_info($userId);
+
+ yield sprintf('User info: %s', serialize($userInfo));
+ }
+
+ if ('true' === $this->plugin->get(AzureActiveDirectory::SETTING_DEACTIVATE_NONEXISTING_USERS)) {
+ yield '----------------';
+
+ yield 'Trying deactivate non-existing users in Azure';
+
+ $users = UserManager::getRepository()->findByAuthSource('azure');
+ $userIdList = array_map(
+ function ($user) {
+ return $user->getId();
+ },
+ $users
+ );
+
+ $nonExistingUsers = array_diff($userIdList, $existingUsers);
+
+ UserManager::deactivate_users($nonExistingUsers);
+
+ yield sprintf(
+ 'Deactivated users IDs: %s',
+ implode(', ', $nonExistingUsers)
+ );
+ }
+ }
+
+ /**
+ * @return Generator>
+ * @throws Exception
+ */
+ private function getAzureUsers(AccessTokenInterface $token): Generator
+ {
+ $userFields = [
+ 'givenName',
+ 'surname',
+ 'mail',
+ 'userPrincipalName',
+ 'businessPhones',
+ 'mobilePhone',
+ 'accountEnabled',
+ 'mailNickname',
+ 'id'
+ ];
+
+ $query = sprintf(
+ '$top=%d&$select=%s',
+ AzureActiveDirectory::API_PAGE_SIZE,
+ implode(',', $userFields)
+ );
+
+ do {
+ try {
+ $azureUsersRequest = $this->provider->request('get', "users?$query", $token);
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting users from Azure: '.$e->getMessage());
+ }
+
+ $azureUsersInfo = $azureUsersRequest['value'] ?? [];
+
+ foreach ($azureUsersInfo as $azureUserInfo) {
+ yield $azureUserInfo;
+ }
+
+ $hasNextLink = false;
+
+ if (!empty($azureUsersRequest['@odata.nextLink'])) {
+ $hasNextLink = true;
+ $query = parse_url($azureUsersRequest['@odata.nextLink'], PHP_URL_QUERY);
+ }
+ } while ($hasNextLink);
+ }
+}
diff --git a/plugin/azure_active_directory/src/scripts/sync_users.php b/plugin/azure_active_directory/src/scripts/sync_users.php
index 82d9de6c93..350848b004 100644
--- a/plugin/azure_active_directory/src/scripts/sync_users.php
+++ b/plugin/azure_active_directory/src/scripts/sync_users.php
@@ -1,91 +1,18 @@
getProviderForApiGraph();
-
-echo 'Synchronizing users from Azure.'.PHP_EOL;
+$command = new AzureSyncUsersCommand();
try {
- $token = $provider->getAccessToken(
- 'client_credentials',
- ['resource' => $provider->resource]
- );
-
- $userFields = [
- 'givenName',
- 'surname',
- 'mail',
- 'userPrincipalName',
- 'businessPhones',
- 'mobilePhone',
- 'accountEnabled',
- 'mailNickname',
- 'id'
- ];
-
- $azureUsersInfo = $provider->get(
- 'users?$select='.implode(',', $userFields),
- $token
- );
-} catch (Exception $e) {
- printf("%s - %s".PHP_EOL, time(), $e->getMessage());
- die;
-}
-
-printf("%s - Number of users obtained %d".PHP_EOL, time(), count($azureUsersInfo));
-
-$existingUsers = [];
-
-/** @var array $user */
-foreach ($azureUsersInfo as $azureUserInfo) {
- try {
- $userId = $plugin->registerUser(
- $token,
- $provider,
- $azureUserInfo,
- 'users/' . $azureUserInfo['id'] . '/memberOf',
- 'id',
- 'id'
- );
-
- $existingUsers[] = $userId;
-
- $userInfo = api_get_user_info($userId);
-
- printf("%s - UserInfo %s".PHP_EOL, time(), serialize($userInfo));
- } catch (Exception $e) {
- printf("%s - %s".PHP_EOL, time(), $e->getMessage());
-
- continue;
+ foreach ($command() as $str) {
+ printf("%d - %s".PHP_EOL, time(), $str);
}
-}
-
-if ('true' === $plugin->get(AzureActiveDirectory::SETTING_DEACTIVATE_NONEXISTING_USERS)) {
- echo '----------------'.PHP_EOL;
- printf('Trying deactivate non-existing users in Azure.'.PHP_EOL, time());
-
- $users = UserManager::getRepository()->findByAuthSource('azure');
- $userIdList = array_map(
- function ($user) {
- return $user->getId();
- },
- $users
- );
-
- $nonExistingUsers = array_diff($userIdList, $existingUsers);
-
- UserManager::deactivate_users($nonExistingUsers);
- printf(
- "%d - Deactivated users IDs: %s".PHP_EOL,
- time(),
- implode(', ', $nonExistingUsers)
- );
+} catch (Exception $e) {
+ printf('%s - Exception: %s'.PHP_EOL, time(), $e->getMessage());
}
From 091a9d29af8934bbae7cb1d0364aec2d3b534bb8 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Wed, 4 Sep 2024 17:52:58 -0500
Subject: [PATCH 10/22] Plugin: Azure: Refactor to get paginated results when
syncing user groups - refs BT#21930
---
.../src/AzureSyncUsergroupsCommand.php | 143 ++++++++++++++++++
.../src/scripts/sync_usergroups.php | 91 +----------
2 files changed, 149 insertions(+), 85 deletions(-)
create mode 100644 plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
new file mode 100644
index 0000000000..9102c9d82c
--- /dev/null
+++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
@@ -0,0 +1,143 @@
+
+ * @throws Exception
+ */
+ public function __invoke(): Generator
+ {
+ yield 'Synchronizing groups from Azure.';
+
+ $token = $this->provider->getAccessToken(
+ 'client_credentials',
+ ['resource' => $this->provider->resource]
+ );
+
+ foreach ($this->getAzureGroups($token) as $azureGroupInfo) {
+ $usergroup = new UserGroup();
+
+ if ($usergroup->usergroup_exists($azureGroupInfo['displayName'])) {
+ $groupId = $usergroup->getIdByName($azureGroupInfo['displayName']);
+
+ if ($groupId) {
+ $usergroup->subscribe_users_to_usergroup($groupId, []);
+
+ yield sprintf('Class exists, all users unsubscribed: %s', $azureGroupInfo['displayName']);
+ }
+ } else {
+ $groupId = $usergroup->save([
+ 'name' => $azureGroupInfo['displayName'],
+ 'description' => $azureGroupInfo['description'],
+ ]);
+
+ if ($groupId) {
+ yield sprintf('Class created: %s', $azureGroupInfo['displayName']);
+ }
+ }
+
+ $newGroupMembers = [];
+
+ foreach ($this->getAzureGroupMembers($token, $azureGroupInfo['id']) as $azureGroupMember) {
+ if ($userId = $this->plugin->getUserIdByVerificationOrder($azureGroupMember, 'id')) {
+ $newGroupMembers[] = $userId;
+ }
+ }
+
+ $usergroup->subscribe_users_to_usergroup($groupId, $newGroupMembers);
+
+ yield sprintf(
+ 'User IDs subscribed in class %s: %s',
+ $azureGroupInfo['displayName'],
+ implode(', ', $newGroupMembers)
+ );
+ }
+ }
+
+ /**
+ * @return Generator>
+ * @throws Exception
+ */
+ private function getAzureGroups(AccessTokenInterface $token): Generator
+ {
+ $groupFields = [
+ 'id',
+ 'displayName',
+ 'description',
+ ];
+
+ $query = sprintf(
+ '$top=%d&$select=%s',
+ AzureActiveDirectory::API_PAGE_SIZE,
+ implode(',', $groupFields)
+ );
+
+ do {
+ try {
+ $azureGroupsRequest = $this->provider->request('get', "groups?$query", $token);
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting groups from Azure: '.$e->getMessage());
+ }
+
+ $azureGroupsInfo = $azureGroupsRequest['value'] ?? [];
+
+ foreach ($azureGroupsInfo as $azureGroupInfo) {
+ yield $azureGroupInfo;
+ }
+
+ $hasNextLink = false;
+
+ if (!empty($azureGroupsRequest['@odata.nextLink'])) {
+ $hasNextLink = true;
+ $query = parse_url($azureGroupsRequest['@odata.nextLink'], PHP_URL_QUERY);
+ }
+ } while($hasNextLink);
+ }
+
+ /**
+ * @return Generator>
+ * @throws Exception
+ */
+ private function getAzureGroupMembers(AccessTokenInterface $token, string $groupObjectId): Generator
+ {
+ $userFields = [
+ 'mail',
+ 'mailNickname',
+ 'id'
+ ];
+ $query = sprintf(
+ '$top=%d&$select=%s',
+ AzureActiveDirectory::API_PAGE_SIZE,
+ implode(',', $userFields)
+ );
+ $hasNextLink = false;
+
+ do {
+ try {
+ $azureGroupMembersRequest = $this->provider->request(
+ 'get',
+ "groups/$groupObjectId/members?$query",
+ $token
+ );
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage());
+ }
+
+ $azureGroupMembers = $azureGroupMembersRequest['value'] ?? [];
+
+ foreach ($azureGroupMembers as $azureGroupMember) {
+ yield $azureGroupMember;
+ }
+
+ if (!empty($azureGroupMembersRequest['@odata.nextLink'])) {
+ $hasNextLink = true;
+ $query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY);
+ }
+ } while ($hasNextLink);
+ }
+}
diff --git a/plugin/azure_active_directory/src/scripts/sync_usergroups.php b/plugin/azure_active_directory/src/scripts/sync_usergroups.php
index c91b7e823f..f215a950e6 100644
--- a/plugin/azure_active_directory/src/scripts/sync_usergroups.php
+++ b/plugin/azure_active_directory/src/scripts/sync_usergroups.php
@@ -1,97 +1,18 @@
getProviderForApiGraph();
-
-echo 'Synchronizing groups from Azure.'.PHP_EOL;
+$command = new AzureSyncUsergroupsCommand();
try {
- $token = $provider->getAccessToken(
- 'client_credentials',
- ['resource' => $provider->resource]
- );
-
- $groupFields = [
- 'id',
- 'displayName',
- 'description',
- ];
-
- $azureGroupsInfo = $provider->get(
- 'groups?$select='.implode(',', $groupFields),
- $token
- );
-} catch (Exception $e) {
- printf("%s - %s".PHP_EOL, time(), $e->getMessage());
- die;
-}
-
-printf("%s - Number of groups obtained %d".PHP_EOL, time(), count($azureGroupsInfo));
-
-/** @var array $azureGroupInfo */
-foreach ($azureGroupsInfo as $azureGroupInfo) {
- $usergroup = new UserGroup();
-
- $exists = $usergroup->usergroup_exists($azureGroupInfo['displayName']);
-
- if (!$exists) {
- $groupId = $usergroup->save([
- 'name' => $azureGroupInfo['displayName'],
- 'description' => $azureGroupInfo['description'],
- ]);
-
- if ($groupId) {
- printf('%d - Class created: %s'.PHP_EOL, time(), $azureGroupInfo['displayName']);
- }
- } else {
- $groupId = $usergroup->getIdByName($azureGroupInfo['displayName']);
-
- if ($groupId) {
- $usergroup->subscribe_users_to_usergroup($groupId, []);
-
- printf('%d - Class exists, all users unsubscribed: %s'.PHP_EOL, time(), $azureGroupInfo['displayName']);
- }
- }
-
- try {
- $userFields = [
- 'mail',
- 'mailNickname',
- 'id'
- ];
- $azureGroupMembers = $provider->get(
- sprintf('groups/%s/members?$select=%s', $azureGroupInfo['id'], implode(',', $userFields)),
- $token
- );
- } catch (Exception $e) {
- printf("%s - %s".PHP_EOL, time(), $e->getMessage());
-
- continue;
+ foreach ($command() as $str) {
+ printf("%d - %s".PHP_EOL, time(), $str);
}
-
- $newGroupMembers = [];
-
- foreach ($azureGroupMembers as $azureGroupMember) {
- $userId = $plugin->getUserIdByVerificationOrder($azureGroupMember, 'id');
-
- if ($userId) {
- $newGroupMembers[] = $userId;
- }
- }
-
- $usergroup->subscribe_users_to_usergroup($groupId, $newGroupMembers);
- printf(
- '%d - User IDs subscribed in class %s: %s'.PHP_EOL,
- time(),
- $azureGroupInfo['displayName'],
- implode(', ', $newGroupMembers)
- );
+} catch (Exception $e) {
+ printf('%s - Exception: %s'.PHP_EOL, time(), $e->getMessage());
}
From 7e0862badee5f59e2486357629fc4cfe80346c19 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Wed, 4 Sep 2024 18:00:11 -0500
Subject: [PATCH 11/22] Plugin: Azure: Refactor conditions to register/update
user - refs BT#21930
---
.../src/AzureActiveDirectory.php | 201 ++++++++++--------
1 file changed, 109 insertions(+), 92 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 4bc55054a4..9a05cbfa7e 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -227,104 +227,116 @@ class AzureActiveDirectory extends Plugin
if (empty($userId)) {
// If we didn't find the user
- if ($this->get(self::SETTING_PROVISION_USERS) === 'true') {
- [
- $firstNme,
- $lastName,
- $username,
- $email,
- $phone,
- $authSource,
- $active,
- $extra,
- $userRole,
- $isAdmin,
- ] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
-
- // If the option is set to create users, create it
- $userId = UserManager::create_user(
- $firstNme,
- $lastName,
- $userRole,
- $email,
- $username,
- '',
- null,
- null,
- $phone,
- null,
- $authSource,
- null,
- $active,
- null,
- $extra,
- null,
- null,
- $isAdmin
- );
- if (!$userId) {
- throw new Exception(get_lang('UserNotAdded').' '.$azureUserInfo['userPrincipalName']);
- }
- } else {
- throw new Exception('User not found when checking the extra fields from '.$azureUserInfo['mail'].' or '.$azureUserInfo['mailNickname'].' or '.$azureUserInfo[$azureUidKey].'.');
+ if ($this->get(self::SETTING_PROVISION_USERS) !== 'true') {
+ throw new Exception('User not found when checking the extra fields from ' . $azureUserInfo['mail'] . ' or ' . $azureUserInfo['mailNickname'] . ' or ' . $azureUserInfo[$azureUidKey] . '.');
}
- } else {
- if ($this->get(self::SETTING_UPDATE_USERS) === 'true') {
- [
- $firstNme,
- $lastName,
- $username,
- $email,
- $phone,
- $authSource,
- $active,
- $extra,
- $userRole,
- $isAdmin,
- ] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
-
- $userId = UserManager::update_user(
- $userId,
- $firstNme,
- $lastName,
- $username,
- '',
- $authSource,
- $email,
- $userRole,
- null,
- $phone,
- null,
- null,
- $active,
- null,
- 0,
- $extra
- );
-
- if (!$userId) {
- throw new Exception(get_lang('CouldNotUpdateUser').' '.$azureUserInfo['userPrincipalName']);
- }
+
+ [
+ $firstNme,
+ $lastName,
+ $username,
+ $email,
+ $phone,
+ $authSource,
+ $active,
+ $extra,
+ $userRole,
+ $isAdmin,
+ ] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
+
+ // If the option is set to create users, create it
+ $userId = UserManager::create_user(
+ $firstNme,
+ $lastName,
+ $userRole,
+ $email,
+ $username,
+ '',
+ null,
+ null,
+ $phone,
+ null,
+ $authSource,
+ null,
+ $active,
+ null,
+ $extra,
+ null,
+ null,
+ $isAdmin
+ );
+
+ if (!$userId) {
+ throw new Exception(get_lang('UserNotAdded') . ' ' . $azureUserInfo['userPrincipalName']);
+ }
+
+ return $userId;
+ }
+
+ if ($this->get(self::SETTING_UPDATE_USERS) === 'true') {
+ [
+ $firstNme,
+ $lastName,
+ $username,
+ $email,
+ $phone,
+ $authSource,
+ $active,
+ $extra,
+ $userRole,
+ $isAdmin,
+ ] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
+
+ $userId = UserManager::update_user(
+ $userId,
+ $firstNme,
+ $lastName,
+ $username,
+ '',
+ $authSource,
+ $email,
+ $userRole,
+ null,
+ $phone,
+ null,
+ null,
+ $active,
+ null,
+ 0,
+ $extra
+ );
+
+ if (!$userId) {
+ throw new Exception(get_lang('CouldNotUpdateUser').' '.$azureUserInfo['userPrincipalName']);
}
}
return $userId;
}
+ /**
+ * @throws Exception
+ */
private function formatUserData(
AccessTokenInterface $token,
Azure $provider,
array $azureUserInfo,
string $apiGroupsRef,
- string $objectIdKey,
+ string $groupObjectIdKey,
string $azureUidKey
): array {
- [$userRole, $isAdmin] = $this->getUserRoleAndCheckIsAdmin(
- $token,
- $provider,
- $apiGroupsRef,
- $objectIdKey
- );
+ try {
+ [$userRole, $isAdmin] = $this->getUserRoleAndCheckIsAdmin(
+ $token,
+ $provider,
+ $apiGroupsRef,
+ $groupObjectIdKey
+ );
+ } catch (Exception $e) {
+ throw new Exception(
+ 'Exception when formatting user '.$azureUserInfo[$azureUidKey].' data: '.$e->getMessage()
+ );
+ }
$phone = null;
@@ -363,15 +375,20 @@ class AzureActiveDirectory extends Plugin
];
}
+ /**
+ * @throws Exception
+ */
private function getUserRoleAndCheckIsAdmin(
AccessTokenInterface $token,
- Azure $provider = null,
+ Azure $provider,
string $apiRef = 'me/memberOf',
- string $objectIdKey = 'objectId'
+ string $groupObjectIdKey = 'objectId'
): array {
- $provider = $provider ?: $this->getProvider();
-
- $groups = $provider->get($apiRef, $token);
+ try {
+ $groups = $provider->get($apiRef, $token);
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting user groups from Azure: '.$e->getMessage());
+ }
// If any specific group ID has been defined for a specific role, use that
// ID to give the user the right role
@@ -381,12 +398,12 @@ class AzureActiveDirectory extends Plugin
$userRole = STUDENT;
$isAdmin = false;
foreach ($groups as $group) {
- if ($givenAdminGroup == $group[$objectIdKey]) {
+ if ($givenAdminGroup == $group[$groupObjectIdKey]) {
$userRole = COURSEMANAGER;
$isAdmin = true;
- } elseif ($givenSessionAdminGroup == $group[$objectIdKey]) {
+ } elseif ($givenSessionAdminGroup == $group[$groupObjectIdKey]) {
$userRole = SESSIONADMIN;
- } elseif ($userRole != SESSIONADMIN && $givenTeacherGroup == $group[$objectIdKey]) {
+ } elseif ($userRole != SESSIONADMIN && $givenTeacherGroup == $group[$groupObjectIdKey]) {
$userRole = COURSEMANAGER;
}
}
From e3978e15d448375fa19a6ada1eae21e8c7036207 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Wed, 4 Sep 2024 18:32:35 -0500
Subject: [PATCH 12/22] Plugin: Azure: Reload plugin settings when running
scripts - refs BT#21930
---
plugin/azure_active_directory/src/AzureCommand.php | 1 +
plugin/azure_active_directory/src/scripts/sync_usergroups.php | 3 +++
plugin/azure_active_directory/src/scripts/sync_users.php | 3 +++
3 files changed, 7 insertions(+)
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
index 60e6f44f97..18ecc04284 100644
--- a/plugin/azure_active_directory/src/AzureCommand.php
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -18,6 +18,7 @@ class AzureCommand
public function __construct()
{
$this->plugin = AzureActiveDirectory::create();
+ $this->plugin->get_settings(true);
$this->provider = $this->plugin->getProviderForApiGraph();
}
}
diff --git a/plugin/azure_active_directory/src/scripts/sync_usergroups.php b/plugin/azure_active_directory/src/scripts/sync_usergroups.php
index f215a950e6..8ad128c5db 100644
--- a/plugin/azure_active_directory/src/scripts/sync_usergroups.php
+++ b/plugin/azure_active_directory/src/scripts/sync_usergroups.php
@@ -7,6 +7,9 @@ if (PHP_SAPI !== 'cli') {
exit('Run this script through the command line or comment this line in the code');
}
+// Uncomment to indicate the access url to get the plugin settings when using multi-url
+//$_configuration['access_url'] = 1;
+
$command = new AzureSyncUsergroupsCommand();
try {
diff --git a/plugin/azure_active_directory/src/scripts/sync_users.php b/plugin/azure_active_directory/src/scripts/sync_users.php
index 350848b004..33eb4768e6 100644
--- a/plugin/azure_active_directory/src/scripts/sync_users.php
+++ b/plugin/azure_active_directory/src/scripts/sync_users.php
@@ -7,6 +7,9 @@ if (PHP_SAPI !== 'cli') {
exit('Run this script through the command line or comment this line in the code');
}
+// Uncomment to indicate the access url to get the plugin settings when using multi-url
+//$_configuration['access_url'] = 1;
+
$command = new AzureSyncUsersCommand();
try {
From 228c3dc8fd932e0cb31ed4467a70186ed152cedf Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Wed, 4 Sep 2024 18:33:28 -0500
Subject: [PATCH 13/22] Minor: Format code - refs BT#21930
---
.../src/AzureActiveDirectory.php | 11 +++++------
.../src/AzureSyncUsergroupsCommand.php | 13 ++++++++-----
.../src/AzureSyncUsersCommand.php | 10 ++++++----
3 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 9a05cbfa7e..f3467cebea 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -180,7 +180,8 @@ class AzureActiveDirectory extends Plugin
return $defaultOrder;
}
- public function getUserIdByVerificationOrder(array $azureUserData, string $azureUidKey = 'objectId'): ?int {
+ public function getUserIdByVerificationOrder(array $azureUserData, string $azureUidKey = 'objectId'): ?int
+ {
$selectedOrder = $this->getExistingUserVerificationOrder();
$extraFieldValue = new ExtraFieldValue('user');
@@ -228,7 +229,7 @@ class AzureActiveDirectory extends Plugin
if (empty($userId)) {
// If we didn't find the user
if ($this->get(self::SETTING_PROVISION_USERS) !== 'true') {
- throw new Exception('User not found when checking the extra fields from ' . $azureUserInfo['mail'] . ' or ' . $azureUserInfo['mailNickname'] . ' or ' . $azureUserInfo[$azureUidKey] . '.');
+ throw new Exception('User not found when checking the extra fields from '.$azureUserInfo['mail'].' or '.$azureUserInfo['mailNickname'].' or '.$azureUserInfo[$azureUidKey].'.');
}
[
@@ -267,7 +268,7 @@ class AzureActiveDirectory extends Plugin
);
if (!$userId) {
- throw new Exception(get_lang('UserNotAdded') . ' ' . $azureUserInfo['userPrincipalName']);
+ throw new Exception(get_lang('UserNotAdded').' '.$azureUserInfo['userPrincipalName']);
}
return $userId;
@@ -333,9 +334,7 @@ class AzureActiveDirectory extends Plugin
$groupObjectIdKey
);
} catch (Exception $e) {
- throw new Exception(
- 'Exception when formatting user '.$azureUserInfo[$azureUidKey].' data: '.$e->getMessage()
- );
+ throw new Exception('Exception when formatting user '.$azureUserInfo[$azureUidKey].' data: '.$e->getMessage());
}
$phone = null;
diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
index 9102c9d82c..6677683596 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
@@ -7,8 +7,9 @@ use League\OAuth2\Client\Token\AccessTokenInterface;
class AzureSyncUsergroupsCommand extends AzureCommand
{
/**
- * @return Generator
* @throws Exception
+ *
+ * @return Generator
*/
public function __invoke(): Generator
{
@@ -60,8 +61,9 @@ class AzureSyncUsergroupsCommand extends AzureCommand
}
/**
- * @return Generator>
* @throws Exception
+ *
+ * @return Generator>
*/
private function getAzureGroups(AccessTokenInterface $token): Generator
{
@@ -96,19 +98,20 @@ class AzureSyncUsergroupsCommand extends AzureCommand
$hasNextLink = true;
$query = parse_url($azureGroupsRequest['@odata.nextLink'], PHP_URL_QUERY);
}
- } while($hasNextLink);
+ } while ($hasNextLink);
}
/**
- * @return Generator>
* @throws Exception
+ *
+ * @return Generator>
*/
private function getAzureGroupMembers(AccessTokenInterface $token, string $groupObjectId): Generator
{
$userFields = [
'mail',
'mailNickname',
- 'id'
+ 'id',
];
$query = sprintf(
'$top=%d&$select=%s',
diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
index 826c22eb3b..3e3cf3b11a 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
@@ -7,8 +7,9 @@ use League\OAuth2\Client\Token\AccessTokenInterface;
class AzureSyncUsersCommand extends AzureCommand
{
/**
- * @return Generator
* @throws Exception
+ *
+ * @return Generator
*/
public function __invoke(): Generator
{
@@ -27,7 +28,7 @@ class AzureSyncUsersCommand extends AzureCommand
$token,
$this->provider,
$azureUserInfo,
- 'users/' . $azureUserInfo['id'] . '/memberOf',
+ 'users/'.$azureUserInfo['id'].'/memberOf',
'id',
'id'
);
@@ -69,8 +70,9 @@ class AzureSyncUsersCommand extends AzureCommand
}
/**
- * @return Generator>
* @throws Exception
+ *
+ * @return Generator>
*/
private function getAzureUsers(AccessTokenInterface $token): Generator
{
@@ -83,7 +85,7 @@ class AzureSyncUsersCommand extends AzureCommand
'mobilePhone',
'accountEnabled',
'mailNickname',
- 'id'
+ 'id',
];
$query = sprintf(
From c9d99a60dbbbab38212dcd5fec29533da3ed5285 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Fri, 6 Sep 2024 16:56:49 -0500
Subject: [PATCH 14/22] Plugin: Azure: Request a new access token when it
expires - refs BT#21930
---
.../src/AzureActiveDirectory.php | 6 +++---
.../azure_active_directory/src/AzureCommand.php | 17 +++++++++++++++++
.../src/AzureSyncUsergroupsCommand.php | 9 +++++----
.../src/AzureSyncUsersCommand.php | 15 ++++++++++-----
4 files changed, 35 insertions(+), 12 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index f3467cebea..88f1e079ab 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -213,7 +213,7 @@ class AzureActiveDirectory extends Plugin
* @throws Exception
*/
public function registerUser(
- AccessTokenInterface $token,
+ AccessTokenInterface &$token,
Azure $provider,
array $azureUserInfo,
string $apiGroupsRef = 'me/memberOf',
@@ -319,7 +319,7 @@ class AzureActiveDirectory extends Plugin
* @throws Exception
*/
private function formatUserData(
- AccessTokenInterface $token,
+ AccessTokenInterface &$token,
Azure $provider,
array $azureUserInfo,
string $apiGroupsRef,
@@ -378,7 +378,7 @@ class AzureActiveDirectory extends Plugin
* @throws Exception
*/
private function getUserRoleAndCheckIsAdmin(
- AccessTokenInterface $token,
+ AccessTokenInterface &$token,
Azure $provider,
string $apiRef = 'me/memberOf',
string $groupObjectIdKey = 'objectId'
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
index 18ecc04284..6ebd9ded2b 100644
--- a/plugin/azure_active_directory/src/AzureCommand.php
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -2,6 +2,8 @@
/* For license terms, see /license.txt */
+use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
+use League\OAuth2\Client\Token\AccessTokenInterface;
use TheNetworg\OAuth2\Client\Provider\Azure;
class AzureCommand
@@ -21,4 +23,19 @@ class AzureCommand
$this->plugin->get_settings(true);
$this->provider = $this->plugin->getProviderForApiGraph();
}
+
+ /**
+ * @throws IdentityProviderException
+ */
+ protected function getToken(?AccessTokenInterface $currentToken = null): AccessTokenInterface
+ {
+ if (!$currentToken || ($currentToken->getExpires() && !$currentToken->getRefreshToken())) {
+ return $this->provider->getAccessToken(
+ 'client_credentials',
+ ['resource' => $this->provider->resource]
+ );
+ }
+
+ return $currentToken;
+ }
}
diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
index 6677683596..26a6215d1c 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
@@ -15,10 +15,7 @@ class AzureSyncUsergroupsCommand extends AzureCommand
{
yield 'Synchronizing groups from Azure.';
- $token = $this->provider->getAccessToken(
- 'client_credentials',
- ['resource' => $this->provider->resource]
- );
+ $token = $this->getToken();
foreach ($this->getAzureGroups($token) as $azureGroupInfo) {
$usergroup = new UserGroup();
@@ -80,6 +77,8 @@ class AzureSyncUsergroupsCommand extends AzureCommand
);
do {
+ $token = $this->getToken($token);
+
try {
$azureGroupsRequest = $this->provider->request('get', "groups?$query", $token);
} catch (Exception $e) {
@@ -121,6 +120,8 @@ class AzureSyncUsergroupsCommand extends AzureCommand
$hasNextLink = false;
do {
+ $token = $this->getToken($token);
+
try {
$azureGroupMembersRequest = $this->provider->request(
'get',
diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
index 3e3cf3b11a..6bfed0b792 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
@@ -15,15 +15,14 @@ class AzureSyncUsersCommand extends AzureCommand
{
yield 'Synchronizing users from Azure.';
- $token = $this->provider->getAccessToken(
- 'client_credentials',
- ['resource' => $this->provider->resource]
- );
+ $token = $this->getToken();
$existingUsers = [];
foreach ($this->getAzureUsers($token) as $azureUserInfo) {
try {
+ $token = $this->getToken($token);
+
$userId = $this->plugin->registerUser(
$token,
$this->provider,
@@ -95,8 +94,14 @@ class AzureSyncUsersCommand extends AzureCommand
);
do {
+ $token = $this->getToken($token);
+
try {
- $azureUsersRequest = $this->provider->request('get', "users?$query", $token);
+ $azureUsersRequest = $this->provider->request(
+ 'get',
+ "users?$query",
+ $token
+ );
} catch (Exception $e) {
throw new Exception('Exception when requesting users from Azure: '.$e->getMessage());
}
From d2ebff9e0ed6000bac73f0eb19c89156353027df Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Wed, 11 Sep 2024 19:08:15 -0500
Subject: [PATCH 15/22] Plugin: Azure: Optimize request when
registering/updating user - refs BT#21930
---
.../src/AzureActiveDirectory.php | 42 +++++++++++--------
.../src/AzureCommand.php | 4 ++
2 files changed, 29 insertions(+), 17 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 88f1e079ab..4bb6b25f9c 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -383,27 +383,35 @@ class AzureActiveDirectory extends Plugin
string $apiRef = 'me/memberOf',
string $groupObjectIdKey = 'objectId'
): array {
- try {
- $groups = $provider->get($apiRef, $token);
- } catch (Exception $e) {
- throw new Exception('Exception when requesting user groups from Azure: '.$e->getMessage());
- }
-
// If any specific group ID has been defined for a specific role, use that
// ID to give the user the right role
- $givenAdminGroup = $this->get(self::SETTING_GROUP_ID_ADMIN);
- $givenSessionAdminGroup = $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN);
- $givenTeacherGroup = $this->get(self::SETTING_GROUP_ID_TEACHER);
$userRole = STUDENT;
$isAdmin = false;
- foreach ($groups as $group) {
- if ($givenAdminGroup == $group[$groupObjectIdKey]) {
- $userRole = COURSEMANAGER;
- $isAdmin = true;
- } elseif ($givenSessionAdminGroup == $group[$groupObjectIdKey]) {
- $userRole = SESSIONADMIN;
- } elseif ($userRole != SESSIONADMIN && $givenTeacherGroup == $group[$groupObjectIdKey]) {
- $userRole = COURSEMANAGER;
+
+ $groupRoles = [
+ 'admin' => $this->get(self::SETTING_GROUP_ID_ADMIN),
+ 'sessionAdmin' => $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN),
+ 'teacher' => $this->get(self::SETTING_GROUP_ID_TEACHER),
+ ];
+
+ if ($groupRoles = array_filter($groupRoles)) {
+ try {
+ $groups = $provider->get($apiRef, $token);
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting user groups from Azure: '.$e->getMessage());
+ }
+
+ foreach ($groups as $group) {
+ $groupId = $group[$groupObjectIdKey];
+
+ if (isset($groupRoles['admin']) && $groupRoles['admin'] === $groupId) {
+ $userRole = COURSEMANAGER;
+ $isAdmin = true;
+ } elseif (isset($groupRoles['sessionAdmin']) && $groupRoles['sessionAdmin'] === $groupId) {
+ $userRole = SESSIONADMIN;
+ } elseif (isset($groupRoles['teacher']) && $groupRoles['teacher'] === $groupId && $userRole !== SESSIONADMIN) {
+ $userRole = COURSEMANAGER;
+ }
}
}
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
index 6ebd9ded2b..9151705aad 100644
--- a/plugin/azure_active_directory/src/AzureCommand.php
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -16,6 +16,10 @@ class AzureCommand
* @var Azure
*/
protected $provider;
+ /**
+ * @var AccessTokenInterface
+ */
+ private $token;
public function __construct()
{
From a600f8bb1bebec7ef783fe06e50159fd58bc1161 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Thu, 12 Sep 2024 12:18:56 -0500
Subject: [PATCH 16/22] Plugin: Azure: Increase page size for results - refs
BT#21930
---
plugin/azure_active_directory/src/AzureActiveDirectory.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index 4bb6b25f9c..d9374cbb1d 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -36,7 +36,7 @@ class AzureActiveDirectory extends Plugin
public const EXTRA_FIELD_AZURE_ID = 'azure_id';
public const EXTRA_FIELD_AZURE_UID = 'azure_uid';
- public const API_PAGE_SIZE = 100;
+ public const API_PAGE_SIZE = 999;
/**
* AzureActiveDirectory constructor.
From 95426d37c3b468c0974a507e8bbbb3401055206e Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Fri, 13 Sep 2024 14:10:19 -0500
Subject: [PATCH 17/22] Plugin: Azure: Register users as student and then
update their status according defined groups - refs BT#21930
---
main/inc/lib/usermanager.lib.php | 8 +-
.../src/AzureActiveDirectory.php | 98 ++++++----------
.../src/AzureCommand.php | 17 ++-
.../src/AzureSyncUsersCommand.php | 106 ++++++++++++++----
.../azure_active_directory/src/callback.php | 35 ++++--
5 files changed, 164 insertions(+), 100 deletions(-)
diff --git a/main/inc/lib/usermanager.lib.php b/main/inc/lib/usermanager.lib.php
index 183f082b29..2893af7fd1 100755
--- a/main/inc/lib/usermanager.lib.php
+++ b/main/inc/lib/usermanager.lib.php
@@ -6250,7 +6250,7 @@ class UserManager
return $icon_link;
}
- public static function addUserAsAdmin(User $user)
+ public static function addUserAsAdmin(User $user, bool $andFlush = true)
{
if ($user) {
$userId = $user->getId();
@@ -6261,11 +6261,11 @@ class UserManager
}
$user->addRole('ROLE_SUPER_ADMIN');
- self::getManager()->updateUser($user, true);
+ self::getManager()->updateUser($user, $andFlush);
}
}
- public static function removeUserAdmin(User $user)
+ public static function removeUserAdmin(User $user, bool $andFlush = true)
{
$userId = (int) $user->getId();
if (self::is_admin($userId)) {
@@ -6273,7 +6273,7 @@ class UserManager
$sql = "DELETE FROM $table WHERE user_id = $userId";
Database::query($sql);
$user->removeRole('ROLE_SUPER_ADMIN');
- self::getManager()->updateUser($user, true);
+ self::getManager()->updateUser($user, $andFlush);
}
}
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index d9374cbb1d..d0f6e466ce 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -1,7 +1,7 @@
formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
+ ] = $this->formatUserData($azureUserInfo, $azureUidKey);
// If the option is set to create users, create it
$userId = UserManager::create_user(
$firstNme,
$lastName,
- $userRole,
+ STUDENT,
$email,
$username,
'',
@@ -263,8 +257,7 @@ class AzureActiveDirectory extends Plugin
null,
$extra,
null,
- null,
- $isAdmin
+ null
);
if (!$userId) {
@@ -284,9 +277,7 @@ class AzureActiveDirectory extends Plugin
$authSource,
$active,
$extra,
- $userRole,
- $isAdmin,
- ] = $this->formatUserData($token, $provider, $azureUserInfo, $apiGroupsRef, $objectIdKey, $azureUidKey);
+ ] = $this->formatUserData($azureUserInfo, $azureUidKey);
$userId = UserManager::update_user(
$userId,
@@ -296,7 +287,7 @@ class AzureActiveDirectory extends Plugin
'',
$authSource,
$email,
- $userRole,
+ STUDENT,
null,
$phone,
null,
@@ -319,24 +310,9 @@ class AzureActiveDirectory extends Plugin
* @throws Exception
*/
private function formatUserData(
- AccessTokenInterface &$token,
- Azure $provider,
array $azureUserInfo,
- string $apiGroupsRef,
- string $groupObjectIdKey,
string $azureUidKey
): array {
- try {
- [$userRole, $isAdmin] = $this->getUserRoleAndCheckIsAdmin(
- $token,
- $provider,
- $apiGroupsRef,
- $groupObjectIdKey
- );
- } catch (Exception $e) {
- throw new Exception('Exception when formatting user '.$azureUserInfo[$azureUidKey].' data: '.$e->getMessage());
- }
-
$phone = null;
if (isset($azureUserInfo['telephoneNumber'])) {
@@ -369,52 +345,44 @@ class AzureActiveDirectory extends Plugin
$authSource,
$active,
$extra,
- $userRole,
- $isAdmin,
];
}
/**
- * @throws Exception
+ * @return array
*/
- private function getUserRoleAndCheckIsAdmin(
- AccessTokenInterface &$token,
- Azure $provider,
- string $apiRef = 'me/memberOf',
- string $groupObjectIdKey = 'objectId'
- ): array {
- // If any specific group ID has been defined for a specific role, use that
- // ID to give the user the right role
- $userRole = STUDENT;
- $isAdmin = false;
-
- $groupRoles = [
+ public function getGroupUidByRole(): array
+ {
+ $groupUidList = [
'admin' => $this->get(self::SETTING_GROUP_ID_ADMIN),
'sessionAdmin' => $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN),
'teacher' => $this->get(self::SETTING_GROUP_ID_TEACHER),
];
- if ($groupRoles = array_filter($groupRoles)) {
- try {
- $groups = $provider->get($apiRef, $token);
- } catch (Exception $e) {
- throw new Exception('Exception when requesting user groups from Azure: '.$e->getMessage());
- }
+ return array_filter($groupUidList);
+ }
- foreach ($groups as $group) {
- $groupId = $group[$groupObjectIdKey];
-
- if (isset($groupRoles['admin']) && $groupRoles['admin'] === $groupId) {
- $userRole = COURSEMANAGER;
- $isAdmin = true;
- } elseif (isset($groupRoles['sessionAdmin']) && $groupRoles['sessionAdmin'] === $groupId) {
- $userRole = SESSIONADMIN;
- } elseif (isset($groupRoles['teacher']) && $groupRoles['teacher'] === $groupId && $userRole !== SESSIONADMIN) {
- $userRole = COURSEMANAGER;
- }
- }
- }
+ /**
+ * @return array
+ */
+ public function getUpdateActionByRole(): array
+ {
+ return [
+ 'admin' => function (User $user) {
+ $user->setStatus(COURSEMANAGER);
+
+ UserManager::addUserAsAdmin($user, false);
+ },
+ 'sessionAdmin' => function (User $user) {
+ $user->setStatus(SESSIONADMIN);
- return [$userRole, $isAdmin];
+ UserManager::removeUserAdmin($user, false);
+ },
+ 'teacher' => function (User $user) {
+ $user->setStatus(COURSEMANAGER);
+
+ UserManager::removeUserAdmin($user, false);
+ },
+ ];
}
}
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
index 9151705aad..9790c174f1 100644
--- a/plugin/azure_active_directory/src/AzureCommand.php
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -16,10 +16,6 @@ class AzureCommand
* @var Azure
*/
protected $provider;
- /**
- * @var AccessTokenInterface
- */
- private $token;
public function __construct()
{
@@ -42,4 +38,17 @@ class AzureCommand
return $currentToken;
}
+
+ /**
+ * @throws IdentityProviderException
+ */
+ protected function generateOrRefreshToken(?AccessTokenInterface &$token)
+ {
+ if (!$token || ($token->getExpires() && !$token->getRefreshToken())) {
+ $token = $this->provider->getAccessToken(
+ 'client_credentials',
+ ['resource' => $this->provider->resource]
+ );
+ }
+ }
}
diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
index 6bfed0b792..fea4f77af1 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
@@ -2,7 +2,7 @@
/* For license terms, see /license.txt */
-use League\OAuth2\Client\Token\AccessTokenInterface;
+use Chamilo\UserBundle\Entity\User;
class AzureSyncUsersCommand extends AzureCommand
{
@@ -15,33 +15,54 @@ class AzureSyncUsersCommand extends AzureCommand
{
yield 'Synchronizing users from Azure.';
- $token = $this->getToken();
-
+ /** @var array $existingUsers */
$existingUsers = [];
- foreach ($this->getAzureUsers($token) as $azureUserInfo) {
+ foreach ($this->getAzureUsers() as $azureUserInfo) {
try {
- $token = $this->getToken($token);
-
- $userId = $this->plugin->registerUser(
- $token,
- $this->provider,
- $azureUserInfo,
- 'users/'.$azureUserInfo['id'].'/memberOf',
- 'id',
- 'id'
- );
+ $userId = $this->plugin->registerUser($azureUserInfo, 'id');
} catch (Exception $e) {
yield $e->getMessage();
continue;
}
- $existingUsers[] = $userId;
+ $existingUsers[$azureUserInfo['id']] = $userId;
+
+ yield sprintf('User (ID %d) with received info: %s ', $userId, serialize($azureUserInfo));
+ }
+
+ yield '----------------';
+ yield 'Updating users status';
+
+ $roleGroups = $this->plugin->getGroupUidByRole();
+ $roleActions = $this->plugin->getUpdateActionByRole();
+
+ $userManager = UserManager::getManager();
+ $em = Database::getManager();
+
+ foreach ($roleGroups as $userRole => $groupUid) {
+ $azureGroupMembersInfo = iterator_to_array($this->getAzureGroupMembers($groupUid));
+ $azureGroupMembersUids = array_column($azureGroupMembersInfo, 'id');
+
+ foreach ($azureGroupMembersUids as $azureGroupMembersUid) {
+ $userId = $existingUsers[$azureGroupMembersUid] ?? null;
+
+ if (!$userId) {
+ continue;
+ }
+
+ if (isset($roleActions[$userRole])) {
+ /** @var User $user */
+ $user = $userManager->find($userId);
+
+ $roleActions[$userRole]($user);
- $userInfo = api_get_user_info($userId);
+ yield sprintf('User (ID %d) status %s', $userId, $userRole);
+ }
+ }
- yield sprintf('User info: %s', serialize($userInfo));
+ $em->flush();
}
if ('true' === $this->plugin->get(AzureActiveDirectory::SETTING_DEACTIVATE_NONEXISTING_USERS)) {
@@ -73,7 +94,7 @@ class AzureSyncUsersCommand extends AzureCommand
*
* @return Generator>
*/
- private function getAzureUsers(AccessTokenInterface $token): Generator
+ private function getAzureUsers(): Generator
{
$userFields = [
'givenName',
@@ -93,8 +114,10 @@ class AzureSyncUsersCommand extends AzureCommand
implode(',', $userFields)
);
+ $token = null;
+
do {
- $token = $this->getToken($token);
+ $this->generateOrRefreshToken($token);
try {
$azureUsersRequest = $this->provider->request(
@@ -120,4 +143,49 @@ class AzureSyncUsersCommand extends AzureCommand
}
} while ($hasNextLink);
}
+
+ /**
+ * @throws Exception
+ */
+ public function getAzureGroupMembers(string $groupUid): Generator
+ {
+ $userFields = [
+ 'id',
+ ];
+
+ $query = sprintf(
+ '$top=%d&$select=%s',
+ AzureActiveDirectory::API_PAGE_SIZE,
+ implode(',', $userFields)
+ );
+
+ $token = null;
+
+ do {
+ $this->generateOrRefreshToken($token);
+
+ try {
+ $azureGroupMembersRequest = $this->provider->request(
+ 'get',
+ "groups/$groupUid/members?$query",
+ $token
+ );
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage());
+ }
+
+ $azureGroupMembers = $azureGroupMembersRequest['value'] ?? [];
+
+ foreach ($azureGroupMembers as $azureGroupMember) {
+ yield $azureGroupMember;
+ }
+
+ $hasNextLink = false;
+
+ if (!empty($azureGroupMembersRequest['@odata.nextLink'])) {
+ $hasNextLink = true;
+ $query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY);
+ }
+ } while ($hasNextLink);
+ }
}
diff --git a/plugin/azure_active_directory/src/callback.php b/plugin/azure_active_directory/src/callback.php
index 09d0b363d6..6e59c9c812 100644
--- a/plugin/azure_active_directory/src/callback.php
+++ b/plugin/azure_active_directory/src/callback.php
@@ -4,6 +4,9 @@
* Callback script for Azure. The URL of this file is sent to Azure as a
* point of contact to send particular signals.
*/
+
+use Chamilo\UserBundle\Entity\User;
+
require __DIR__.'/../../../main/inc/global.inc.php';
if (!empty($_GET['error']) && !empty($_GET['state'])) {
@@ -85,17 +88,33 @@ try {
throw new Exception('The id field is empty in Azure AD and is needed to set the unique Azure ID for this user.');
}
- $userId = $plugin->registerUser(
- $token,
- $provider,
- $me
- );
+ $userId = $plugin->registerUser($me);
- $userInfo = api_get_user_info($userId);
+ if ($roleGroups = $plugin->getGroupUidByRole()) {
+ $roleActions = $plugin->getUpdateActionByRole();
+ /** @var User $user */
+ $user = UserManager::getManager()->find($userId);
+
+ $azureGroups = $provider->get('me/memberOf', $token);
+
+ foreach ($azureGroups as $azureGroup) {
+ $azureGroupUid = $azureGroup['objectId'];
- //TODO add user update management for groups
+ foreach ($roleGroups as $userRole => $groupUid) {
+ if ($azureGroupUid === $groupUid) {
+ $roleActions[$userRole]($user);
+
+ break 2;
+ }
+ }
+ }
+
+ Database::getManager()->flush();
+ }
+
+ $userInfo = api_get_user_info($userId);
- //TODO add support if user exists in another URL but is validated in this one, add the user to access_url_rel_user
+ /* @TODO add support if user exists in another URL but is validated in this one, add the user to access_url_rel_user */
if (empty($userInfo)) {
throw new Exception('User '.$userId.' not found.');
From 90588f255884b1f567c4217fc338e99c666e524b Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Fri, 13 Sep 2024 16:49:10 -0500
Subject: [PATCH 18/22] Plugin: Azure: Fix script to sync user groups and
divide process to subscriptions - refs BT#21930
---
.../src/AzureCommand.php | 62 ++++++++++----
.../src/AzureSyncUsergroupsCommand.php | 82 ++++++-------------
.../src/AzureSyncUsersCommand.php | 45 ----------
3 files changed, 73 insertions(+), 116 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
index 9790c174f1..6a14c94e07 100644
--- a/plugin/azure_active_directory/src/AzureCommand.php
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -6,7 +6,7 @@ use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Token\AccessTokenInterface;
use TheNetworg\OAuth2\Client\Provider\Azure;
-class AzureCommand
+abstract class AzureCommand
{
/**
* @var AzureActiveDirectory
@@ -27,28 +27,62 @@ class AzureCommand
/**
* @throws IdentityProviderException
*/
- protected function getToken(?AccessTokenInterface $currentToken = null): AccessTokenInterface
+ protected function generateOrRefreshToken(?AccessTokenInterface &$token)
{
- if (!$currentToken || ($currentToken->getExpires() && !$currentToken->getRefreshToken())) {
- return $this->provider->getAccessToken(
+ if (!$token || ($token->getExpires() && !$token->getRefreshToken())) {
+ $token = $this->provider->getAccessToken(
'client_credentials',
['resource' => $this->provider->resource]
);
}
-
- return $currentToken;
}
/**
- * @throws IdentityProviderException
+ * @return Generator>
+ *
+ * @throws Exception
*/
- protected function generateOrRefreshToken(?AccessTokenInterface &$token)
+ protected function getAzureGroupMembers(string $groupUid): Generator
{
- if (!$token || ($token->getExpires() && !$token->getRefreshToken())) {
- $token = $this->provider->getAccessToken(
- 'client_credentials',
- ['resource' => $this->provider->resource]
- );
- }
+ $userFields = [
+ 'mail',
+ 'mailNickname',
+ 'id',
+ ];
+
+ $query = sprintf(
+ '$top=%d&$select=%s',
+ AzureActiveDirectory::API_PAGE_SIZE,
+ implode(',', $userFields)
+ );
+
+ $token = null;
+
+ do {
+ $this->generateOrRefreshToken($token);
+
+ try {
+ $azureGroupMembersRequest = $this->provider->request(
+ 'get',
+ "groups/$groupUid/members?$query",
+ $token
+ );
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage());
+ }
+
+ $azureGroupMembers = $azureGroupMembersRequest['value'] ?? [];
+
+ foreach ($azureGroupMembers as $azureGroupMember) {
+ yield $azureGroupMember;
+ }
+
+ $hasNextLink = false;
+
+ if (!empty($azureGroupMembersRequest['@odata.nextLink'])) {
+ $hasNextLink = true;
+ $query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY);
+ }
+ } while ($hasNextLink);
}
}
diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
index 26a6215d1c..2fe7c0508f 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
@@ -15,11 +15,11 @@ class AzureSyncUsergroupsCommand extends AzureCommand
{
yield 'Synchronizing groups from Azure.';
- $token = $this->getToken();
+ $usergroup = new UserGroup();
- foreach ($this->getAzureGroups($token) as $azureGroupInfo) {
- $usergroup = new UserGroup();
+ $groupIdByUid = [];
+ foreach ($this->getAzureGroups() as $azureGroupInfo) {
if ($usergroup->usergroup_exists($azureGroupInfo['displayName'])) {
$groupId = $usergroup->getIdByName($azureGroupInfo['displayName']);
@@ -39,21 +39,32 @@ class AzureSyncUsergroupsCommand extends AzureCommand
}
}
+ $groupIdByUid[$azureGroupInfo['id']] = $groupId;
+ }
+
+ yield '----------------';
+ yield 'Subscribing users to groups';
+
+ foreach ($groupIdByUid as $azureGroupUid => $groupId) {
$newGroupMembers = [];
- foreach ($this->getAzureGroupMembers($token, $azureGroupInfo['id']) as $azureGroupMember) {
+ yield sprintf('Obtaining members for group (ID %d)', $groupId);
+
+ foreach ($this->getAzureGroupMembers($azureGroupUid) as $azureGroupMember) {
if ($userId = $this->plugin->getUserIdByVerificationOrder($azureGroupMember, 'id')) {
$newGroupMembers[] = $userId;
}
}
- $usergroup->subscribe_users_to_usergroup($groupId, $newGroupMembers);
+ if ($newGroupMembers) {
+ $usergroup->subscribe_users_to_usergroup($groupId, $newGroupMembers);
- yield sprintf(
- 'User IDs subscribed in class %s: %s',
- $azureGroupInfo['displayName'],
- implode(', ', $newGroupMembers)
- );
+ yield sprintf(
+ 'User IDs subscribed in class (ID %d): %s',
+ $groupId,
+ implode(', ', $newGroupMembers)
+ );
+ }
}
}
@@ -62,7 +73,7 @@ class AzureSyncUsergroupsCommand extends AzureCommand
*
* @return Generator>
*/
- private function getAzureGroups(AccessTokenInterface $token): Generator
+ private function getAzureGroups(): Generator
{
$groupFields = [
'id',
@@ -76,8 +87,10 @@ class AzureSyncUsergroupsCommand extends AzureCommand
implode(',', $groupFields)
);
+ $token = null;
+
do {
- $token = $this->getToken($token);
+ $this->generateOrRefreshToken($token);
try {
$azureGroupsRequest = $this->provider->request('get', "groups?$query", $token);
@@ -99,49 +112,4 @@ class AzureSyncUsergroupsCommand extends AzureCommand
}
} while ($hasNextLink);
}
-
- /**
- * @throws Exception
- *
- * @return Generator>
- */
- private function getAzureGroupMembers(AccessTokenInterface $token, string $groupObjectId): Generator
- {
- $userFields = [
- 'mail',
- 'mailNickname',
- 'id',
- ];
- $query = sprintf(
- '$top=%d&$select=%s',
- AzureActiveDirectory::API_PAGE_SIZE,
- implode(',', $userFields)
- );
- $hasNextLink = false;
-
- do {
- $token = $this->getToken($token);
-
- try {
- $azureGroupMembersRequest = $this->provider->request(
- 'get',
- "groups/$groupObjectId/members?$query",
- $token
- );
- } catch (Exception $e) {
- throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage());
- }
-
- $azureGroupMembers = $azureGroupMembersRequest['value'] ?? [];
-
- foreach ($azureGroupMembers as $azureGroupMember) {
- yield $azureGroupMember;
- }
-
- if (!empty($azureGroupMembersRequest['@odata.nextLink'])) {
- $hasNextLink = true;
- $query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY);
- }
- } while ($hasNextLink);
- }
}
diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
index fea4f77af1..97bf5f1bb4 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
@@ -143,49 +143,4 @@ class AzureSyncUsersCommand extends AzureCommand
}
} while ($hasNextLink);
}
-
- /**
- * @throws Exception
- */
- public function getAzureGroupMembers(string $groupUid): Generator
- {
- $userFields = [
- 'id',
- ];
-
- $query = sprintf(
- '$top=%d&$select=%s',
- AzureActiveDirectory::API_PAGE_SIZE,
- implode(',', $userFields)
- );
-
- $token = null;
-
- do {
- $this->generateOrRefreshToken($token);
-
- try {
- $azureGroupMembersRequest = $this->provider->request(
- 'get',
- "groups/$groupUid/members?$query",
- $token
- );
- } catch (Exception $e) {
- throw new Exception('Exception when requesting group members from Azure: '.$e->getMessage());
- }
-
- $azureGroupMembers = $azureGroupMembersRequest['value'] ?? [];
-
- foreach ($azureGroupMembers as $azureGroupMember) {
- yield $azureGroupMember;
- }
-
- $hasNextLink = false;
-
- if (!empty($azureGroupMembersRequest['@odata.nextLink'])) {
- $hasNextLink = true;
- $query = parse_url($azureGroupMembersRequest['@odata.nextLink'], PHP_URL_QUERY);
- }
- } while ($hasNextLink);
- }
}
From 274f9f28cd6deb461c8da12a0ce746b9ebdefb6a Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Fri, 13 Sep 2024 16:53:13 -0500
Subject: [PATCH 19/22] Plugin: Azure: Move methods to parent class - refs
BT#21930
---
.../src/AzureCommand.php | 100 ++++++++++++++++++
.../src/AzureSyncUsergroupsCommand.php | 45 --------
.../src/AzureSyncUsersCommand.php | 55 ----------
3 files changed, 100 insertions(+), 100 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
index 6a14c94e07..1c0ccd3e7b 100644
--- a/plugin/azure_active_directory/src/AzureCommand.php
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -37,6 +37,106 @@ abstract class AzureCommand
}
}
+ /**
+ * @throws Exception
+ *
+ * @return Generator>
+ */
+ protected function getAzureUsers(): Generator
+ {
+ $userFields = [
+ 'givenName',
+ 'surname',
+ 'mail',
+ 'userPrincipalName',
+ 'businessPhones',
+ 'mobilePhone',
+ 'accountEnabled',
+ 'mailNickname',
+ 'id',
+ ];
+
+ $query = sprintf(
+ '$top=%d&$select=%s',
+ AzureActiveDirectory::API_PAGE_SIZE,
+ implode(',', $userFields)
+ );
+
+ $token = null;
+
+ do {
+ $this->generateOrRefreshToken($token);
+
+ try {
+ $azureUsersRequest = $this->provider->request(
+ 'get',
+ "users?$query",
+ $token
+ );
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting users from Azure: '.$e->getMessage());
+ }
+
+ $azureUsersInfo = $azureUsersRequest['value'] ?? [];
+
+ foreach ($azureUsersInfo as $azureUserInfo) {
+ yield $azureUserInfo;
+ }
+
+ $hasNextLink = false;
+
+ if (!empty($azureUsersRequest['@odata.nextLink'])) {
+ $hasNextLink = true;
+ $query = parse_url($azureUsersRequest['@odata.nextLink'], PHP_URL_QUERY);
+ }
+ } while ($hasNextLink);
+ }
+
+ /**
+ * @throws Exception
+ *
+ * @return Generator>
+ */
+ protected function getAzureGroups(): Generator
+ {
+ $groupFields = [
+ 'id',
+ 'displayName',
+ 'description',
+ ];
+
+ $query = sprintf(
+ '$top=%d&$select=%s',
+ AzureActiveDirectory::API_PAGE_SIZE,
+ implode(',', $groupFields)
+ );
+
+ $token = null;
+
+ do {
+ $this->generateOrRefreshToken($token);
+
+ try {
+ $azureGroupsRequest = $this->provider->request('get', "groups?$query", $token);
+ } catch (Exception $e) {
+ throw new Exception('Exception when requesting groups from Azure: '.$e->getMessage());
+ }
+
+ $azureGroupsInfo = $azureGroupsRequest['value'] ?? [];
+
+ foreach ($azureGroupsInfo as $azureGroupInfo) {
+ yield $azureGroupInfo;
+ }
+
+ $hasNextLink = false;
+
+ if (!empty($azureGroupsRequest['@odata.nextLink'])) {
+ $hasNextLink = true;
+ $query = parse_url($azureGroupsRequest['@odata.nextLink'], PHP_URL_QUERY);
+ }
+ } while ($hasNextLink);
+ }
+
/**
* @return Generator>
*
diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
index 2fe7c0508f..3f51db2736 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
@@ -67,49 +67,4 @@ class AzureSyncUsergroupsCommand extends AzureCommand
}
}
}
-
- /**
- * @throws Exception
- *
- * @return Generator>
- */
- private function getAzureGroups(): Generator
- {
- $groupFields = [
- 'id',
- 'displayName',
- 'description',
- ];
-
- $query = sprintf(
- '$top=%d&$select=%s',
- AzureActiveDirectory::API_PAGE_SIZE,
- implode(',', $groupFields)
- );
-
- $token = null;
-
- do {
- $this->generateOrRefreshToken($token);
-
- try {
- $azureGroupsRequest = $this->provider->request('get', "groups?$query", $token);
- } catch (Exception $e) {
- throw new Exception('Exception when requesting groups from Azure: '.$e->getMessage());
- }
-
- $azureGroupsInfo = $azureGroupsRequest['value'] ?? [];
-
- foreach ($azureGroupsInfo as $azureGroupInfo) {
- yield $azureGroupInfo;
- }
-
- $hasNextLink = false;
-
- if (!empty($azureGroupsRequest['@odata.nextLink'])) {
- $hasNextLink = true;
- $query = parse_url($azureGroupsRequest['@odata.nextLink'], PHP_URL_QUERY);
- }
- } while ($hasNextLink);
- }
}
diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
index 97bf5f1bb4..748204680b 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
@@ -88,59 +88,4 @@ class AzureSyncUsersCommand extends AzureCommand
);
}
}
-
- /**
- * @throws Exception
- *
- * @return Generator>
- */
- private function getAzureUsers(): Generator
- {
- $userFields = [
- 'givenName',
- 'surname',
- 'mail',
- 'userPrincipalName',
- 'businessPhones',
- 'mobilePhone',
- 'accountEnabled',
- 'mailNickname',
- 'id',
- ];
-
- $query = sprintf(
- '$top=%d&$select=%s',
- AzureActiveDirectory::API_PAGE_SIZE,
- implode(',', $userFields)
- );
-
- $token = null;
-
- do {
- $this->generateOrRefreshToken($token);
-
- try {
- $azureUsersRequest = $this->provider->request(
- 'get',
- "users?$query",
- $token
- );
- } catch (Exception $e) {
- throw new Exception('Exception when requesting users from Azure: '.$e->getMessage());
- }
-
- $azureUsersInfo = $azureUsersRequest['value'] ?? [];
-
- foreach ($azureUsersInfo as $azureUserInfo) {
- yield $azureUserInfo;
- }
-
- $hasNextLink = false;
-
- if (!empty($azureUsersRequest['@odata.nextLink'])) {
- $hasNextLink = true;
- $query = parse_url($azureUsersRequest['@odata.nextLink'], PHP_URL_QUERY);
- }
- } while ($hasNextLink);
- }
}
From 2b5eeb58ad79c954afa8d3da2046e454670e7802 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Fri, 13 Sep 2024 17:10:48 -0500
Subject: [PATCH 20/22] Minor: Format code - refs BT#21930
---
.../src/AzureActiveDirectory.php | 76 +++++++++----------
.../src/AzureCommand.php | 4 +-
.../src/AzureSyncUsergroupsCommand.php | 2 -
3 files changed, 40 insertions(+), 42 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureActiveDirectory.php b/plugin/azure_active_directory/src/AzureActiveDirectory.php
index d0f6e466ce..fce9cf9b68 100644
--- a/plugin/azure_active_directory/src/AzureActiveDirectory.php
+++ b/plugin/azure_active_directory/src/AzureActiveDirectory.php
@@ -306,6 +306,44 @@ class AzureActiveDirectory extends Plugin
return $userId;
}
+ /**
+ * @return array
+ */
+ public function getGroupUidByRole(): array
+ {
+ $groupUidList = [
+ 'admin' => $this->get(self::SETTING_GROUP_ID_ADMIN),
+ 'sessionAdmin' => $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN),
+ 'teacher' => $this->get(self::SETTING_GROUP_ID_TEACHER),
+ ];
+
+ return array_filter($groupUidList);
+ }
+
+ /**
+ * @return array
+ */
+ public function getUpdateActionByRole(): array
+ {
+ return [
+ 'admin' => function (User $user) {
+ $user->setStatus(COURSEMANAGER);
+
+ UserManager::addUserAsAdmin($user, false);
+ },
+ 'sessionAdmin' => function (User $user) {
+ $user->setStatus(SESSIONADMIN);
+
+ UserManager::removeUserAdmin($user, false);
+ },
+ 'teacher' => function (User $user) {
+ $user->setStatus(COURSEMANAGER);
+
+ UserManager::removeUserAdmin($user, false);
+ },
+ ];
+ }
+
/**
* @throws Exception
*/
@@ -347,42 +385,4 @@ class AzureActiveDirectory extends Plugin
$extra,
];
}
-
- /**
- * @return array
- */
- public function getGroupUidByRole(): array
- {
- $groupUidList = [
- 'admin' => $this->get(self::SETTING_GROUP_ID_ADMIN),
- 'sessionAdmin' => $this->get(self::SETTING_GROUP_ID_SESSION_ADMIN),
- 'teacher' => $this->get(self::SETTING_GROUP_ID_TEACHER),
- ];
-
- return array_filter($groupUidList);
- }
-
- /**
- * @return array
- */
- public function getUpdateActionByRole(): array
- {
- return [
- 'admin' => function (User $user) {
- $user->setStatus(COURSEMANAGER);
-
- UserManager::addUserAsAdmin($user, false);
- },
- 'sessionAdmin' => function (User $user) {
- $user->setStatus(SESSIONADMIN);
-
- UserManager::removeUserAdmin($user, false);
- },
- 'teacher' => function (User $user) {
- $user->setStatus(COURSEMANAGER);
-
- UserManager::removeUserAdmin($user, false);
- },
- ];
- }
}
diff --git a/plugin/azure_active_directory/src/AzureCommand.php b/plugin/azure_active_directory/src/AzureCommand.php
index 1c0ccd3e7b..ce79c45ca2 100644
--- a/plugin/azure_active_directory/src/AzureCommand.php
+++ b/plugin/azure_active_directory/src/AzureCommand.php
@@ -138,9 +138,9 @@ abstract class AzureCommand
}
/**
- * @return Generator>
- *
* @throws Exception
+ *
+ * @return Generator>
*/
protected function getAzureGroupMembers(string $groupUid): Generator
{
diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
index 3f51db2736..f64975cd25 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
@@ -2,8 +2,6 @@
/* For license terms, see /license.txt */
-use League\OAuth2\Client\Token\AccessTokenInterface;
-
class AzureSyncUsergroupsCommand extends AzureCommand
{
/**
From f5d563c36143d792a50e0940b6291ff5cbee54a0 Mon Sep 17 00:00:00 2001
From: Angel Fernando Quiroz Campos <1697880+AngelFQC@users.noreply.github.com>
Date: Tue, 17 Sep 2024 13:17:14 -0500
Subject: [PATCH 21/22] Plugin: Azure: Catch exception when getting group
members - refs BT#21930
---
.../src/AzureSyncUsergroupsCommand.php | 12 +++++++++---
.../src/AzureSyncUsersCommand.php | 9 ++++++++-
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
index f64975cd25..30087a16e6 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsergroupsCommand.php
@@ -48,10 +48,16 @@ class AzureSyncUsergroupsCommand extends AzureCommand
yield sprintf('Obtaining members for group (ID %d)', $groupId);
- foreach ($this->getAzureGroupMembers($azureGroupUid) as $azureGroupMember) {
- if ($userId = $this->plugin->getUserIdByVerificationOrder($azureGroupMember, 'id')) {
- $newGroupMembers[] = $userId;
+ try {
+ foreach ($this->getAzureGroupMembers($azureGroupUid) as $azureGroupMember) {
+ if ($userId = $this->plugin->getUserIdByVerificationOrder($azureGroupMember, 'id')) {
+ $newGroupMembers[] = $userId;
+ }
}
+ } catch (Exception $e) {
+ yield $e->getMessage();
+
+ continue;
}
if ($newGroupMembers) {
diff --git a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
index 748204680b..36b60f9a2f 100644
--- a/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
+++ b/plugin/azure_active_directory/src/AzureSyncUsersCommand.php
@@ -42,7 +42,14 @@ class AzureSyncUsersCommand extends AzureCommand
$em = Database::getManager();
foreach ($roleGroups as $userRole => $groupUid) {
- $azureGroupMembersInfo = iterator_to_array($this->getAzureGroupMembers($groupUid));
+ try {
+ $azureGroupMembersInfo = iterator_to_array($this->getAzureGroupMembers($groupUid));
+ } catch (Exception $e) {
+ yield $e->getMessage();
+
+ continue;
+ }
+
$azureGroupMembersUids = array_column($azureGroupMembersInfo, 'id');
foreach ($azureGroupMembersUids as $azureGroupMembersUid) {
From df28327bba235204f9012ec9f35f88e2a5ace0e3 Mon Sep 17 00:00:00 2001
From: Nicolas Ducoulombier
Date: Mon, 23 Sep 2024 14:56:39 +0200
Subject: [PATCH 22/22] Plugin: Azure: adapt order for role verification to
have first admin, then teacher to avoid setting a teacher role to an admin -
refs BT#21500
---
plugin/azure_active_directory/src/callback.php | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/plugin/azure_active_directory/src/callback.php b/plugin/azure_active_directory/src/callback.php
index 6e59c9c812..3bc85d443c 100644
--- a/plugin/azure_active_directory/src/callback.php
+++ b/plugin/azure_active_directory/src/callback.php
@@ -97,10 +97,9 @@ try {
$azureGroups = $provider->get('me/memberOf', $token);
- foreach ($azureGroups as $azureGroup) {
- $azureGroupUid = $azureGroup['objectId'];
-
- foreach ($roleGroups as $userRole => $groupUid) {
+ foreach ($roleGroups as $userRole => $groupUid) {
+ foreach ($azureGroups as $azureGroup) {
+ $azureGroupUid = $azureGroup['objectId'];
if ($azureGroupUid === $groupUid) {
$roleActions[$userRole]($user);