Chamilo is a learning management system focused on ease of use and accessibility
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
chamilo-lms/main/admin/init_cas_user_extra_field_f...

325 lines
12 KiB

<?php
/* For licensing terms, see /license.txt */
/*
User account CASification interactive script
to move user accounts from LDAP authentication to CAS authentication
Creates the "cas_user" extra field if missing, then for each registred user:
- copies over the right CAS identifier to the "cas_user" extra field
- ensures the "username" is spelled right
- updates the "auth_source".
This script should be run from a terminal.
It does not read any parameter from the command line, but uses the global configuration arrays
$extldap_config
and
$extldap_user_correspondance
defined in app/config/auth.conf.php.
The username is used to search the LDAP directory, in both attributes
$extldap_user_correspondance['username']
and
$extldap_user_correspondance['extra']['cas_user'].
Any user account with no match or more than one matches in the LDAP directory is skipped.
All the corrections are only applied in phase 2, and take time.
Phase 1 only builds a TO-DO list.
Phase 2 starts with the script asking the operator confirmation for each modification category:
- fix usernames
- add missing CAS identifiers
- fix wrong CAS identifiers
- fix auth source
Planned modifications and progress are displayed.
Diagnostics and modifications can be saved using command script(1).
This script does not need to be run more than once,
but can be run several times.
In case phase 2 is stopped before the end, one should run this script again.
If this script is run after all user accounts were CASified, it just stops after Phase 1.
This can be used to check whether no work is left to do.
*/
if (php_sapi_name() !== 'cli') {
exit("this script is supposed to be run from the command-line\n");
}
require __DIR__.'/../../cli-config.php';
require_once __DIR__.'/../../app/config/auth.conf.php';
require_once __DIR__.'/../../main/inc/lib/api.lib.php';
require_once __DIR__.'/../../main/inc/lib/database.constants.inc.php';
require_once __DIR__.'/../../main/inc/lib/internationalization.lib.php';
require_once __DIR__.'/../../main/inc/lib/text.lib.php';
// Bind to LDAP server
$ldap = false;
foreach ($extldap_config['host'] as $ldapHost) {
$ldap = array_key_exists('port', $extldap_config)
? ldap_connect($ldapHost, $extldap_config['port'])
: ldap_connect($ldapHost);
if (false !== $ldap) {
break;
}
}
if (false === $ldap) {
exit("ldap_connect() failed\n");
}
echo "Connected to LDAP server $ldapHost.\n";
ldap_set_option(
$ldap,
LDAP_OPT_PROTOCOL_VERSION,
array_key_exists('protocol_version', $extldap_config) ? $extldap_config['protocol_version'] : 2
);
ldap_set_option(
$ldap,
LDAP_OPT_REFERRALS,
array_key_exists('referrals', $extldap_config) ? $extldap_config['referrals'] : false
);
ldap_bind($ldap, $extldap_config['admin_dn'], $extldap_config['admin_password'])
or exit('ldap_bind() failed: '.ldap_error($ldap)."\n");
echo "Bound to LDAP server as ${extldap_config['admin_dn']}.\n";
// set a few variables for LDAP search
$baseDn = $extldap_config['base_dn']
or exit("cannot read the LDAP directory base DN where to search for user entries\n");
echo "Base DN is '$baseDn'.\n";
$ldapCASUserAttribute = $extldap_user_correspondance['extra']['cas_user']
or exit("cannot read the name of the LDAP attribute where to find the CAS user code\n");
echo "LDAP CAS user code attribute is '$ldapCASUserAttribute'.\n";
$ldapUsernameAttribute = $extldap_user_correspondance['username']
or exit("cannot read the name of the LDAP attribute where to find the username\n");
echo "LDAP username attribute is '$ldapUsernameAttribute'.\n";
$filters = [
"$ldapCASUserAttribute=*",
"$ldapUsernameAttribute=*",
];
if (array_key_exists('filter', $extldap_config)) {
$filters[] = $extldap_config['filter'];
}
// read 'cas_user' extra field id from internal database
$extraField = new ExtraField('user');
$extraFieldData = $extraField->get_handler_field_info_by_field_variable('cas_user');
if (empty($extraFieldData)) {
if ('y' === readline("Create missing 'cas_user' extra field ? (type 'y' to confirm) ")) {
$fieldId = $extraField->save(
[
'variable' => 'cas_user',
'field_type' => ExtraField::FIELD_TYPE_TEXT,
'visible_to_self' => true,
'filter' => true,
'display_text' => get_lang('CAS User Identifier'),
]
);
if (false === $fieldId) {
exit("failed to create extra field\n");
}
} else {
exit("Required extra field is missing\n");
}
} else {
$fieldId = $extraFieldData['id'];
}
echo "'cas_user' extra field id is $fieldId.\n";
// read cas_user extra field existing values as an associative array ( user id => CAS code )
$extraFieldValueModel = new ExtraFieldValue('user');
$recordList = $extraFieldValueModel->getValuesByFieldId($fieldId);
$existingCasUserValues = [];
if (false !== $recordList) {
foreach ($recordList as $value) {
$existingCasUserValues[$value['item_id']] = $value['value'];
}
}
echo count($existingCasUserValues)." users have their cas_user value set already.\n";
// read all users from the internal database and check their LDAP CAS code to build a to-do list
$userRepository = Database::getManager()->getRepository('ChamiloUserBundle:User');
$databaseUsers = $userRepository->findAll();
$count = count($databaseUsers);
echo "$count users are registered in the internal database.\n";
$userNamesInUse = [];
foreach ($databaseUsers as $user) {
$userNamesInUse[$user->getUsername()] = $user->getId();
}
$missingCASCodes = [];
$wrongCASCodes = [];
$wrongUserNames = [];
$wrongAuthSources = [];
$checked = 0;
foreach ($databaseUsers as $user) {
$username = $user->getUsername();
echo "Checked $checked / $count users - now checking '$username'…\r";
$filter = '(&('
.join(
')(',
array_merge($filters, ["|($ldapUsernameAttribute=$username)($ldapCASUserAttribute=$username)"])
)
.'))';
$searchResult = ldap_search($ldap, $baseDn, $filter, [$ldapCASUserAttribute, $ldapUsernameAttribute]);
if (false === $searchResult) {
exit('ldap_search() failed: '.ldap_error($ldap)."\n");
}
$userId = $user->getId();
echo "$username ($userId): ";
switch (ldap_count_entries($ldap, $searchResult)) {
case 0:
print "does not exist in the LDAP directory, skipping.\n";
break;
case 1:
$entry = ldap_first_entry($ldap, $searchResult);
if (false === $entry) {
exit('ldap_first_entry() failed: '.ldap_error($ldap)."\n");
}
$ldapCASUser = ldap_get_values($ldap, $entry, $ldapCASUserAttribute)[0];
if (false === $ldapCASUser) {
exit('cannot read CAS user code from LDAP entry: '.ldap_error($ldap)."\n");
}
$ldapUsername = ldap_get_values($ldap, $entry, $ldapUsernameAttribute)[0];
if (false === $ldapUsername) {
exit('cannot read username from LDAP entry: '.ldap_error($ldap)."\n");
}
echo "\033[2K\r$ldapUsernameAttribute: $ldapUsername, $ldapCASUserAttribute: $ldapCASUser, ";
$problems = [];
if ($username === $ldapUsername) {
true; // fine
} elseif (in_array(
strtolower(trim($username)),
[strtolower(trim($ldapUsername)), strtolower(trim($ldapCASUser))]
)) {
if (array_key_exists($ldapUsername, $userNamesInUse)) {
echo "wrong username but '$ldapUsername' is already taken, skipping.\n";
break;
} else {
$problems[] = "wrong username";
$wrongUserNames[$userId] = $ldapUsername;
$userNamesInUse[$ldapUsername] = $userId;
}
} else {
exit("LDAP search result does not match username; our filter is wrong: $filter\n");
}
if (array_key_exists($userId, $existingCasUserValues)) {
$currentValue = $existingCasUserValues[$userId];
if ($currentValue !== $ldapCASUser) {
$problems[] = "wrong current CAS user code '$currentValue'";
$wrongCASCodes[$userId] = $ldapCASUser;
}
} else {
$problems[] = "CAS user code missing in database";
$missingCASCodes[$userId] = $ldapCASUser;
}
$currentAuthSource = $user->getAuthSource();
if (CAS_AUTH_SOURCE !== $currentAuthSource) {
$problems[] = "wrong auth source '$currentAuthSource'";
$wrongAuthSources[$userId] = true;
}
echo empty($problems) ? "ok\r" : (join(', ', $problems)."\n");
break;
default:
print "more than 1 entries for username '$username' in the LDAP directory for user id=$userId, skipping.\n";
}
$checked++;
}
echo "\033[2K\r";
// ask for confirmation and write changes to the database
$fixUsernames = (
!empty($wrongUserNames)
&&
('y' === readline("Fix wrong user names for ".count($wrongUserNames)." users ? (type 'y' to confirm) "))
);
if ($fixUsernames) {
echo "I will fix user names.\n";
}
$fixMissingCASCodes = (
!empty($missingCASCodes)
&&
('y' === readline("Fix missing CAS codes for ".count($missingCASCodes)." users ? (type 'y' to confirm) "))
);
if ($fixMissingCASCodes) {
echo "I will fix missing CAS codes.\n";
}
$fixWrongCASCodes = (
!empty($wrongCASCodes)
&&
('y' === readline("Fix wrong CAS codes for ".count($wrongCASCodes)." users ? (type 'y' to confirm) "))
);
if ($fixWrongCASCodes) {
echo "I will fix wrong CAS codes.\n";
}
$fixWrongAuthSources = (
!empty($wrongAuthSources)
&&
('y' === readline("Fix auth source for ".count($wrongAuthSources)." users ? (type 'y' to confirm) "))
);
if ($fixWrongAuthSources) {
echo "I will fix wrong authentication sources.\n";
}
if ($fixUsernames || $fixWrongAuthSources || $fixWrongCASCodes || $fixMissingCASCodes) {
$usersToFix = [];
foreach ($databaseUsers as $user) {
$userId = $user->getId();
if ($fixUsernames && array_key_exists($userId, $wrongUserNames)
|| $fixWrongAuthSources && array_key_exists($userId, $wrongAuthSources)
|| $fixMissingCASCodes && array_key_exists($userId, $missingCASCodes)
|| $fixWrongCASCodes && array_key_exists($userId, $wrongCASCodes)
) {
$usersToFix[] = $user;
}
}
$fixCount = count($usersToFix);
echo "Now fixing $fixCount out of $count database users…\n";
$done = 0;
foreach ($usersToFix as $user) {
$userId = $user->getId();
$dirty = false;
if ($fixUsernames && array_key_exists($userId, $wrongUserNames)) {
$user->setUsername($wrongUserNames[$userId]);
$dirty = true;
}
if ($fixWrongAuthSources && array_key_exists($userId, $wrongAuthSources)) {
$user->setAuthSource(CAS_AUTH_SOURCE);
$dirty = true;
}
if ($dirty) {
try {
UserManager::getManager()->save($user);
} catch (Exception $exception) {
echo $exception->getMessage()."\n";
exit("Script stopped before the end.\n");
}
}
if ($fixMissingCASCodes && array_key_exists($userId, $missingCASCodes)) {
UserManager::update_extra_field_value($userId, 'cas_user', $missingCASCodes[$userId]);
} elseif ($fixWrongCASCodes && array_key_exists($userId, $wrongCASCodes)) {
UserManager::update_extra_field_value($userId, 'cas_user', $wrongCASCodes[$userId]);
}
$done++;
echo "Fixed $done / $fixCount users\r";
}
echo "\n";
}
echo "End of script.\n";