Actualización

This commit is contained in:
Xes
2025-04-10 12:49:05 +02:00
parent 4aff98e77b
commit 1cdd00920f
9151 changed files with 1800913 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
<?php
/* This file contains all the configuration variable for the cas module
* In the future, these will be in the database
*/
if (api_is_cas_activated()) {
require_once __DIR__.'/../../../vendor/apereo/phpcas/source/CAS.php';
// Get the $cas array from app/config/auth.conf.php
global $cas;
if (is_array($cas) && array_key_exists('debug', $cas) && !empty($cas['debug'])) {
phpCAS::setDebug($cas['debug']);
}
if (is_array($cas) && array_key_exists('verbose', $cas) && $cas['verbose']) {
phpCAS::setVerbose(true);
}
if (!phpCAS::isInitialized()) {
switch (api_get_setting('cas_protocol')) {
case 'CAS1':
$version = CAS_VERSION_1_0;
break;
case 'CAS3':
$version = CAS_VERSION_3_0;
break;
case 'SAML':
$version = SAML_VERSION_1_1;
break;
case 'CAS2':
default:
$version = CAS_VERSION_2_0;
}
$port = api_get_setting('cas_port');
if (is_null($port)) {
$port = 443;
} else {
$port = intval($port) ?: 443;
}
$uri = api_get_setting('cas_server_uri') ?: '';
$hostname = api_get_setting('cas_server') ?: 'localhost';
$serviceBaseUrl = '';
if (is_array($cas)) {
if (array_key_exists('service_base_url', $cas)) {
$serviceBaseUrl = $cas['service_base_url'];
}
}
phpCAS::client($version, $hostname, $port, $uri, $serviceBaseUrl);
if (is_array($cas) && array_key_exists('noCasServerValidation', $cas) && $cas['noCasServerValidation']) {
phpCAS::setNoCasServerValidation();
}
if (is_array($cas)) {
if (array_key_exists('fixedServiceURL', $cas)) {
$fixedServiceURL = $cas['fixedServiceURL'];
if (is_string($fixedServiceURL)) {
phpCAS::setFixedServiceURL($fixedServiceURL);
} elseif (is_bool($fixedServiceURL) && $fixedServiceURL) {
phpCAS::setFixedServiceURL(api_get_configuration_value('root_web'));
}
}
if (isset($cas['saml_validate_url'])) {
phpCAS::setServerSamlValidateURL($cas['saml_validate_url']);
}
}
}
}

View File

@@ -0,0 +1,51 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../../inc/global.inc.php';
$url = api_get_path(WEB_PATH).'main/auth/conditional_login/complete_phone_number.php';
if (!isset($_SESSION['conditional_login']['uid'])) {
exit("Not Authorised");
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="fr" xml:lang="fr" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
</head>
<body>
<form id="data_completion" name="data_completion" method="post" action="<?php echo $url; ?>">
Téléphone : <input type="text" name="phone_number" />
<input type="submit" name="submit" value="Submit" />
</form>
</body>
</html>
<?php
if (isset($_POST['submit'])) {
$u = api_get_user_info($_SESSION['conditional_login']['uid']);
$u['phone'] = $_POST['phone_number'];
$password = null; // we don't want to change the password
$updated = UserManager::update_user(
$u['user_id'],
$u['firstname'],
$u['lastname'],
$u['username'],
$password,
$u['auth_source'],
$u['email'],
$u['status'],
$u['official_code'],
$u['phone'],
$u['picture_uri'],
$u['expiration_date'],
$u['active'],
$u['creator_id'],
$u['hr_dept_id'],
$u['extra'],
$u['language'],
''
);
if ($updated) {
ConditionalLogin::login();
}
}

View File

@@ -0,0 +1,81 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/*
This script is included by local.inc.php to redirect users to some url if some conditions are satisfied.
* Please populate the $login_conditions array with a conditional function and an url.
* If the conditional function returns true the user will be redirected to URL at login.
* This array must be filled for this module to work.
* This is an example asking the user to enter his phone number if it is empty.
* Note you can enter more than one condition in the array. They will be checked in the array order.
*/
/**
* Please implements the functions of the $login_conditions array.
* Each of these function will take a user array
* (user_id, username, password (crypted), auth_source, active, expiration_date).
*/
$login_conditions = [];
$conditionalLoginHook = HookConditionalLogin::create();
if (!empty($conditionalLoginHook)) {
foreach ($conditionalLoginHook->notifyConditionalLogin() as $condition) {
$login_conditions[] = $condition;
}
}
// "Terms and conditions" condition
array_push(
$login_conditions,
[
'conditional_function' => 'check_platform_legal_conditions',
'url' => api_get_path(WEB_CODE_PATH).'auth/inscription.php',
]
);
//array_push($login_conditions, array(
// 'conditional_function' => 'dc_check_phone_number',
// 'url' => api_get_path(WEB_PATH).'main/auth/conditional_login/complete_phone_number.php'
//));
function dc_check_phone_number($user)
{
$uInfo = api_get_user_info($user['user_id']);
if (empty($uInfo['phone'])) {
return false;
}
return true;
}
/**
* Checks if the user accepted or not the legal conditions.
*
* @param array $user
*
* @return bool true if user pass, false otherwise
*/
function check_platform_legal_conditions($user)
{
if (api_get_setting('allow_terms_conditions') === 'true' &&
api_get_setting('load_term_conditions_section') === 'login'
) {
$termAndConditionStatus = api_check_term_condition($user['user_id']);
// @todo not sure why we need the login password and update_term_status
if ($termAndConditionStatus === false) {
Session::write('term_and_condition', ['user_id' => $user['user_id']]);
return false;
} else {
Session::erase('term_and_condition');
return true;
}
} else {
// No validation user can pass
return true;
}
}

242
main/auth/courses.php Normal file
View File

@@ -0,0 +1,242 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\SequenceResource;
// Delete the globals['_cid'], we don't need it here.
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
// Section for the tabs.
$this_section = SECTION_CATALOG;
if ('true' !== api_get_setting('course_catalog_published')) {
// Access rights: anonymous users can't do anything useful here.
api_block_anonymous_users();
}
$userCanViewPage = CoursesAndSessionsCatalog::userCanView();
$defaultAction = CoursesAndSessionsCatalog::is(CATALOG_SESSIONS) ? 'display_sessions' : 'display_courses';
$action = isset($_REQUEST['action']) ? Security::remove_XSS($_REQUEST['action']) : $defaultAction;
$categoryCode = isset($_REQUEST['category_code']) ? Security::remove_XSS($_REQUEST['category_code']) : '';
$searchTerm = isset($_REQUEST['search_term']) ? Security::remove_XSS($_REQUEST['search_term']) : '';
$nameTools = CourseCategory::getCourseCatalogNameTools($action);
if (empty($nameTools)) {
$nameTools = get_lang('CourseManagement');
} else {
if (!in_array(
$action,
['display_random_courses', 'display_courses', 'subscribe']
)) {
$interbreadcrumb[] = [
'url' => api_get_path(WEB_CODE_PATH).'auth/courses.php',
'name' => get_lang('CourseManagement'),
];
}
$interbreadcrumb[] = ['url' => '#', 'name' => $nameTools];
}
switch ($action) {
case 'unsubscribe':
// We are unsubscribing from a course (=Unsubscribe from course).
$ctok = Security::get_existing_token();
if (!empty($_GET['sec_token']) && $ctok == $_GET['sec_token']) {
$auth = new Auth();
$result = $auth->remove_user_from_course($_GET['course_code']);
if ($result) {
Display::addFlash(
Display::return_message(get_lang('YouAreNowUnsubscribed'), 'success')
);
}
}
$currentUrl = api_get_path(WEB_CODE_PATH).'auth/courses.php?category_code='.$categoryCode.'&search_term='.$searchTerm;
header('Location: '.$currentUrl);
exit;
case 'subscribe_course':
$courseCodeToSubscribe = isset($_GET['course_code']) ? Security::remove_XSS($_GET['course_code']) : '';
if (api_is_anonymous()) {
header('Location: '.api_get_path(WEB_CODE_PATH).'auth/inscription.php?c='.$courseCodeToSubscribe);
exit;
}
if (Security::check_token('get')) {
$courseInfo = api_get_course_info($courseCodeToSubscribe);
if (!empty($courseInfo)) {
CourseManager::autoSubscribeToCourse($courseCodeToSubscribe);
$redirectionTarget = CoursesAndSessionsCatalog::generateRedirectUrlAfterSubscription(
$courseInfo['course_public_url']
);
header("Location: $redirectionTarget");
exit;
}
}
Display::addFlash(
Display::return_message(get_lang('NoResults'), 'warning')
);
CoursesAndSessionsCatalog::displayCoursesList('search_course', $searchTerm, $categoryCode);
exit;
break;
case 'subscribe_course_validation':
$toolTitle = get_lang('Subscribe');
$courseCodeToSubscribe = isset($_GET['course_code']) ? Security::remove_XSS($_GET['course_code']) : '';
$courseInfo = api_get_course_info($courseCodeToSubscribe);
if (empty($courseInfo)) {
header('Location: '.api_get_self());
exit;
}
$message = get_lang('CourseRequiresPassword').' ';
$message .= $courseInfo['title'].' ('.$courseInfo['visual_code'].') ';
$action = api_get_self().'?action=subscribe_course_validation&sec_token='.
Security::getTokenFromSession().'&course_code='.$courseInfo['code'];
$form = new FormValidator(
'subscribe_user_with_password',
'post',
$action
);
$form->addHeader($message);
$form->addElement('hidden', 'sec_token', Security::getTokenFromSession());
$form->addElement('hidden', 'subscribe_user_with_password', $courseInfo['code']);
$form->addElement('text', 'course_registration_code');
$form->addButtonSave(get_lang('SubmitRegistrationCode'));
$content = $form->returnForm();
if ($form->validate()) {
if (sha1($_POST['course_registration_code']) === $courseInfo['registration_code']) {
CourseManager::autoSubscribeToCourse($_POST['subscribe_user_with_password']);
$redirectionTarget = CoursesAndSessionsCatalog::generateRedirectUrlAfterSubscription(
$courseInfo['course_public_url']
);
header("Location: $redirectionTarget");
} else {
Display::addFlash(Display::return_message(get_lang('CourseRegistrationCodeIncorrect'), 'warning'));
header('Location: '.$action);
}
exit;
}
$template = new Template($toolTitle, true, true, false, false, false);
$template->assign('content', $content);
$template->display_one_col_template();
break;
case 'subscribe':
if (!$userCanViewPage) {
api_not_allowed(true);
}
header('Location: '.api_get_self());
exit;
case 'display_random_courses':
case 'display_courses':
case 'search_course':
if (!$userCanViewPage) {
api_not_allowed(true);
}
CoursesAndSessionsCatalog::displayCoursesList($action, $searchTerm, $categoryCode);
exit;
case 'display_sessions':
if (!$userCanViewPage) {
api_not_allowed(true);
}
CoursesAndSessionsCatalog::sessionList();
exit;
case 'subscribe_to_session':
if (!$userCanViewPage) {
api_not_allowed(true);
}
$userId = api_get_user_id();
$confirmed = isset($_GET['confirm']);
$sessionId = (int) $_GET['session_id'];
if (empty($userId)) {
api_not_allowed();
exit;
}
if (!$confirmed) {
$template = new Template(null, false, false, false, false, false);
$template->assign('session_id', $sessionId);
$layout = $template->get_template('auth/confirm_session_subscription.tpl');
echo $template->fetch($layout);
exit;
}
$registrationAllowed = api_get_setting('catalog_allow_session_auto_subscription');
if ('true' === $registrationAllowed) {
$entityManager = Database::getManager();
$repository = $entityManager->getRepository('ChamiloCoreBundle:SequenceResource');
$sequences = $repository->getRequirements(
$sessionId,
SequenceResource::SESSION_TYPE
);
if (count($sequences) > 0) {
$requirementsData = $repository->checkRequirementsForUser(
$sequences,
SequenceResource::SESSION_TYPE,
$userId,
$sessionId
);
$continueWithSubscription = $repository->checkSequenceAreCompleted($requirementsData);
if (!$continueWithSubscription) {
header('Location: '.api_get_path(WEB_CODE_PATH).'auth/courses.php');
exit;
}
}
SessionManager::subscribeUsersToSession(
$sessionId,
[$userId],
SESSION_VISIBLE_READ_ONLY,
false
);
$coursesList = SessionManager::get_course_list_by_session_id($sessionId);
$count = count($coursesList);
$url = '';
if ($count <= 0) {
// no course in session -> return to catalog
$url = api_get_path(WEB_CODE_PATH).'auth/courses.php';
} elseif (1 == $count) {
// only one course, so redirect directly to this course
foreach ($coursesList as $course) {
$url = api_get_path(WEB_COURSE_PATH).$course['directory'].'/index.php?id_session='.$sessionId;
}
} else {
$url = api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$sessionId;
}
header('Location: '.$url);
exit;
}
break;
case 'search_tag':
if (!$userCanViewPage) {
api_not_allowed(true);
}
CoursesAndSessionsCatalog::sessionsListByCoursesTag();
exit;
case 'search_session_title':
if (!$userCanViewPage) {
api_not_allowed(true);
}
CoursesAndSessionsCatalog::sessionsListByName();
exit;
}

View File

@@ -0,0 +1,220 @@
<?php
/**
* Licence: GPL
* Please contact CBlue regarding any licences issues.
* Author: noel@cblue.be
* Copyright: CBlue SPRL, 20XX.
*
* External login module : FACEBOOK
*
* This files provides the facebookConnect() and facebook_get_url functions
* Please edit the facebook.conf.php file to adapt it to your fb application parameter
*/
require_once __DIR__.'/../../inc/global.inc.php';
require_once __DIR__.'/facebook.init.php';
require_once __DIR__.'/functions.inc.php';
/**
* This function connect to facebook and retrieves the user info
* If user does not exist in chamilo, it creates it and logs in
* If user already exists, it updates his info.
*/
function facebookConnect()
{
$fb = new \Facebook\Facebook([
'app_id' => $GLOBALS['facebook_config']['appId'],
'app_secret' => $GLOBALS['facebook_config']['secret'],
'default_graph_version' => 'v2.2',
]);
$helper = $fb->getRedirectLoginHelper();
try {
$accessToken = $helper->getAccessToken();
} catch (Facebook\Exceptions\FacebookResponseException $e) {
Display::addFlash(
Display::return_message('Facebook Graph returned an error: '.$e->getMessage(), 'error')
);
header('Location: '.api_get_path(WEB_PATH));
exit;
} catch (Facebook\Exceptions\FacebookSDKException $e) {
Display::addFlash(
Display::return_message('Facebook SDK returned an error: '.$e->getMessage(), 'error')
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
if (!isset($accessToken)) {
if (!$helper->getError()) {
return;
}
if (isset($_GET['loginFailed'])) {
return;
}
$error = implode('<br>', [
'Error: '.$helper->getError(),
'Error Code: '.$helper->getErrorCode(),
'Error Reason: '.$helper->getErrorReason(),
'Error Description: '.$helper->getErrorDescription(),
]);
Display::addFlash(
Display::return_message($error, 'error', false)
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$oAuth2Client = $fb->getOAuth2Client();
$tokenMetadata = $oAuth2Client->debugToken($accessToken);
$tokenMetadata->validateAppId($GLOBALS['facebook_config']['appId']);
$tokenMetadata->validateExpiration();
if (!$accessToken->isLongLived()) {
try {
$accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
} catch (Facebook\Exceptions\FacebookSDKException $e) {
Display::addFlash(
Display::return_message('Error getting long-lived access token: '.$e->getMessage(), 'error')
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
}
try {
$response = $fb->get('/me?fields=id,first_name,last_name,locale,email', $accessToken->getValue());
} catch (Facebook\Exceptions\FacebookResponseException $e) {
Display::addFlash(
Display::return_message('Graph returned an error: '.$e->getMessage(), 'error')
);
header('Location: '.api_get_path(WEB_PATH));
exit;
} catch (Facebook\Exceptions\FacebookSDKException $e) {
Display::addFlash(
Display::return_message('Facebook SDK returned an error: '.$e->getMessage(), 'error')
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$user = $response->getGraphUser();
$language = facebookPluginGetLanguage($user['locale']);
if (!$language) {
$language = 'en_US';
}
$u = [
'firstname' => $user->getFirstName(),
'lastname' => $user->getLastName(),
'status' => STUDENT,
'email' => $user->getEmail(),
'username' => changeToValidChamiloLogin($user->getEmail()),
'language' => $language,
'password' => 'facebook',
'auth_source' => 'facebook',
'extra' => [],
];
$chamiloUinfo = api_get_user_info_from_email($user->getEmail());
$_user['uidReset'] = true;
$_user['language'] = $language;
if ($chamiloUinfo === false) {
// We have to create the user
$chamilo_uid = external_add_user($u);
if ($chamilo_uid === false) {
Display::addFlash(
Display::return_message(get_lang('UserNotRegistered'), 'error')
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$_user['user_id'] = $chamilo_uid;
$_SESSION['_user'] = $_user;
header('Location: '.api_get_path(WEB_PATH));
exit();
}
// User already exists, update info and login
$chamilo_uid = $chamiloUinfo['user_id'];
$u['user_id'] = $chamilo_uid;
external_update_user($u);
$_user['user_id'] = $chamilo_uid;
$_SESSION['_user'] = $_user;
header('Location: '.api_get_path(WEB_PATH));
exit();
}
/**
* Get facebook login url for the platform.
*
* @return string
*/
function facebookGetLoginUrl()
{
$fb = new \Facebook\Facebook([
'app_id' => $GLOBALS['facebook_config']['appId'],
'app_secret' => $GLOBALS['facebook_config']['secret'],
'default_graph_version' => 'v2.2',
]);
$helper = $fb->getRedirectLoginHelper();
$loginUrl = $helper->getLoginUrl(api_get_path(WEB_PATH).'?action=fbconnect', [
'email',
]);
return $loginUrl;
}
/**
* Return a valid Chamilo login
* Chamilo login only use characters lettres, des chiffres et les signes _ . -.
*
* @param $in_txt
*
* @return mixed
*/
function changeToValidChamiloLogin($in_txt)
{
return preg_replace("/[^a-zA-Z1-9_\-.]/", "_", $in_txt);
}
/**
* Get user language.
*
* @param string $language
*
* @return bool
*/
function facebookPluginGetLanguage($language = 'en_US')
{
$language = substr($language, 0, 2);
$sqlResult = Database::query(
"SELECT english_name FROM ".
Database::get_main_table(TABLE_MAIN_LANGUAGE).
" WHERE available = 1 AND isocode = '$language'"
);
if (Database::num_rows($sqlResult)) {
$result = Database::fetch_array($sqlResult);
return $result['english_name'];
}
return false;
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Licence: GPL
* Please contact CBlue regarding any licences issues.
* Author: noel@cblue.be
* Copyright: CBlue SPRL, 20XX.
*
* External login module : FACEBOOK
*
* Configuration file
* Please edit this file to match with your FACEBOOK settings
* */
/**
* Facebook application setting.
* */
//Loads the portal facebook settings
/**
* Facebook application setting
* Loads the portal facebook settings
* See facebook section of the auth.conf.php file.
*/
require __DIR__.'/../../../app/config/auth.conf.php';

View File

@@ -0,0 +1,225 @@
<?php
//TODO : Please implements this function for this module to work.
/**
* Gets user info from external source.
*
* @param string login
* @param string password
*
* @return user array with at least the following fields:
* firstname
* lastname
* status
* email
* login
* password
* or false if no data
* */
function external_get_user_info($login, $password)
{
//Those are the mandatory fields for user creation.
//See external_add_user function for all the fields you can have.
$table = USERINFO_TABLE;
$sql = "SELECT * from $table where username='".Database::escape_string($login)."'";
$result = Database::query($sql);
if (Database::num_rows($result) == 0) { //false password
return false;
}
$user_info = Database::fetch_assoc($result);
// User status
$admin = false;
switch ($user_info['status']) {
case 'admin':
$status = COURSEMANAGER;
$admin = true;
break;
case 'teacher':
$status = COURSEMANAGER;
break;
case 'user':
$status = STUDENT;
break;
default:
$status = STUDENT;
}
// Language
switch ($user_info['language']) {
case 'FR':
$language = 'french';
break;
case 'EN':
$language = 'english';
break;
default:
$language = 'english';
break;
}
//Can Send Message ?
$can_send_message = ($user_info['can_send_message'] == 1) ? 'yes' : 'no';
$u = [
'firstname' => $user_info['firstname'],
'lastname' => $user_info['lastname'],
'status' => $status,
'admin' => $admin,
'email' => $user_info['email'],
'username' => $user_info['username'],
'language' => $language,
'password' => DEFAULT_PASSWORD,
'courses' => $user_info['courses'],
'profile_link' => $user_info['profile_link'],
'worldwide_bu' => $user_info['worlwide_bu'],
'manager' => $user_info['manager'],
'extra' => [
'position_title' => $user_info['position_title'],
'country' => $user_info['country'],
'job_family' => $user_info['job_family'],
'country_bu' => $user_info['country_bu'],
'worldwide_bu' => $user_info['worldwide_bu'],
'profile_link' => $user_info['profile_link'],
'can_send_message' => $can_send_message,
'update_type' => 'external_logininfo', ],
];
return $u; //Please return false if user does not exist
//return false;
}
/**
* Return an array with all user info.
*
* @param associative array with at least thes fields setted :
firstname, lastname, status, email, login, password
* @return mixed new user id - if the new user creation succeeds, false otherwise
* */
function external_add_user($u)
{
//Setting default
if (empty($u['password'])) {
$u['password'] = null;
}
if (empty($u['status'])) {
$u['status'] = 5;
}
if (!isset($u['email'])) {
$u['email'] = '';
}
if (!isset($u['official_code'])) {
$u['official_code'] = '';
}
if (!isset($u['language'])) {
$u['language'] = '';
}
if (!isset($u['phone'])) {
$u['phone'] = '';
}
if (!isset($u['picture_uri'])) {
$u['picture_uri'] = '';
}
if (!isset($u['auth_source'])) {
$u['auth_source'] = PLATFORM_AUTH_SOURCE;
}
if (!isset($u['expiration_date'])) {
$u['expiration_date'] = '';
}
if (!isset($u['active'])) {
$u['active'] = 1;
}
if (!isset($u['hr_dept_id'])) {
$u['hr_dept_id'] = 0;
} //id of responsible HR
if (!isset($u['extra'])) {
$u['extra'] = null;
}
if (!isset($u['encrypt_method'])) {
$u['encrypt_method'] = '';
}
$chamilo_uid = UserManager::create_user(
$u['firstname'],
$u['lastname'],
$u['status'],
$u['email'],
$u['username'],
$u['password'],
$u['official_code'],
$u['language'],
$u['phone'],
$u['picture_uri'],
$u['auth_source'],
$u['expiration_date'],
$u['active'],
$u['hr_dept_id'],
$u['extra'],
$u['encrypt_method']
);
return $chamilo_uid;
}
/**
* Update the user in chamilo database. It upgrade only info that is present in the
* new_user array.
*
* @param $new_user associative array with the value to upgrade
* WARNING user_id key is MANDATORY
* Possible keys are :
* - firstname
* - lastname
* - username
* - auth_source
* - email
* - status
* - official_code
* - phone
* - picture_uri
* - expiration_date
* - active
* - creator_id
* - hr_dept_id
* - extra : array of custom fields
* - language
* - courses : string of all courses code separated by '|'
* - admin : boolean
*
* @return bool|null
*
* @author ndiechburg <noel@cblue.be>
* */
function external_update_user($new_user)
{
$old_user = api_get_user_info($new_user['user_id']);
$u = array_merge($old_user, $new_user);
UserManager::update_user(
$u['user_id'],
$u['firstname'],
$u['lastname'],
$u['username'],
null,
$u['auth_source'],
$u['email'],
$u['status'],
$u['official_code'],
$u['phone'],
$u['picture_uri'],
$u['expiration_date'],
$u['active'],
$u['creator_id'],
$u['hr_dept_id'],
$u['extra'],
$u['language'],
''
);
if (isset($u['courses']) && !empty($u['courses'])) {
$autoSubscribe = explode('|', $u['courses']);
foreach ($autoSubscribe as $code) {
if (CourseManager::course_exists($code)) {
CourseManager::subscribeUser($u['user_id'], $code, STUDENT);
}
}
}
}

View File

@@ -0,0 +1,522 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This files is included by newUser.ldap.php and login.ldap.php
* It implements the functions nedded by both files.
* */
require_once __DIR__.'/../../inc/global.inc.php';
$debug = false;
/**
* Returns a transcoded and trimmed string.
*
* @param string
*
* @return string
*
* @author ndiechburg <noel@cblue.be>
* */
function extldap_purify_string($string)
{
global $extldap_config;
if (isset($extldap_config['encoding'])) {
return trim(api_to_system_encoding($string, $extldap_config['encoding']));
} else {
return trim($string);
}
}
/**
* Establishes a connection to the LDAP server and sets the protocol version.
*
* @return resource|bool ldap link identifier or false
*
* @author ndiechburg <noel@cblue.be>
* */
function extldap_connect()
{
global $extldap_config, $debug;
if (!is_array($extldap_config['host'])) {
$extldap_config['host'] = [$extldap_config['host']];
}
foreach ($extldap_config['host'] as $host) {
//Trying to connect
if (isset($extldap_config['port'])) {
$ds = ldap_connect($host, $extldap_config['port']);
} else {
$ds = ldap_connect($host);
}
if (!$ds) {
$port = isset($extldap_config['port']) ? $extldap_config['port'] : 389;
if ($debug) {
error_log(
'EXTLDAP ERROR : cannot connect to '.$extldap_config['host'].':'.$port
);
}
} else {
break;
}
}
if (!$ds) {
if ($debug) {
error_log('EXTLDAP ERROR : no valid server found');
}
return false;
}
// Setting protocol version
if (isset($extldap_config['protocol_version'])) {
if (!ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $extldap_config['protocol_version'])) {
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 2);
}
}
// Setting protocol version
if (isset($extldap_config['referrals'])) {
if (!ldap_set_option($ds, LDAP_OPT_REFERRALS, $extldap_config['referrals'])) {
ldap_set_option($ds, LDAP_OPT_REFERRALS, $extldap_config['referrals']);
}
}
return $ds;
}
/**
* Authenticate user on external ldap server and return user ldap entry if that succeeds.
*
* @param string $password
*
* @return mixed false if user cannot authenticate on ldap, user ldap entry if tha succeeds
*
* @author ndiechburg <noel@cblue.be>
* Modified by hubert.borderiou@grenet.fr
* Add possibility to get user info from LDAP without check password (if CAS auth and LDAP profil update)
*
* */
function extldap_authenticate($username, $password, $in_auth_with_no_password = false)
{
global $extldap_config, $debug;
if (empty($username) || empty($password)) {
return false;
}
$ds = extldap_connect();
if (!$ds) {
return false;
}
// Connection as admin to search dn of user
if (api_get_configuration_value('ldap_encrypt_admin_password')) {
$ldap_pass = api_decrypt_ldap_password($extldap_config['admin_password']);
} else {
$ldap_pass = $extldap_config['admin_password'];
}
$ldapbind = @ldap_bind($ds, $extldap_config['admin_dn'], $ldap_pass);
if ($ldapbind === false) {
if ($debug) {
error_log(
'EXTLDAP ERROR : cannot connect with admin login/password'
);
}
return false;
}
$user_search = extldap_get_user_search_string($username);
// Search distinguish name of user
$sr = ldap_search($ds, $extldap_config['base_dn'], $user_search);
if (!$sr) {
if ($debug) {
error_log(
'EXTLDAP ERROR : ldap_search('.$ds.', '.$extldap_config['base_dn'].", $user_search) failed"
);
}
return false;
}
$entries_count = ldap_count_entries($ds, $sr);
if ($entries_count > 1) {
if ($debug) {
error_log(
'EXTLDAP ERROR : more than one entry for that user ( ldap_search(ds, '.$extldap_config['base_dn'].", $user_search) )"
);
}
return false;
}
if ($entries_count < 1) {
if ($debug) {
error_log(
'EXTLDAP ERROR : No entry for that user ( ldap_search(ds, '.$extldap_config['base_dn'].", $user_search) )"
);
}
return false;
}
$users = ldap_get_entries($ds, $sr);
$user = $users[0];
// If we just want to have user info from LDAP and not to check password
if ($in_auth_with_no_password) {
return $user;
}
// now we try to autenthicate the user in the ldap
$ubind = @ldap_bind($ds, $user['dn'], $password);
if ($ubind !== false) {
return $user;
} else {
if ($debug) {
error_log('EXTLDAP : Wrong password for '.$user['dn']);
}
return false;
}
}
/**
* Return an array with userinfo compatible with chamilo using $extldap_user_correspondance
* configuration array declared in ldap.conf.php file.
*
* @param array ldap user
* @param array correspondance array (if not set use extldap_user_correspondance declared in auth.conf.php
*
* @return array userinfo array
*
* @author ndiechburg <noel@cblue.be>
* */
function extldap_get_chamilo_user($ldap_user, $cor = null)
{
global $extldap_user_correspondance, $debug;
if (is_null($cor)) {
$cor = $extldap_user_correspondance;
}
$chamilo_user = [];
foreach ($cor as $chamilo_field => $ldap_field) {
if (is_array($ldap_field)) {
$chamilo_user[$chamilo_field] = extldap_get_chamilo_user($ldap_user, $ldap_field);
continue;
}
switch ($ldap_field) {
case 'func':
$func = "extldap_get_$chamilo_field";
if (function_exists($func)) {
$chamilo_user[$chamilo_field] = extldap_purify_string($func($ldap_user));
} else {
if ($debug) {
error_log(
"EXTLDAP WARNING : You forgot to declare $func"
);
}
}
break;
default:
//if string begins with "!", then this is a constant
if ($ldap_field[0] === '!') {
$chamilo_user[$chamilo_field] = trim($ldap_field, "!\t\n\r\0");
break;
}
if (!array_key_exists($ldap_field, $ldap_user)) {
$lowerCaseFieldName = strtolower($ldap_field);
if (array_key_exists($lowerCaseFieldName, $ldap_user)) {
$ldap_field = $lowerCaseFieldName;
}
}
if (isset($ldap_user[$ldap_field][0])) {
$chamilo_user[$chamilo_field] = extldap_purify_string($ldap_user[$ldap_field][0]);
} else {
if ($debug) {
error_log(
'EXTLDAP WARNING : '.$ldap_field.'[0] field is not set in ldap array'
);
}
}
break;
}
}
return $chamilo_user;
}
/**
* Please declare here all the function you use in extldap_user_correspondance
* All these functions must have an $ldap_user parameter. This parameter is the
* array returned by the ldap for the user.
* */
function extldap_get_status($ldap_user)
{
return STUDENT;
}
function extldap_get_admin($ldap_user)
{
return false;
}
/**
* return the string used to search a user in ldap.
*
* @param string username
*
* @return string the serach string
*
* @author ndiechburg <noel@cblue.be>
* */
function extldap_get_user_search_string($username)
{
global $extldap_config;
// init
$filter = '('.$extldap_config['user_search'].')';
// replacing %username% by the actual username
$filter = str_replace('%username%', $username, $filter);
// append a global filter if needed
if (isset($extldap_config['filter']) && $extldap_config['filter'] != "") {
$filter = '(&'.$filter.'('.$extldap_config['filter'].'))';
}
return $filter;
}
/**
* Imports all LDAP users into Chamilo.
*
* @return false|null false on error, true otherwise
*/
function extldap_import_all_users()
{
global $extldap_config, $debug;
//echo "Connecting...\n";
$ds = extldap_connect();
if (!$ds) {
return false;
}
//echo "Binding...\n";
$ldapbind = false;
//Connection as admin to search dn of user
if (api_get_configuration_value('ldap_encrypt_admin_password')) {
$ldap_pass = api_decrypt_ldap_password($extldap_config['admin_password']);
} else {
$ldap_pass = $extldap_config['admin_password'];
}
$ldapbind = @ldap_bind($ds, $extldap_config['admin_dn'], $ldap_pass);
if ($ldapbind === false) {
if ($debug) {
error_log(
'EXTLDAP ERROR : cannot connect with admin login/password'
);
}
return false;
}
//browse ASCII values from a to z to avoid 1000 results limit of LDAP
$count = 0;
$alphanum = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
for ($a = 97; $a <= 122; $a++) {
$alphanum[] = chr($a);
}
foreach ($alphanum as $char1) {
foreach ($alphanum as $char2) {
$user_search = $extldap_config['user_search_import_all_users'];
//Search distinguish name of user
$sr = ldap_search($ds, $extldap_config['base_dn'], $user_search);
if (!$sr) {
if ($debug) {
error_log(
'EXTLDAP ERROR : ldap_search('.$ds.', '.$extldap_config['base_dn'].", $user_search) failed"
);
}
return false;
}
//echo "Getting entries\n";
$users = ldap_get_entries($ds, $sr);
//echo "Entries: ".$users['count']."\n";
for ($key = 0; $key < $users['count']; $key++) {
$user_id = extldap_add_user_by_array($users[$key], true);
$count++;
}
}
}
//echo "Found $count users in total\n";
@ldap_close($ds);
}
/**
* Insert users from an array of user fields.
*/
function extldap_add_user_by_array($data, $update_if_exists = true)
{
global $extldap_user_correspondance;
$lastname = api_convert_encoding($data[$extldap_user_correspondance['lastname']][0], api_get_system_encoding(), 'UTF-8');
$firstname = api_convert_encoding($data[$extldap_user_correspondance['firstname']][0], api_get_system_encoding(), 'UTF-8');
$email = $data[$extldap_user_correspondance['email']][0];
$username = $data[$extldap_user_correspondance['username']][0];
// TODO the password, if encrypted at the source, will be encrypted twice, which makes it useless. Try to fix that.
$passwordKey = isset($extldap_user_correspondance['password']) ? $extldap_user_correspondance['password'] : 'userPassword';
$password = $data[$passwordKey][0];
// To ease management, we add the step-year (etape-annee) code
//$official_code = $etape."-".$annee;
$official_code = api_convert_encoding($data[$extldap_user_correspondance['official_code']][0], api_get_system_encoding(), 'UTF-8');
$auth_source = 'ldap';
// No expiration date for students (recover from LDAP's shadow expiry)
$expiration_date = '';
$active = 1;
$status = 5;
$phone = '';
$picture_uri = '';
// Adding user
$user_id = 0;
if (UserManager::is_username_available($username)) {
//echo "$username\n";
$user_id = UserManager::create_user(
$firstname,
$lastname,
$status,
$email,
$username,
$password,
$official_code,
api_get_setting('platformLanguage'),
$phone,
$picture_uri,
$auth_source,
$expiration_date,
$active
);
} else {
if ($update_if_exists) {
$user = api_get_user_info($username);
$user_id = $user['user_id'];
//echo "$username\n";
UserManager::update_user(
$user_id,
$firstname,
$lastname,
$username,
null,
null,
$email,
$status,
$official_code,
$phone,
$picture_uri,
$expiration_date,
$active
);
}
}
return $user_id;
}
/**
* Get one user's single attribute value.
* User is identified by filter.
* $extldap_config['filter'] is also applied in complement, if defined.
*
* @param $filter string LDAP entry filter, such as '(uid=10000)'
* @param $attribute string name of the LDAP attribute to read the value from
*
* @throws Exception if more than one entries matched or on internal error
*
* @return string|bool the single matching user entry's single attribute value or false if not found
*/
function extldapGetUserAttributeValue($filter, $attribute)
{
global $extldap_config;
if (array_key_exists('filter', $extldap_config) && !empty($extldap_config['filter'])) {
$filter = '(&'.$filter.'('.$extldap_config['filter'].'))';
}
$ldap = extldap_connect();
if (false === $ldap) {
throw new Exception(get_lang('LDAPConnectFailed'));
}
if (api_get_configuration_value('ldap_encrypt_admin_password')) {
$ldap_pass = api_decrypt_ldap_password($extldap_config['admin_password']);
} else {
$ldap_pass = $extldap_config['admin_password'];
}
if (false === ldap_bind($ldap, $extldap_config['admin_dn'], $ldap_pass)) {
throw new Exception(get_lang('LDAPBindFailed'));
}
$searchResult = ldap_search($ldap, $extldap_config['base_dn'], $filter, [$attribute]);
if (false === $searchResult) {
throw new Exception(get_lang('LDAPSearchFailed'));
}
switch (ldap_count_entries($ldap, $searchResult)) {
case 0:
return false;
case 1:
$entry = ldap_first_entry($ldap, $searchResult);
if (false === $entry) {
throw new Exception(get_lang('LDAPFirstEntryFailed'));
}
$values = ldap_get_values($ldap, $entry, $attribute);
if (false == $values) {
throw new Exception(get_lang('LDAPGetValuesFailed'));
}
if ($values['count'] == 1) {
return $values[0];
}
throw new Exception(get_lang('MoreThanOneAttributeValueFound'));
default:
throw new Exception(get_lang('MoreThanOneUserMatched'));
}
}
/**
* Get the username from the CAS-supplied user identifier.
*
* searches in attribute $extldap_user_correspondance['extra']['cas_user'] or 'uid' by default
* reads value from attribute $extldap_user_correspondance['username'] or 'uid' by default
*
* @param $casUser string code returned from the CAS server to identify the user
*
* @throws Exception on error
*
* @return string|bool user login name, false if not found
*/
function extldapCasUserLogin($casUser)
{
global $extldap_user_correspondance;
// which LDAP attribute is the cas user identifier stored in ?
$attributeToFilterOn = 'uid';
if (is_array($extldap_user_correspondance) && array_key_exists('extra', $extldap_user_correspondance)) {
$extra = $extldap_user_correspondance['extra'];
if (is_array($extra) && array_key_exists('cas_user', $extra) && !empty($extra['cas_user'])) {
$attributeToFilterOn = $extra['cas_user'];
}
}
// which LDAP attribute is the username ?
$attributeToRead = 'uid';
if (is_array($extldap_user_correspondance)
&& array_key_exists('username', $extldap_user_correspondance)
&& !empty($extldap_user_correspondance['username'])
) {
$attributeToRead = $extldap_user_correspondance['username'];
}
// return the value
return extldapGetUserAttributeValue("($attributeToFilterOn=$casUser)", $attributeToRead);
}

View File

@@ -0,0 +1,22 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This script executes the importation of all users in the LDAP repository
* into Chamilo.
*
* @package chamilo.auth.ldap
*/
/**
* Init.
*/
if (PHP_SAPI != 'cli') {
exit('For security reasons, this script can only be launched from cron or from the command line');
}
require __DIR__.'/../../inc/global.inc.php';
require __DIR__.'/ldap.inc.php';
require __DIR__.'/../../../app/config/auth.conf.php';
/**
* Code execution.
*/
extldap_import_all_users();

View File

@@ -0,0 +1,55 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/functions.inc.php';
/** @var array $uData */
if ($uData['auth_source'] === 'azure') {
$plugin = AzureActiveDirectory::create();
if ('true' !== $plugin->get(AzureActiveDirectory::SETTING_ENABLE)) {
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'],
AzureActiveDirectory::EXTRA_FIELD_AZURE_ID
);
if (empty($azureIdValue) || empty($azureIdValue['value'])) {
api_not_allowed(true);
}
$organsationEmailField = new ExtraFieldValue('user');
$organsationEmailValue = $organsationEmailField->get_values_by_handler_and_field_variable(
$uData['user_id'],
AzureActiveDirectory::EXTRA_FIELD_ORGANISATION_EMAIL
);
if (empty($organsationEmailValue) || empty($organsationEmailValue['value'])) {
api_not_allowed(true);
}
$provider = $plugin->getProvider();
$authUrl = $provider->getAuthorizationUrl(['login_hint' => $organsationEmailValue['value']]);
ChamiloSession::write('oauth2state', $provider->getState());
// Redirect to Azure login.
header('Location: '.$authUrl);
// Avoid execution from here in local.inc.php script.
exit;
}

View File

@@ -0,0 +1,85 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* This file is included in main/inc/local.inc.php at user login if the user have 'extldap' in
* his auth_source field instead of platform.
*
* Variables that can be used :
* - $login : string containing the username posted by the user
* - $password : string containing the password posted by the user
* - $uData : associative array with those keys :
* -username
* -password
* -auth_source
* -active
* -expiration_date
*
* If login succeeds, we have 2 choices :
* 1. - set $loginFailed to false,
* - set $_SESSION['_user']['user_id'] with the Chamilo user_id
* - set $uidReset to true
* - upgrade user info in chamilo database if needed
* - let the script local.inc.php continue
*
* 2. - set $_SESSION['_user']['user_id'] with the Chamilo user_id
* - set $_SESSION['_user']['uidReset'] to true
* - upgrade user info in chamilo database if needed
* - redirect to any page and let local.inc.php do the magic
*
* If login fails we have to redirect to index.php with the right message
* Possible messages are :
* - index.php?loginFailed=1&error=access_url_inactive
* - index.php?loginFailed=1&error=account_expired
* - index.php?loginFailed=1&error=account_inactive
* - index.php?loginFailed=1&error=user_password_incorrect
* - index.php?loginFailed=1&error=unrecognize_sso_origin');
*/
require_once __DIR__.'/ldap.inc.php';
require_once __DIR__.'/functions.inc.php';
$debug = false;
if ($debug) {
error_log('Entering login.ldap.php');
}
$ldap_user = extldap_authenticate($login, $password);
if ($ldap_user !== false) {
if ($debug) {
error_log('extldap_authenticate works');
}
$chamilo_user = extldap_get_chamilo_user($ldap_user);
//userid is not on the ldap, we have to use $uData variable from local.inc.php
$chamilo_user['user_id'] = $uData['user_id'];
if ($debug) {
error_log("chamilo_user found user_id: {$uData['user_id']}");
}
//Update user info
if (isset($extldap_config['update_userinfo']) && $extldap_config['update_userinfo']) {
external_update_user($chamilo_user);
if ($debug) {
error_log("Calling external_update_user");
}
}
$loginFailed = false;
$_user['user_id'] = $chamilo_user['user_id'];
$_user['status'] = (isset($chamilo_user['status']) ? $chamilo_user['status'] : 5);
$_user['uidReset'] = true;
Session::write('_user', $_user);
$uidReset = true;
$logging_in = true;
Event::eventLogin($_user['user_id']);
} else {
if ($debug) {
error_log('extldap_authenticate error');
}
$loginFailed = true;
$uidReset = false;
if (isset($_user) && isset($_user['user_id'])) {
unset($_user['user_id']);
}
}

View File

@@ -0,0 +1,39 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/functions.inc.php';
/** @var array $uData */
if ('oauth2' === $uData['auth_source']) {
$plugin = OAuth2::create();
if ('true' !== $plugin->get(OAuth2::SETTING_ENABLE)) {
api_not_allowed(true);
}
$oauth2IdField = new ExtraFieldValue('user');
$oauth2IdValue = $oauth2IdField->get_values_by_handler_and_field_variable(
$uData['user_id'],
OAuth2::EXTRA_FIELD_OAUTH2_ID
);
if (empty($oauth2IdValue) || empty($oauth2IdValue['value'])) {
api_not_allowed(true);
}
$provider = $plugin->getProvider();
// Redirect to OAuth2 login.
$authUrl = $provider->getAuthorizationUrl();
ChamiloSession::write('oauth2state', $provider->getState());
if (OAuth2::isFirstLoginAfterAuthSource($uData['user_id'])) {
ChamiloSession::write('aouth2_authorization_url', $authUrl);
$authUrl = api_get_path(WEB_PLUGIN_PATH).'oauth2/redirect_info.php';
}
header('Location: '.$authUrl);
// Avoid execution from here in local.inc.php script.
exit;
}

View File

@@ -0,0 +1,107 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
// External login module : WS (for Web Services)
/**
* This file is included in main/inc/local.inc.php at user login if the user
* have 'ws' in his auth_source field instead of 'platform'.
*/
// Configure the web service URL here. e.g. http://174.1.1.19:8020/login.asmx?WSDL
$wsUrl = '';
// include common authentication functions
require_once __DIR__.'/functions.inc.php';
// call the login checker (defined below)
$isValid = loginWSAuthenticate($login, $password, $wsUrl);
// if the authentication was successful, proceed
if ($isValid === 1) {
//error_log('WS authentication worked');
$chamiloUser = api_get_user_info_from_username($login);
$loginFailed = false;
$_user['user_id'] = $chamiloUser['user_id'];
$_user['status'] = (isset($chamiloUser['status']) ? $chamiloUser['status'] : 5);
$_user['uidReset'] = true;
Session::write('_user', $_user);
$uidReset = true;
$logging_in = true;
Event::eventLogin($_user['user_id']);
} else {
//error_log('WS authentication error - user not approved by external WS');
$loginFailed = true;
$uidReset = false;
if (isset($_user) && isset($_user['user_id'])) {
unset($_user['user_id']);
}
}
/**
* Checks whether a user has the right to enter on the platform or not.
*
* @param string The username, as provided in form
* @param string The cleartext password, as provided in form
* @param string The WS URL, as provided at the beginning of this script
*/
function loginWSAuthenticate($username, $password, $wsUrl)
{
// check params
if (empty($username) || empty($password) || empty($wsUrl)) {
return false;
}
// Create new SOAP client instance
$client = new SoapClient($wsUrl);
if (!$client) {
return false;
}
// Include phpseclib methods, because of a bug with AES/CFB in mcrypt
include_once api_get_path(LIBRARY_PATH).'phpseclib/Crypt/AES.php';
// Define all elements necessary to the encryption
$key = '-+*%$({[]})$%*+-';
// Complete password con PKCS7-specific padding
$blockSize = 16;
$padding = $blockSize - (strlen($password) % $blockSize);
$password .= str_repeat(chr($padding), $padding);
$cipher = new Crypt_AES(CRYPT_AES_MODE_CFB);
$cipher->setKeyLength(128);
$cipher->setKey($key);
$cipher->setIV($key);
$cipheredPass = $cipher->encrypt($password);
// Mcrypt call left for documentation purposes - broken, see https://bugs.php.net/bug.php?id=51146
//$cipheredPass = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $password, MCRYPT_MODE_CFB, $key);
// Following lines present for debug purposes only
/*
$arr = preg_split('//', $cipheredPass, -1, PREG_SPLIT_NO_EMPTY);
foreach ($arr as $char) {
error_log(ord($char));
}
*/
// Change to base64 to avoid communication alteration
$passCrypted = base64_encode($cipheredPass);
// The call to the webservice will change depending on your definition
try {
$response = $client->validateUser(
[
'user' => $username,
'pass' => $passCrypted,
'system' => 'chamilo',
]
);
} catch (SoapFault $fault) {
error_log('Caught something');
if ($fault->faultstring != 'Could not connect to host') {
error_log('Not a connection problem');
throw $fault;
} else {
error_log('Could not connect to WS host');
}
return 0;
}
return $response->validateUserResult;
}

View File

@@ -0,0 +1,71 @@
<?php
// External login module : LDAP
/**
* This file is included by main/inc/local.inc.php when extldap is activated, a user try to login
* and chamilo does not find his user
* Variables that can be used :
* - $login : string containing the username posted by the user
* - $password : string containing the password posted by the user.
*
* Please configure the exldap module in main/auth/external_login/ldap.conf.php
*
* If login succeeds, we have to add the user in the chamilo database and then
* we have 2 choices :
* 1. - set $loginFailed to false,
* - set $_SESSION['_user']['user_id'] with the dokeos user_id
* - set $uidReset to true
* - let the script local.inc.php continue
*
* 2. - set $_SESSION['_user']['user_id'] with the dokeos user_id
* - set $_SESSION['_user']['uidReset'] to true
* - upgrade user info in dokeos database if needeed
* - redirect to any page and let local.inc.php do the magic
*
* If login fails we have also 2 choices :
* 1. - unset $_user['user_id']
* - set $loginFailed=true
* - set $uidReset = false
* User wil then have the user password incorrect message
*
* 2. We redirect the user to index.php with appropriate message :
* Possible messages are :
* - index.php?loginFailed=1&error=access_url_inactive
* - index.php?loginFailed=1&error=account_expired
* - index.php?loginFailed=1&error=account_inactive
* - index.php?loginFailed=1&error=user_password_incorrect
* - index.php?loginFailed=1&error=unrecognize_sso_origin');
* */
use ChamiloSession as Session;
require_once __DIR__.'/ldap.inc.php';
require_once __DIR__.'/functions.inc.php';
$ldap_user = extldap_authenticate($login, $password);
if ($ldap_user !== false) {
$chamilo_user = extldap_get_chamilo_user($ldap_user);
//username is not on the ldap, we have to use $login variable
$chamilo_user['username'] = $login;
$chamilo_uid = external_add_user($chamilo_user);
$chamiloUser = api_get_user_entity($chamilo_uid);
if ($chamiloUser) {
$loginFailed = false;
$_user['user_id'] = $chamiloUser->getId();
$_user['status'] = $chamiloUser->getStatus();
$_user['uidReset'] = true;
Session::write('_user', $_user);
$uidReset = true;
// Is user admin?
if ($chamilo_user['admin'] === true) {
$is_platformAdmin = true;
Database::query("INSERT INTO admin values ('{$chamiloUser->getId()}')");
}
Event::eventLogin($chamiloUser->getId());
MessageManager::sendNotificationOfNewRegisteredUser($chamiloUser);
}
} else {
$loginFailed = true;
$uidReset = false;
}

View File

@@ -0,0 +1,54 @@
<?php
/*
Template to automatically create a new user with information from anywhere.
This file is loaded by main/inc/local.inc.php
To use it please add this line to main/inc/conf/configuration.php :
$extAuthSource["external_logininfo"]["newUser"] = $_configuration['root_sys']."main/auth/external_logininfo/newUser.php";
You also have to implements the external_get_user_info function in functions.inc.php
*/
use ChamiloSession as Session;
require_once __DIR__.'/functions.inc.php';
//MAIN CODE
//$login and $password variables are setted in main/inc/local.inc.php
if ($password != DEFAULT_PASSWORD) {
$user = false;
} else {
$user = external_get_user_info($login, $password);
}
if ($user !== false && ($chamilo_uid = external_add_user($user)) !== false) {
//log in the user
$loginFailed = false;
$_user['user_id'] = $chamilo_uid;
$_user['uidReset'] = true;
Session::write('_user', $_user);
$uidReset = true;
//Autosubscribe to courses
if (!empty($user['courses'])) {
$autoSubscribe = explode('|', $user['courses']);
foreach ($autoSubscribe as $code) {
if (CourseManager::course_exists($code)) {
CourseManager::subscribeUser($_user['user_id'], $code);
}
}
}
// Is User Admin ?
if ($user['admin']) {
$is_platformAdmin = true;
Database::query("INSERT INTO admin values ('$chamilo_uid')");
}
// Can user create course
$is_allowedCreateCourse = (bool) (($user['status'] == COURSEMANAGER) or (api_get_setting('drhCourseManagerRights') and $user['status'] == SESSIONADMIN));
Event::eventLogin($chamilo_uid);
} else {
$loginFailed = true;
unset($_user['user_id']);
$uidReset = false;
}

View File

@@ -0,0 +1,41 @@
<?php
//Script loaded by local.inc.php providing update user information of type external_logininfo.
/*
This script must not exit.
*/
use ChamiloSession as Session;
require_once __DIR__.'/functions.inc.php';
//MAIN CODE
//$uData variable is set in local.inc.php
$user = api_get_user_info($uData['user_id']);
$new_user = external_get_user_info($login);
$user['firstname'] = $new_user['firstname'];
$user['lastname'] = $new_user['lastname'];
$user['status'] = $new_user['status'];
$user['admin'] = $new_user['admin'];
$user['email'] = $new_user['email'];
$user['username'] = $new_user['username'];
$user['profile_link'] = $new_user['profile_link'];
$user['worldwide_bu'] = $new_user['worldwide_bu'];
$user['manager'] = $new_user['manager'];
$user['country_bu'] = $new_user['country_bu'];
$user['extra'] = $new_user['extra'];
if ($new_user !== false) { //User can login
external_update_user($user);
$loginFailed = false;
$_user['user_id'] = $user['user_id'];
$_user['uidReset'] = true;
$uidReset = true;
Session::write('_user', $_user);
} else {
//User cannot login
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_password_incorrect');
exit;
}

76
main/auth/gotocourse.php Normal file
View File

@@ -0,0 +1,76 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Allow the user to login to a course after reaching a course URL
* (e.g. http://chamilo.chamilo.org/courses/MYCOURSE/?id_session=0 )
* See https://support.chamilo.org/issues/6768.
*
* Author : hubert.borderiou@grenet.fr
*/
require_once __DIR__.'/../inc/global.inc.php';
$msg = null;
if (isset($_GET['firstpage'])) {
$firstpage = $_GET['firstpage'];
// if course is public, go to course without auth
$tab_course_info = api_get_course_info($firstpage);
api_set_firstpage_parameter($firstpage);
$tpl = new Template(null, 1, 1);
$action = api_get_self().'?'.Security::remove_XSS($_SERVER['QUERY_STRING']);
$action = str_replace('&amp;', '&', $action);
$form = new FormValidator('formLogin', 'post', $action, null, ['class' => 'form-stacked']);
$params = [
'placeholder' => get_lang('UserName'),
];
// Avoid showing the autocapitalize option if the browser doesn't
// support it: this attribute is against the HTML5 standard
if (api_browser_support('autocapitalize')) {
$params['autocapitalize'] = 'none';
}
$form->addElement(
'text',
'login',
null,
$params
);
$params = [
'placeholder' => get_lang('Password'),
];
if (api_browser_support('autocapitalize')) {
$params['autocapitalize'] = 'none';
}
$form->addElement(
'password',
'password',
null,
$params
);
$form->addButtonNext(get_lang('LoginEnter'), 'submitAuth');
// see same text in main_api.lib.php function api_not_allowed
if (api_is_cas_activated()) {
$msg .= Display::return_message(sprintf(get_lang('YouHaveAnInstitutionalAccount'), api_get_setting("Institution")), '', false);
$msg .= Display::div(Template::displayCASLoginButton(), ['align' => 'center']);
$msg .= Display::return_message(get_lang('YouDontHaveAnInstitutionAccount'));
$msg .= "<p style='text-align:center'><a href='#' onclick='$(this).parent().next().toggle()'>".get_lang('LoginWithExternalAccount')."</a></p>";
$msg .= "<div style='display:none;'>";
}
$msg .= '<div class="well_login">';
$msg .= $form->returnForm();
$msg .= '</div>';
if (api_is_cas_activated()) {
$msg .= "</div>";
}
$msg .= '<hr/><p style="text-align:center"><a href="'.api_get_path(WEB_PATH).'">'.get_lang('ReturnToCourseHomepage').'</a></p>';
$tpl->assign('content', '<h4>'.get_lang('LoginToGoToThisCourse').'</h4>'.$msg);
$tpl->display_one_col_template();
} else {
api_delete_firstpage_parameter();
header('Location: '.api_get_path(WEB_PATH).'index.php');
exit;
}

104
main/auth/hmac/login.php Normal file
View File

@@ -0,0 +1,104 @@
<?php
use ChamiloSession as Session;
/**
* This file contains the necessary elements to allow a Single Sign On
* based on a validation of a hmac computed hash.
*
* To allow the SSO access /main/auth/hmac/login.php must receive as
* query string parameters the following parameters:
*
* 'email': user email.
*
* 'time': time of the request, as HH:mm.
*
* 'system': System name, a control value.
*
* 'Token': a HMAC computed SHA256 algorithm based on the concatenation of
* the 'time' and 'email' value.
*
* Example:
*
* https://campus.chamilo/main/auth/hmac/login.php?email=user@domain.com&time=10:48&system=SystemName&Token=XYZ
*
* Also a settings.php file must be configured the set the following values:
*
* 'secret': secret key used to generate a HMAC computed hash to validate the
* received 'Token' parameter on the query string.
*
* 'secret': secret key used to generate a HMAC computed hash to validate the 'Token' parameter on the query string.
*
* 'expiration_time': integer value, maximum time in minutes of the request lifetime.
*/
require_once '../../../main/inc/global.inc.php';
// Create a settings.dist.php
if (file_exists('settings.php')) {
require_once 'settings.php';
} else {
$message = '';
if (api_is_platform_admin()) {
$message = 'Create a settings.php';
}
api_not_allowed(true, $message);
}
// Check if we have all the parameters from the query string
if (isset($_GET['email']) && isset($_GET['time']) && isset($_GET['system']) && isset($_GET['Token'])) {
$email = $_GET['email'];
$time = $_GET['time'];
$system = $_GET['system'];
$token = $_GET['Token'];
// Generate the token
$validToken = hash_hmac('sha256', $time.$email, $settingsInfo['secret'], false);
// Compare the received token & the valid token
if ($token !== $validToken) {
Display::addFlash(Display::return_message('Incorrect token', 'error'));
header('Location: '.api_get_path(WEB_PATH));
exit;
}
// Check the system is correct
if ($settingsInfo['system'] !== $system) {
Display::addFlash(Display::return_message('Incorrect client', 'error'));
header('Location: '.api_get_path(WEB_PATH));
exit;
}
// Check if the request expired with a diff between the query string parameter & the actual time
if ($settingsInfo['expiration_time'] && $settingsInfo['expiration_time'] > 0) {
$tokenTime = strtotime($time);
$diff = abs($tokenTime - time()) / 60;
if ($diff > $settingsInfo['expiration_time']) {
Display::addFlash(Display::return_message('Token expired', 'error'));
header('Location: '.api_get_path(WEB_PATH));
exit;
}
}
// Get the user info
$userInfo = api_get_user_info_from_email($email);
// Log-in user if exists or a show error message
if (!empty($userInfo)) {
Session::write('_user', $userInfo);
Session::write('is_platformAdmin', false);
Session::write('is_allowedCreateCourse', false);
Event::eventLogin($userInfo['user_id']);
Session::write('flash_messages', '');
} else {
Display::addFlash(Display::return_message(get_lang('UserNotFound'), 'error'));
header('Location: '.api_get_path(WEB_PATH));
exit;
}
header('Location: '.api_get_path(WEB_PATH).'user_portal.php');
exit;
} else {
Display::addFlash(Display::return_message('Invalid request', 'error'));
header('Location: '.api_get_path(WEB_PATH));
exit;
}

View File

@@ -0,0 +1,7 @@
<?php
$settingsInfo = [
'secret' => '',
'system' => '',
'expiration_time' => 0,
];

48
main/auth/hrm_courses.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../inc/global.inc.php';
api_block_anonymous_users();
$isHrm = api_is_drh();
if (!$isHrm) {
api_not_allowed(true);
}
$hrm = api_get_user_entity(api_get_user_id());
$assignedUsers = UserManager::get_users_followed_by_drh($hrm->getId());
$users = [];
$courseController = new IndexManager('');
foreach ($assignedUsers as $assignedUserId => $assignedUserInfo) {
$assignedUser = api_get_user_entity($assignedUserId);
if (!$assignedUser) {
continue;
}
$userInfo = [
'username' => $assignedUser->getUsername(),
'complete_name' => UserManager::formatUserFullName($assignedUser),
'picture_url' => UserManager::getUserPicture($assignedUserId),
'course_list' => $courseController->returnCoursesAndSessions($assignedUserId)['html'],
];
$users[$assignedUser->getId()] = $userInfo;
}
$toolName = get_lang('HrmAssignedUsersCourseList');
$view = new Template($toolName);
$view->assign('users', $users);
$content = $view->fetch(
$view->get_template('auth/hrm_courses.tpl')
);
$view->assign('header', $toolName);
$view->assign('content', $content);
$view->display_one_col_template();

7
main/auth/index.html Normal file
View File

@@ -0,0 +1,7 @@
<html>
<head>
<meta http-equiv="refresh" content="0; url=courses.php">
</head>
<body>
</body>
</html>

1103
main/auth/inscription.php Normal file

File diff suppressed because it is too large Load Diff

230
main/auth/justification.php Normal file
View File

@@ -0,0 +1,230 @@
<?php
/* For licensing terms, see /license.txt */
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
api_block_anonymous_users(true);
$allowJustification = api_get_plugin_setting('justification', 'tool_enable') === 'true';
if (!$allowJustification) {
api_not_allowed(true);
}
$user_data = api_get_user_info(api_get_user_id());
$justification = '';
$plugin = Justification::create();
$fields = $plugin->getList();
$formValidator = new FormValidator('justification');
$formValidator->addHeader($plugin->get_lang('Justification'));
foreach ($fields as $field) {
$formValidator->addHtml('<a name="'.$field['code'].'"></a>');
$formValidator->addFile($field['code'].'_file', [$field['name'], $field['comment']]);
if ($field['date_manual_on']) {
$formValidator->addDatePicker($field['code'].'_date', $plugin->get_lang('ValidityDate'));
}
$formValidator->addHtml('<hr>');
}
$formValidator->addButtonSend(get_lang('Send'));
if ($formValidator->validate() && isset($_FILES)) {
foreach ($fields as $field) {
$fieldId = $field['id'];
$days = $field['validity_duration'];
if (isset($_FILES[$field['code'].'_file']) && !empty($_FILES[$field['code'].'_file']['tmp_name'])) {
$file = $_FILES[$field['code'].'_file'];
} else {
continue;
}
$date = isset($_REQUEST[$field['code'].'_date']) ? $_REQUEST[$field['code'].'_date'].' 13:00:00' : api_get_local_time();
$startDate = api_get_utc_datetime($date, false, true);
$interval = new \DateInterval('P'.$days.'D');
$startDate->add($interval);
$finalDate = $startDate->format('Y-m-d');
$file['name'] = api_replace_dangerous_char($file['name']);
$fileName = $file['name'];
$params = [
'file_path' => $fileName,
'user_id' => api_get_user_id(),
'date_validity' => $finalDate,
'justification_document_id' => $fieldId,
];
$id = Database::insert('justification_document_rel_users', $params);
if ($id) {
api_upload_file('justification', $file, $id);
Display::addFlash(Display::return_message($plugin->get_lang('JustificationSaved')));
}
}
header('Location: '.api_get_self());
exit;
}
$userJustifications = $plugin->getUserJustificationList(api_get_user_id());
if (!empty($userJustifications)) {
if (count($fields) <= count($userJustifications) && $_REQUEST['a'] != 'notification_sent') {
$formValidator->addHtml('<label class="col-sm-2 control-label"></label><a class="btn btn-primary" href="'.api_get_self().'?a=notify_justification" >'.$plugin->get_lang('SendNotificationToAllAdmins').'</a>');
}
}
$userJustificationList = '';
$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : '';
$justificationContent = '';
switch ($action) {
case 'notify_justification':
$link = api_get_path(WEB_PATH).'plugin/justification/justification_by_user.php?user_id='.api_get_user_id();
$notificationEmailSubject = $plugin->get_lang('JustificationsCompleted').': '.$userInfo['complete_name'];
$notificationEmailContent = $notificationEmailSubject.' <br /><br />'.'<a href="'.$link.'">'.$link.'</a>';
if (api_get_plugin_setting('justification', 'notification_to_creator_only') === 'true') {
$sql = "select creator_id from user where user_id = ".api_get_user_id();
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
$row = Database::fetch_array($result);
$sendToAllAdmins = false;
MessageManager::send_message_simple(
$row['creator_id'],
$notificationEmailSubject,
$notificationEmailContent,
api_get_user_id());
}
}
if ($sendToAllAdmins) {
// get_all_administrators
$adminList = UserManager::get_all_administrators();
foreach ($adminList as $adminId => $data) {
MessageManager::send_message_simple(
$adminId,
$notificationEmailSubject,
$notificationEmailContent,
api_get_user_id());
}
}
Display::addFlash(Display::return_message(get_lang('MessageSent')));
header('Location: '.api_get_self().'?a=notification_sent');
exit;
break;
case 'edit_justification':
$justificationId = isset($_REQUEST['justification_id']) ? (int) $_REQUEST['justification_id'] : '';
$userJustification = $plugin->getUserJustification($justificationId);
$justification = $plugin->getJustification($userJustification['justification_document_id']);
if ($justification['date_manual_on'] == 0) {
api_not_allowed(true);
}
$formEdit = new FormValidator('edit', 'post', api_get_self().'?a=edit_justification&justification_id='.$justificationId);
$formEdit->addHeader($justification['name']);
$element = $formEdit->addDatePicker('date_validity', $plugin->get_lang('ValidityDate'));
$element->setValue($userJustification['date_validity']);
$formEdit->addButtonUpdate(get_lang('Update'));
$formEdit->setDefaults($userJustification);
$justificationContent = $formEdit->returnForm();
if ($formEdit->validate()) {
$values = $formEdit->getSubmitValues();
$date = Database::escape_string($values['date_validity']);
$sql = "UPDATE justification_document_rel_users SET date_validity = '$date'
WHERE id = $justificationId AND user_id = ".$user_data['id'];
Database::query($sql);
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.api_get_self());
exit;
}
break;
case 'delete_justification':
$justificationId = isset($_REQUEST['justification_id']) ? (int) $_REQUEST['justification_id'] : '';
$userJustification = $plugin->getUserJustification($justificationId);
if ($userJustification && $userJustification['user_id'] == api_get_user_id()) {
api_remove_uploaded_file_by_id('justification', $justificationId, $userJustification['file_path']);
$sql = "DELETE FROM justification_document_rel_users
WHERE id = $justificationId AND user_id = ".$user_data['id'];
Database::query($sql);
Display::addFlash(Display::return_message(get_lang('Deleted')));
}
header('Location: '.api_get_self());
exit;
break;
}
if (!empty($userJustifications)) {
$userJustificationList .= Display::page_subheader3($plugin->get_lang('MyJustifications'));
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$column = 0;
$row = 0;
$headers = [
get_lang('Name'),
get_lang('File'),
$plugin->get_lang('ValidityDate'),
get_lang('Actions'),
];
foreach ($headers as $header) {
$table->setHeaderContents($row, $column, $header);
$column++;
}
$row = 1;
foreach ($userJustifications as $userJustification) {
$justification = $plugin->getJustification($userJustification['justification_document_id']);
$url = api_get_uploaded_web_url('justification', $userJustification['id'], $userJustification['file_path']);
$link = Display::url($userJustification['file_path'], $url);
$col = 0;
$table->setCellContents($row, $col++, $justification['name']);
$table->setCellContents($row, $col++, $link);
$date = $userJustification['date_validity'];
if ($userJustification['date_validity'] < api_get_local_time()) {
$date = Display::label($userJustification['date_validity'], 'warning');
}
$table->setCellContents($row, $col++, $date);
$actions = '';
if ($justification['date_manual_on'] == 1) {
$actions .= Display::url(get_lang('Edit'), api_get_self().'?a=edit_justification&justification_id='.$userJustification['id'], ['class' => 'btn btn-primary']);
}
$actions .= '&nbsp;'.Display::url(get_lang('Delete'), api_get_self().'?a=delete_justification&justification_id='.$userJustification['id'], ['class' => 'btn btn-danger']);
$table->setCellContents($row, $col++, $actions);
$code = $justification['code'];
$htmlHeadXtra[] = '<script type="text/javascript" >$(function(){$("#file_'.$code.'_file label").css("color","green");});</script>';
$row++;
}
$userJustificationList .= $justificationContent.$table->toHtml();
}
$htmlHeadXtra[] = '<script type="text/javascript" >
$(function(){
$("#justification label").each(function(){
var colorG = $(this).css("color");
var lgtxt = $(this).text().replace(/ /g,"").length;
if (colorG!="green"&&colorG!="rgb(0, 128, 0)"&&lgtxt>3) {
$(this).append("<img src=\"'.api_get_path(WEB_PATH).'main/img/icons/22/warning.png\" />");
}
});
});</script>';
$tabs = SocialManager::getHomeProfileTabs('justification');
$justification = $tabs.$formValidator->returnForm().$userJustificationList;
$tpl = new Template(get_lang('ModifyProfile'));
SocialManager::setSocialUserBlock($tpl, api_get_user_id(), 'home');
$menu = SocialManager::show_social_menu(
'home',
null,
api_get_user_id(),
false,
false
);
$tpl->assign('social_menu_block', $menu);
$tpl->assign('social_right_content', $justification);
$social_layout = $tpl->get_template('social/edit_profile.tpl');
$tpl->display($social_layout);

8
main/auth/key/index.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
/**
* Display nothing. This ensure Apache doesn't display the list of files and folders
* when it is not propertly configured.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/

View File

@@ -0,0 +1,268 @@
<?php
use ChamiloSession as Session;
/**
* Used to authenticate user with an access token. By default this method is disabled.
* Method used primarily to make API calls: Rss, file upload.
*
* Access is granted only for the services that are enabled.
*
* To be secured this method must
*
* 1) be called through httpS to avoid sniffing (note that this is the case anyway with other methods such as cookies)
* 2) the url/access token must be secured
*
* This authentication method is session less. This is to ensure that the navigator
* do not receive an access cookie that will grant it access to other parts of the
* application.
*
*
* Usage:
*
* Enable KeyAuth for a specific service. Add the following lines so that
* the key authentication method is enabled for a specific service before
* calling global.inc.php.
*
* include_once '.../main/inc/autoload.inc.php';
* KeyAuth::enable_services('my_service');
* include_once '.../main/inc/global.inc.php';
*
*
* Enable url access for a short period of time:
*
* token = KeyAuth::create_temp_token();
* url = '...?access_token=' . $token ;
*
* @see AccessToken
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
*/
class KeyAuth
{
public const PARAM_ACCESS_TOKEN = 'access_token';
protected static $services = [];
protected function __construct()
{
}
public static function create_temp_token($service = null, $duration = 60, $user_id = null)
{
return UserApiKeyManager::create_temp_token($service, $duration, $user_id);
}
/**
* Returns enabled services.
*
* @return array
*/
public static function get_services()
{
return self::$services;
}
/**
* Name of the service for which we are goint to check the API Key.
* If empty it disables authentication.
*
* !! 10 chars max !!
*
* @param string $_
*/
public static function enable_services($_)
{
$args = func_get_args();
$names = [];
foreach ($args as $arg) {
if (is_object($arg)) {
$f = [$arg, 'get_service_name'];
$name = call_user_func($f);
} else {
$name = $arg;
}
$name = substr($name, 0, 10);
self::$services[$name] = $name;
}
}
public static function disable_services($_)
{
$args = func_get_args();
$names = [];
foreach ($args as $name) {
$name = substr($name, 0, 10);
unset(self::$services[$name]);
}
}
public static function is_service_enabled($service)
{
$services = self::get_services();
foreach ($services as $s) {
if ($s == $service) {
return true;
}
}
return false;
}
public static function clear_services()
{
self::$services[$name] = [];
}
/**
* Enable key authentication for the default service - i.e. chamilo.
*/
public static function enable()
{
self::enable_services(UserApiKeyManager::default_service());
}
public static function disable()
{
self::$services[$name] = [];
}
/**
* Returns true if the key authentication method is enabled. False otherwise.
* Default to false.
*
* @return bool
*/
public static function is_enabled()
{
return !empty(self::$services);
}
/**
* @return KeyAuth
*/
public static function instance()
{
static $result = null;
if (empty($result)) {
$result = new self();
}
return $result;
}
/**
* Returns true if authentication accepts to run otherwise returns false.
*
* @return bool
*/
public function accept()
{
/**
* Authentication method must be enabled.
*/
if (!self::is_enabled()) {
return false;
}
$token = $this->get_access_token();
if ($token->is_empty()) {
return false;
}
$key = UserApiKeyManager::get_by_id($token->get_id());
if (empty($key)) {
return false;
}
/**
* The service corresponding to the key must be enabled.
*/
$service = $key['api_service'];
if (!self::is_service_enabled($service)) {
return false;
}
/**
* User associated with the key must be active.
*/
$user = api_get_user_info($token->get_user_id());
if (empty($user)) {
return false;
}
if (!$user['active']) {
return false;
}
/**
* Token must be valid.
*/
return $token->is_valid();
}
/**
* If accepted tear down session, log in user and returns true.
* If not accepted do nothing and returns false.
*
* @return bool
*/
public function login()
{
if (!$this->accept()) {
return false;
}
/**
* ! important this is to ensure we don't grant access for other parts.
*/
Session::destroy();
/**
* We don't allow redirection since access is granted only for this call.
*/
global $no_redirection, $noredirection;
$no_redirection = true;
$noredirection = true;
Session::write('noredirection', $noredirection);
$user_id = $this->get_user_id();
$course_code = $this->get_course_code();
$group_id = $this->get_group_id();
Login::init_user($user_id, true);
Login::init_course($course_code, true);
Login::init_group($group_id, true);
return true;
}
/**
* Returns the request access token.
*
* @return AccessToken
*/
public function get_access_token()
{
$string = Request::get(self::PARAM_ACCESS_TOKEN);
return AccessToken::parse($string);
}
public function get_user_id()
{
return $this->get_access_token()->get_user_id();
}
public function get_course_code()
{
return Request::get('cidReq', 0);
}
/**
* @return int
*/
public function get_group_id()
{
return Request::get('gidReq', 0);
}
}

745
main/auth/ldap/authldap.php Normal file
View File

@@ -0,0 +1,745 @@
<?php
/* For licensing terms, see /license.txt */
/**
* LDAP module functions.
*
* If the application uses LDAP, these functions are used
* for logging in, searching user info, adding this info
* to the Chamilo database...
- function ldap_authentication_check()
- function ldap_find_user_info()
- function ldap_login()
- function ldap_put_user_info_locally()
- ldap_set_version()
known bugs
----------
- (fixed 18 june 2003) code has been internationalized
- (fixed 07/05/2003) fixed some non-relative urls or includes
- (fixed 28/04/2003) we now use global config.inc variables instead of local ones
- (fixed 22/04/2003) the last name of a user was restricted to the first part
- (fixed 11/04/2003) the user was never registered as a course manager
version history
---------------
This historial has been discontinued. Please use the Mercurial logs for more
3.2 - updated to allow for specific term search for teachers identification
3.1 - updated code to use database settings, to respect coding conventions
* as much as possible (camel-case removed) and to allow for non-anonymous login
- Patrick Cool: fixing security hole
* @author Roan Embrechts
*
* @version 3.0
*
* @package chamilo.auth.ldap
* Note:
* If you are using a firewall, you might need to check port 389 is open in
* order for Chamilo to communicate with the LDAP server.
* See http://support.chamilo.org/issues/4675 for details.
*/
/**
* Inclusions.
*/
use ChamiloSession as Session;
/**
* Code.
*/
require_once api_get_path(SYS_CODE_PATH).'auth/external_login/ldap.inc.php';
require 'ldap_var.inc.php';
/**
* Check login and password with LDAP.
*
* @return bool when login & password both OK, false otherwise
*
* @author Roan Embrechts (based on code from Universit<69> Jean Monet)
*/
function ldap_login($login, $password)
{
//error_log('Entering ldap_login('.$login.','.$password.')',0);
$res = ldap_authentication_check($login, $password);
// res=-1 -> the user does not exist in the ldap database
// res=1 -> invalid password (user does exist)
if ($res == 1) { //WRONG PASSWORD
//$errorMessage = "LDAP User or password incorrect, try again.<br />";
if (isset($log)) {
unset($log);
}
if (isset($uid)) {
unset($uid);
}
$loginLdapSucces = false;
}
if ($res == -1) { //WRONG USERNAME
//$errorMessage = "LDAP User or password incorrect, try again.<br />";
$login_ldap_success = false;
}
if ($res == 0) { //LOGIN & PASSWORD OK - SUCCES
//$errorMessage = "Successful login w/ LDAP.<br>";
$login_ldap_success = true;
}
//$result = "This is the result: $errorMessage";
$result = $login_ldap_success;
return $result;
}
/**
* Find user info in LDAP.
*
* @return array Array with indexes: "firstname", "name", "email", "employeenumber"
*
* @author Stefan De Wannemacker
* @author Roan Embrechts
*/
function ldap_find_user_info($login)
{
//error_log('Entering ldap_find_user_info('.$login.')',0);
global $ldap_host, $ldap_port, $ldap_basedn, $ldap_rdn, $ldap_pass, $ldap_search_dn;
// basic sequence with LDAP is connect, bind, search,
// interpret search result, close connection
//echo "Connecting ...";
$ldap_connect = ldap_connect($ldap_host, $ldap_port);
ldap_set_version($ldap_connect);
if ($ldap_connect) {
//echo " Connect to LDAP server successful ";
//echo "Binding ...";
$ldap_bind = false;
$ldap_bind_res = ldap_handle_bind($ldap_connect, $ldap_bind);
if ($ldap_bind_res) {
//echo " LDAP bind successful... ";
//echo " Searching for uid... ";
// Search surname entry
//OLD: $sr=ldap_search($ldapconnect,"dc=rug, dc=ac, dc=be", "uid=$login");
//echo "<p> ldapDc = '$LDAPbasedn' </p>";
if (!empty($ldap_search_dn)) {
$sr = ldap_search($ldap_connect, $ldap_search_dn, "uid=$login");
} else {
$sr = ldap_search($ldap_connect, $ldap_basedn, "uid=$login");
}
//echo " Search result is ".$sr;
//echo " Number of entries returned is ".ldap_count_entries($ldapconnect,$sr);
//echo " Getting entries ...";
$info = ldap_get_entries($ldap_connect, $sr);
//echo "Data for ".$info["count"]." items returned:<p>";
} // else could echo "LDAP bind failed...";
//echo "Closing LDAP connection<hr>";
ldap_close($ldap_connect);
} // else could echo "<h3>Unable to connect to LDAP server</h3>";
//DEBUG: $result["firstname"] = "Jan"; $result["name"] = "De Test"; $result["email"] = "email@ugent.be";
$result["firstname"] = $info[0]["cn"][0];
$result["name"] = $info[0]["sn"][0];
$result["email"] = $info[0]["mail"][0];
$tutor_field = api_get_setting('ldap_filled_tutor_field');
$result[$tutor_field] = $info[0][$tutor_field]; //employeenumber by default
return $result;
}
/**
* This function uses the data from ldap_find_user_info()
* to add the userdata to Chamilo
* "firstname", "name", "email", "isEmployee".
*
* @author Roan Embrechts
*/
function ldap_put_user_info_locally($login, $info_array)
{
//error_log('Entering ldap_put_user_info_locally('.$login.',info_array)',0);
global $ldap_pass_placeholder;
global $submitRegistration, $submit, $uname, $email,
$nom, $prenom, $password, $password1, $status;
global $platformLanguage;
global $loginFailed, $uidReset, $_user;
/*----------------------------------------------------------
1. set the necessary variables
------------------------------------------------------------ */
$uname = $login;
$email = $info_array["email"];
$nom = $info_array["name"];
$prenom = $info_array["firstname"];
$password = $ldap_pass_placeholder;
$password1 = $ldap_pass_placeholder;
$official_code = '';
define("STUDENT", 5);
define("COURSEMANAGER", 1);
$tutor_field = api_get_setting('ldap_filled_tutor_field');
$tutor_value = api_get_setting('ldap_filled_tutor_field_value');
if (empty($tutor_field)) {
$status = STUDENT;
} else {
if (empty($tutor_value)) {
//in this case, we are assuming that the admin didn't give a criteria
// so that if the field is not empty, it is a tutor
if (!empty($info_array[$tutor_field])) {
$status = COURSEMANAGER;
} else {
$status = STUDENT;
}
} else {
//the tutor_value is filled, so we need to check the contents of the LDAP field
if (is_array($info_array[$tutor_field]) && in_array($tutor_value, $info_array[$tutor_field])) {
$status = COURSEMANAGER;
} else {
$status = STUDENT;
}
}
}
//$official_code = xxx; //example: choose an attribute
/*----------------------------------------------------------
2. add info to Chamilo
------------------------------------------------------------ */
$language = api_get_setting('platformLanguage');
if (empty($language)) {
$language = 'english';
}
$_userId = UserManager::create_user(
$prenom,
$nom,
$status,
$email,
$uname,
$password,
$official_code,
$language,
'',
'',
'ldap'
);
//echo "new user added to Chamilo, id = $_userId";
//user_id, username, password, auth_source
/*----------------------------------------------------------
3. register session
------------------------------------------------------------ */
$uData['user_id'] = $_userId;
$uData['username'] = $uname;
$uData['auth_source'] = "ldap";
$loginFailed = false;
$uidReset = true;
$_user['user_id'] = $uData['user_id'];
Session::write('_uid', $_user['user_id']);
}
/**
* The code of UGent uses these functions to authenticate.
* function AuthVerifEnseignant ($uname, $passwd)
* function AuthVerifEtudiant ($uname, $passwd)
* function Authentif ($uname, $passwd).
*
* @todo translate the comments and code to english
* @todo let these functions use the variables in config.inc instead of ldap_var.inc
*/
/**
* Checks the existence of a member in LDAP.
*
* @param string username input on keyboard
* @param string password given by user
*
* @return int 0 if authentication succeeded, 1 if password was incorrect, -1 if it didn't belong to LDAP
*/
function ldap_authentication_check($uname, $passwd)
{
//error_log('Entering ldap_authentication_check('.$uname.','.$passwd.')',0);
global $ldap_host, $ldap_port, $ldap_basedn, $ldap_host2, $ldap_port2, $ldap_rdn, $ldap_pass;
//error_log('Entering ldap_authentication_check('.$uname.','.$passwd.')',0);
// Establish anonymous connection with LDAP server
// Etablissement de la connexion anonyme avec le serveur LDAP
$ds = ldap_connect($ldap_host, $ldap_port);
ldap_set_version($ds);
$test_bind = false;
$test_bind_res = ldap_handle_bind($ds, $test_bind);
//if problem, use the replica
if ($test_bind_res === false) {
$ds = ldap_connect($ldap_host2, $ldap_port2);
ldap_set_version($ds);
} // else: error_log('Connected to server '.$ldap_host);
if ($ds !== false) {
//Creation of filter containing values input by the user
// Here it might be necessary to use $filter="(samaccountName=$uname)"; - see http://support.chamilo.org/issues/4675
$filter = "(uid=$uname)";
// Open anonymous LDAP connection
$result = false;
$ldap_bind_res = ldap_handle_bind($ds, $result);
// Executing the search with the $filter parametr
//error_log('Searching for '.$filter.' on LDAP server',0);
$sr = ldap_search($ds, $ldap_basedn, $filter);
$info = ldap_get_entries($ds, $sr);
$dn = ($info[0]["dn"]);
// debug !! echo"<br> dn = $dn<br> pass = $passwd<br>";
// closing 1st connection
ldap_close($ds);
}
// test the Distinguish Name from the 1st connection
if ($dn == "") {
return -1; // doesn't belong to the addressbook
}
//bug ldap.. if password empty, return 1!
if ($passwd == "") {
return 1;
}
// Opening 2nd LDAP connection : Connection user for password check
$ds = ldap_connect($ldap_host, $ldap_port);
ldap_set_version($ds);
if (!$test_bind) {
$ds = ldap_connect($ldap_host2, $ldap_port2);
ldap_set_version($ds);
}
// return in case of wrong password connection error
if (@ldap_bind($ds, $dn, $passwd) === false) {
return 1; // invalid password
} else {// connection successfull
return 0;
}
} // end of check
/**
* Set the protocol version with version from config file (enables LDAP version 3).
*
* @param resource resource LDAP connexion resource, passed by reference
*/
function ldap_set_version(&$resource)
{
//error_log('Entering ldap_set_version(&$resource)',0);
global $ldap_version;
if ($ldap_version > 2) {
ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION, 3);
//ok - don't do anything
//failure - should switch back to version 2 by default
}
}
/**
* Handle bind (whether authenticated or not).
*
* @param resource The LDAP handler to which we are connecting (by reference)
* @param resource The LDAP bind handler we will be modifying
* @param bool $ldap_bind
*
* @return bool Status of the bind assignment. True for success, false for failure.
*/
function ldap_handle_bind(&$ldap_handler, &$ldap_bind)
{
//error_log('Entering ldap_handle_bind(&$ldap_handler,&$ldap_bind)',0);
global $ldap_rdn, $ldap_pass, $extldap_config;
$ldap_rdn = $extldap_config['admin_dn'];
$ldap_pass = $extldap_config['admin_password'];
if (api_get_configuration_value('ldap_encrypt_admin_password')) {
$ldap_pass = api_decrypt_ldap_password($extldap_config['admin_password']);
}
if (!empty($ldap_rdn) and !empty($ldap_pass)) {
//error_log('Trying authenticated login :'.$ldap_rdn.'/'.$ldap_pass,0);
$ldap_bind = ldap_bind($ldap_handler, $ldap_rdn, $ldap_pass);
if (!$ldap_bind) {
//error_log('Authenticated login failed',0);
//try in anonymous mode, you never know...
$ldap_bind = ldap_bind($ldap_handler);
}
} else {
// this is an "anonymous" bind, typically read-only access:
$ldap_bind = ldap_bind($ldap_handler);
}
if (!$ldap_bind) {
return false;
} else {
//error_log('Login finally OK',0);
return true;
}
}
/**
* Get the total number of users on the platform.
*
* @see SortableTable#get_total_number_of_items()
*
* @author Mustapha Alouani
*/
function ldap_get_users()
{
global $ldap_basedn, $ldap_host, $ldap_port, $ldap_rdn, $ldap_pass, $ldap_search_dn, $extldap_user_correspondance;
$keyword_firstname = isset($_GET['keyword_firstname']) ? trim(Database::escape_string($_GET['keyword_firstname'])) : '';
$keyword_lastname = isset($_GET['keyword_lastname']) ? trim(Database::escape_string($_GET['keyword_lastname'])) : '';
$keyword_username = isset($_GET['keyword_username']) ? trim(Database::escape_string($_GET['keyword_username'])) : '';
$keyword_type = isset($_GET['keyword_type']) ? Database::escape_string($_GET['keyword_type']) : '';
$ldap_query = [];
if ($keyword_username != "") {
$ldap_query[] = str_replace('%username%', $keyword_username, $ldap_search_dn);
} else {
if ($keyword_lastname != "") {
$ldap_query[] = "(".$extldap_user_correspondance['lastname']."=".$keyword_lastname."*)";
}
if ($keyword_firstname != "") {
$ldap_query[] = "(".$extldap_user_correspondance['firstname']."=".$keyword_firstname."*)";
}
}
if ($keyword_type != "" && $keyword_type != "all") {
$ldap_query[] = "(employeeType=".$keyword_type.")";
}
if (count($ldap_query) > 1) {
$str_query = "(& ";
foreach ($ldap_query as $query) {
$str_query .= " $query";
}
$str_query .= " )";
} else {
$str_query = count($ldap_query) > 0 ? $ldap_query[0] : null;
}
$ds = ldap_connect($ldap_host, $ldap_port);
ldap_set_version($ds);
if ($ds && count($ldap_query) > 0) {
$r = false;
$res = ldap_handle_bind($ds, $r);
//$sr = ldap_search($ds, "ou=test-ou,$ldap_basedn", $str_query);
$sr = ldap_search($ds, $ldap_basedn, $str_query);
//echo "Le nombre de resultats est : ".ldap_count_entries($ds,$sr)."<p>";
$info = ldap_get_entries($ds, $sr);
return $info;
} else {
if (count($ldap_query) != 0) {
echo Display::return_message(get_lang('LDAPConnectionError'), 'error');
}
return [];
}
}
/**
* Get the total number of users on the platform.
*
* @see SortableTable#get_total_number_of_items()
*
* @author Mustapha Alouani
*/
function ldap_get_number_of_users()
{
$info = ldap_get_users();
if (count($info) > 0) {
return $info['count'];
} else {
return 0;
}
}
/**
* Get the users to display on the current page.
*
* @see SortableTable#get_table_data($from)
*
* @author Mustapha Alouani
*/
function ldap_get_user_data($from, $number_of_items, $column, $direction)
{
global $extldap_user_correspondance;
$users = [];
$is_western_name_order = api_is_western_name_order();
if (isset($_GET['submit'])) {
$info = ldap_get_users();
if ($info['count'] > 0) {
for ($key = 0; $key < $info["count"]; $key++) {
$user = [];
// Get uid from dn
//YW: this might be a variation between LDAP 2 and LDAP 3, but in LDAP 3, the uid is in
//the corresponding index of the array
//$dn_array=ldap_explode_dn($info[$key]["dn"],1);
//$user[] = $dn_array[0]; // uid is first key
//$user[] = $dn_array[0]; // uid is first key
$user[] = $info[$key][$extldap_user_correspondance['username']][0];
$user[] = $info[$key][$extldap_user_correspondance['username']][0];
if ($is_western_name_order) {
$user[] = api_convert_encoding($info[$key][$extldap_user_correspondance['firstname']][0], api_get_system_encoding(), 'UTF-8');
$user[] = api_convert_encoding($info[$key][$extldap_user_correspondance['lastname']][0], api_get_system_encoding(), 'UTF-8');
} else {
$user[] = api_convert_encoding($info[$key][$extldap_user_correspondance['firstname']][0], api_get_system_encoding(), 'UTF-8');
$user[] = api_convert_encoding($info[$key][$extldap_user_correspondance['lastname']][0], api_get_system_encoding(), 'UTF-8');
}
$user[] = $info[$key]['mail'][0];
$user[] = $info[$key][$extldap_user_correspondance['username']][0];
$users[] = $user;
}
} else {
echo Display::return_message(get_lang('NoUser'), 'error');
}
}
return $users;
}
/**
* Build the modify-column of the table.
*
* @param int $user_id The user id
* @param string $url_params
*
* @return string Some HTML-code with modify-buttons
*
* @author Mustapha Alouani
*/
function modify_filter($user_id, $url_params, $row)
{
$query_string = "id[]=".$row[0];
if (!empty($_GET['id_session'])) {
$query_string .= '&amp;id_session='.Security::remove_XSS($_GET['id_session']);
}
$icon = '';
if (UserManager::is_username_available($user_id)) {
$icon = 'invitation_friend.png';
} else {
$icon = 'reload.png';
}
//$url_params_id="id=".$row[0];
$result = '<a href="ldap_users_list.php?action=add_user&amp;user_id='.$user_id.'&amp;'.$query_string.'&amp;sec_token='.Security::getTokenFromSession().'" onclick="javascript:if(!confirm('."'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"), ENT_QUOTES, api_get_system_encoding()))."'".')) return false;">'.Display::return_icon($icon, get_lang('AddUsers')).'</a>';
return $result;
}
/**
* Adds a user to the Chamilo database or updates its data.
*
* @param string username (and uid inside LDAP)
*
* @author Mustapha Alouani
*/
function ldap_add_user($login)
{
if ($ldap_user = extldap_authenticate($login, 'nopass', true)) {
return extldap_add_user_by_array($ldap_user);
}
}
function ldap_add_user_by_array($data, $update_if_exists = true)
{
$lastname = api_convert_encoding($data['sn'][0], api_get_system_encoding(), 'UTF-8');
$firstname = api_convert_encoding($data['cn'][0], api_get_system_encoding(), 'UTF-8');
$email = $data['mail'][0];
// Get uid from dn
$dn_array = ldap_explode_dn($data['dn'], 1);
$username = $dn_array[0]; // uid is first key
$outab[] = $data['edupersonprimaryaffiliation'][0]; // Here, "student"
//$val = ldap_get_values_len($ds, $entry, "userPassword");
//$val = ldap_get_values_len($ds, $data, "userPassword");
//$password = $val[0];
// TODO the password, if encrypted at the source, will be encrypted twice, which makes it useless. Try to fix that.
$password = $data['userPassword'][0];
$structure = $data['edupersonprimaryorgunitdn'][0];
$array_structure = explode(",", $structure);
$array_val = explode("=", $array_structure[0]);
$etape = $array_val[1];
$array_val = explode("=", $array_structure[1]);
$annee = $array_val[1];
// To ease management, we add the step-year (etape-annee) code
$official_code = $etape."-".$annee;
$auth_source = 'ldap';
// No expiration date for students (recover from LDAP's shadow expiry)
$expiration_date = '';
$active = 1;
if (empty($status)) {
$status = 5;
}
if (empty($phone)) {
$phone = '';
}
if (empty($picture_uri)) {
$picture_uri = '';
}
// Adding user
$user_id = 0;
if (UserManager::is_username_available($username)) {
$user_id = UserManager::create_user(
$firstname,
$lastname,
$status,
$email,
$username,
$password,
$official_code,
api_get_setting('platformLanguage'),
$phone,
$picture_uri,
$auth_source,
$expiration_date,
$active
);
} else {
if ($update_if_exists) {
$user = api_get_user_info($username);
$user_id = $user['user_id'];
UserManager::update_user(
$user_id,
$firstname,
$lastname,
$username,
null,
null,
$email,
$status,
$official_code,
$phone,
$picture_uri,
$expiration_date,
$active
);
}
}
return $user_id;
}
/**
* Adds a list of users to one session.
*
* @param array Array of user ids
* @param string Course code
*/
function ldap_add_user_to_session($UserList, $id_session)
{
// Database Table Definitions
$tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
$id_session = (int) $id_session;
// Once users are imported in the users base, we can assign them to the session
$result = Database::query("SELECT c_id FROM $tbl_session_rel_course WHERE session_id ='$id_session'");
$CourseList = [];
while ($row = Database::fetch_array($result)) {
$CourseList[] = $row['c_id'];
}
SessionManager::insertUsersInCourses($UserList, $CourseList, $id_session);
}
/**
* Synchronize users from the configured LDAP connection (in auth.conf.php). If
* configured to disable old users,.
*
* @param bool $disableOldUsers Whether to disable users who have disappeared from LDAP (true) or just leave them be (default: false)
* @param bool $deleteStudents Go one step further and delete completely students missing from LDAP
* @param bool $deleteTeachers Go even one step further and also delete completely teachers missing from LDAP
*
* @return int Total number of users added (not counting possible removals)
*/
function syncro_users(
$disableOldUsers = false,
$deleteStudents = false,
$deleteTeachers = false
) {
global $ldap_basedn, $ldap_host, $ldap_port, $ldap_rdn, $ldap_pass, $ldap_search_dn, $debug;
$i = 0;
if ($debug) {
error_log('Connecting... ('.__FUNCTION__.')');
}
$ldapConnect = ldap_connect($ldap_host, $ldap_port);
ldap_set_version($ldapConnect);
if ($ldapConnect) {
if ($debug) {
error_log('Connected to LDAP server successfully! Binding... ('.__FUNCTION__.')');
}
$ldapBind = false;
$ldapBindRes = ldap_handle_bind($ldapConnect, $ldapBind);
if ($ldapBindRes) {
if ($debug) {
error_log('Bind successful! Searching for uid in LDAP DC: '.$ldap_search_dn);
}
$allUserQuery = "uid=*";
if (!empty($ldap_search_dn)) {
$sr = ldap_search($ldapConnect, $ldap_search_dn, $allUserQuery);
} else {
//OLD: $sr=ldap_search($ldapconnect,"dc=rug, dc=ac, dc=be", "uid=$login");
$sr = ldap_search($ldapConnect, $ldap_basedn, $allUserQuery);
}
if ($debug) {
error_log('Entries returned: '.ldap_count_entries($ldapConnect, $sr));
}
$info = ldap_get_entries($ldapConnect, $sr);
for ($key = 0; $key < $info['count']; $key++) {
$user_id = ldap_add_user_by_array($info[$key], false);
if ($user_id) {
if ($debug) {
error_log('User #'.$user_id.' created from LDAP');
}
$i++;
} else {
if ($debug) {
error_log('User '.$info[$key]['sn'][0].' ('.$info[$key]['mail'][0].') could not be created');
}
}
}
if ($disableOldUsers === true) {
if ($debug) {
error_log('Disable mode selected in '.__FUNCTION__);
if ($deleteStudents) {
error_log('...with complete deletion of users if disabled');
}
}
// Get a big array of all user IDs, usernames only if they are
// registered as auth_source = 'ldap'
// This array will take about 60 bytes per user in memory, so
// having 100K users should only take a few (6?) MB and will
// highly reduce the number of DB queries
$usersDBShortList = [];
$usersLDAPShortList = [];
$sql = "SELECT id, username, status FROM user WHERE auth_source = 'ldap' ORDER BY username";
$res = Database::query($sql);
if ($res !== false) {
// First build a list of users present in LDAP
for ($key = 0; $key < $info['count']; $key++) {
$dn_array = ldap_explode_dn($info[$key]['dn'], 1);
$usersLDAPShortList[$dn_array[0]] = 1;
}
// Go through all 'extldap' users. For any that cannot
// be found in the LDAP list, disable
while ($row = Database::fetch_assoc($res)) {
$usersDBShortList[$row['username']] = $row['id'];
// If any of those users is NOT in LDAP, disable or remove
if (empty($usersLDAPShortList[$row['username']])) {
if ($deleteStudents === true && $row['status'] == 5) {
UserManager::delete_user($usersDBShortList[$row['username']]);
if ($debug) {
error_log('Student '.$row['username'].' removed from Chamilo');
}
} elseif ($deleteTeachers === true && $row['status'] == 1) {
UserManager::delete_user($usersDBShortList[$row['username']]);
if ($debug) {
error_log('Teacher '.$row['username'].' removed from Chamilo');
}
} else {
UserManager::disable($usersDBShortList[$row['username']]);
if ($debug) {
error_log('User '.$row['username'].' disabled in Chamilo');
}
}
}
}
}
}
if ($debug) {
error_log('Data for '.$info['count'].' items processed');
}
//echo "Data for ".$info["count"]." items returned:<p>";
} else {
error_log('Could not bind to LDAP server');
}
ldap_close($ldapConnect);
} else {
error_log('Could not connect to LDAP server');
}
error_log('Ended execution of function '.__FUNCTION__);
}

View File

@@ -0,0 +1,6 @@
<html>
<head>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,49 @@
<?php
/* For licensing terms, see /license.txt */
/**
* LDAP settings
* In the older code, there was a distinction between
* the teacher and student LDAP server. Later I decided not
* to make this distinction. However, it could be built in
* in the future but then perhaps in a more general way.
*
* Originally, Thomas and I agreed to store all settings in one file
* (configuration.php) to make it easier for claroline admins to make changes.
* Since October 2003, this changed: the include directory has been
* changed to be called "inc", and all tools should have their own file(s).
*
* This file "ldap_var.inc.php" was already used by the
* older french authentification functions. I have moved the new
* variables from the configuration.php to here as well.
*
* @author Roan Embrechts
*
* @package chamilo.auth.ldap
*/
/**
* Configuration settings.
*/
// your ldap server
$ldap_host = $extldap_config['host'][0];
// your ldap server's port number
$ldap_port = @$extldap_config['port'] ?: null;
//domain
$ldap_basedn = $extldap_config['base_dn'];
//search term for students
$ldap_search_dn = $extldap_config['user_search'];
//additional server params for use of replica in case of problems
$ldap_host2 = count($extldap_config['host']) > 1 ? $extldap_config['host'][1] : null;
$ldap_port2 = $extldap_config['port'];
//protocol version - set to 3 for LDAP 3
$ldap_version = $extldap_config['protocol_version'];
//non-anonymous LDAP mode
$ldap_rdn = $extldap_config['admin_dn'];
$ldap_pass = $extldap_config['admin_password'];
if (api_get_configuration_value('ldap_encrypt_admin_password')) {
$ldap_pass = api_decrypt_ldap_password($extldap_config['admin_password']);
}
$ldap_pass_placeholder = "PLACEHOLDER";

44
main/auth/ldap/login.php Normal file
View File

@@ -0,0 +1,44 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* Users trying to login, who already exist in the Chamilo database
* and have ldap as authentication type get verified here.
*
* @author Roan Embrechts
*
* @package chamilo.auth.ldap
*/
/**
* An external authentification module needs to set
* - $loginFailed
* - $uidReset
* - $_user['user_id']
* - register the $_user['user_id'] in the session.
*
* As the LDAP code shows, this is not as difficult as you might think.
* LDAP authentification module
* this calls the loginWithLdap function
* from the LDAP library, and sets a few
* variables based on the result.
*/
//require_once('../../inc/global.inc.php'); - this script should be loaded by the /index.php script anyway, so global is already loaded
require_once 'authldap.php';
$loginLdapSucces = ldap_login($login, $password);
if ($loginLdapSucces) {
$loginFailed = false;
$uidReset = true;
$_user['user_id'] = $uData['user_id'];
Session::write('_uid', $_uid);
// Jand: copied from event_login in events.lib.php to enable login statistics:
Event::eventLogin($uData['user_id']);
} else {
$loginFailed = true;
unset($_user['user_id']);
$uidReset = false;
}

View File

@@ -0,0 +1,36 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Users trying to login, who do not yet exist in the Chamilo database,
* can be added by this script which tries to retrieve ldap information
* about them.
*
* @author Roan Embrechts
*
* @package chamilo.auth.ldap
*/
/**
* when a user does not exist yet in dokeos,
* but he or she does exist in the LDAP,
* we add him to the dokeos database.
*/
//require_once('../../inc/global.inc.php'); - this script should be loaded by the /index.php script anyway, so global is already loaded
require_once 'authldap.php';
$ldap_login_success = ldap_login($login, $password);
if ($ldap_login_success) {
//error_log('Found user '.$login.' on LDAP server',0);
/*
In here, we know that
- the user does not exist in dokeos
- the users login and password are correct
*/
$info_array = ldap_find_user_info($login);
ldap_put_user_info_locally($login, $info_array);
} else {
//error_log('Could not find '.$login.' on LDAP server',0);
$loginFailed = true;
unset($_user['user_id']);
$uidReset = false;
}

View File

@@ -0,0 +1,6 @@
<?php
require_once '../../inc/global.inc.php';
require_once 'authldap.php';
syncro_users();

186
main/auth/lostPassword.php Normal file
View File

@@ -0,0 +1,186 @@
<?php
/* For licensing terms, see /license.txt */
/**
* SCRIPT PURPOSE :.
*
* This script allows users to retrieve the password of their profile(s)
* on the basis of their e-mail address. The password is send via email
* to the user.
*
* Special case : If the password are encrypted in the database, we have
* to generate a new one.
*
* @todo refactor, move relevant functions to code libraries
*/
require_once __DIR__.'/../inc/global.inc.php';
// Custom pages
// Had to move the form handling in here, because otherwise there would
// already be some display output.
// Forbidden to retrieve the lost password
if (api_get_setting('allow_lostpassword') === 'false') {
api_not_allowed(true);
}
$reset = Request::get('reset');
$userId = Request::get('id');
$this_section = SECTION_CAMPUS;
$tool_name = get_lang('LostPassword');
if ($reset && $userId) {
$messageText = Login::reset_password($reset, $userId, true);
Display::addFlash(
Display::return_message($messageText, 'info', false)
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$form = new FormValidator('lost_password', 'post', '', '', [], FormValidator::LAYOUT_GRID);
$form->addHeader($tool_name);
$form->addText(
'user',
[
get_lang('LoginOrEmailAddress'),
get_lang('EnterEmailUserAndWellSendYouPassword'),
],
true
);
$captcha = api_get_setting('allow_captcha');
$allowCaptcha = $captcha === 'true';
if ($allowCaptcha) {
$ajax = api_get_path(WEB_AJAX_PATH).'form.ajax.php?a=get_captcha';
$options = [
'width' => 220,
'height' => 90,
'callback' => $ajax.'&var='.basename(__FILE__, '.php'),
'sessionVar' => basename(__FILE__, '.php'),
'imageOptions' => [
'font_size' => 20,
'font_path' => api_get_path(SYS_FONTS_PATH).'opensans/',
'font_file' => 'OpenSans-Regular.ttf',
//'output' => 'gif'
],
];
$form->setLayout('inline');
$captcha_question = $form->addElement(
'CAPTCHA_Image',
'captcha_question',
'',
$options
);
$form->addElement('static', null, null, get_lang('ClickOnTheImageForANewOne'));
$form->addElement('text', 'captcha', get_lang('EnterTheLettersYouSee'), ['size' => 40]);
$form->addRule('captcha', get_lang('EnterTheCharactersYouReadInTheImage'), 'required', null, 'client');
$form->addRule('captcha', get_lang('TheTextYouEnteredDoesNotMatchThePicture'), 'CAPTCHA', $captcha_question);
}
$form->addButtonSend(get_lang('Send'), 'submit', false, [], 'btn-block', null);
if ($form->validate()) {
$values = $form->exportValues();
$user = Login::get_user_accounts_by_username($values['user']);
if (!$user) {
$messageText = get_lang('NoUserAccountWithThisEmailAddress');
if (CustomPages::enabled() && CustomPages::exists(CustomPages::LOST_PASSWORD)) {
CustomPages::display(
CustomPages::LOST_PASSWORD,
['info' => $messageText]
);
exit;
}
Display::addFlash(
Display::return_message($messageText, 'error', false)
);
header('Location: '.api_get_self());
exit;
}
if ('true' === api_get_plugin_setting('whispeakauth', WhispeakAuthPlugin::SETTING_ENABLE)) {
WhispeakAuthPlugin::deleteEnrollment($user['uid']);
}
$passwordEncryption = api_get_configuration_value('password_encryption');
if ($passwordEncryption === 'none') {
$messageText = Login::send_password_to_user($user, true);
if (CustomPages::enabled() && CustomPages::exists(CustomPages::INDEX_UNLOGGED)) {
CustomPages::display(
CustomPages::INDEX_UNLOGGED,
['info' => $messageText]
);
exit;
}
Display::addFlash(
Display::return_message($messageText, 'info', false)
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
if ($user['auth_source'] == 'extldap') {
Display::addFlash(
Display::return_message(get_lang('CouldNotResetPasswordBecauseLDAP'), 'info', false)
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$userResetPasswordSetting = api_get_setting('user_reset_password');
if ($userResetPasswordSetting === 'true') {
$userObj = api_get_user_entity($user['uid']);
Login::sendResetEmail($userObj);
if (CustomPages::enabled() && CustomPages::exists(CustomPages::INDEX_UNLOGGED)) {
CustomPages::display(
CustomPages::INDEX_UNLOGGED,
['info' => get_lang('CheckYourEmailAndFollowInstructions')]
);
exit;
}
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$messageText = Login::handle_encrypted_password($user, true);
if (CustomPages::enabled() && CustomPages::exists(CustomPages::INDEX_UNLOGGED)) {
CustomPages::display(
CustomPages::INDEX_UNLOGGED,
['info' => $messageText]
);
exit;
}
Display::addFlash(
Display::return_message($messageText, 'info', false)
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
if (CustomPages::enabled() && CustomPages::exists(CustomPages::LOST_PASSWORD)) {
CustomPages::display(
CustomPages::LOST_PASSWORD,
['form' => $form->returnForm()]
);
exit;
}
$tpl = new Template(null);
$tpl->assign('content', $form->toHtml());
$tpl->display_one_col_template();

145
main/auth/my_progress.php Normal file
View File

@@ -0,0 +1,145 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Reporting page on the user's own progress.
*/
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
api_block_anonymous_users();
if (api_get_configuration_value('block_my_progress_page')) {
api_not_allowed(true);
}
$this_section = SECTION_TRACKING;
$nameTools = get_lang('MyProgress');
$htmlHeadXtra[] = api_get_js('jquery.timelinr-0.9.54.js');
$htmlHeadXtra[] = "<script>
$(function() {
$().timelinr({
containerDiv: '#my_timeline',
autoPlayPause: 2000
})
});
</script>";
$pluginCalendar = api_get_plugin_setting('learning_calendar', 'enabled') === 'true';
if ($pluginCalendar) {
$plugin = LearningCalendarPlugin::create();
$plugin->setJavaScript($htmlHeadXtra);
}
$user_id = api_get_user_id();
$courseUserList = CourseManager::get_courses_list_by_user_id($user_id);
$dates = $issues = '';
$sessionId = isset($_GET['session_id']) ? (int) $_GET['session_id'] : 0;
$courseCode = isset($_GET['course']) ? Security::remove_XSS($_GET['course']) : null;
$showGraph = false === api_get_configuration_value('hide_session_graph_in_my_progress');
$isAllowedToEdit = api_is_allowed_to_edit();
if (!empty($courseUserList)) {
$items = MySpace::get_connections_from_course_list(
$user_id,
$courseUserList
);
$first = null;
$last = null;
$last_item = count($items);
$count = 1;
foreach ($items as $result) {
$login = $result['login'];
$courseId = $result['c_id'];
$courseInfo = api_get_course_info_by_id($courseId);
if ($count == 1) {
$first = '<a href="#'.$login.'">'.get_lang('First').'</a>';
}
if ($count == $last_item) {
$last = '<a href="#'.$login.'">'.get_lang('Last').'</a>';
}
$course_info = api_get_course_info_by_id($result['c_id']);
$course_image = '<img src="'.$course_info['course_image_large'].'">';
$dates .= '<li><a href="#'.$login.'">'.api_convert_and_format_date($login, DATE_FORMAT_SHORT).'</a></li>';
$entered = sprintf(
get_lang('YouHaveEnteredTheCourseXInY'),
'" '.$courseInfo['name'].' "',
api_convert_and_format_date($login, DATE_TIME_FORMAT_LONG)
);
$issues .= '<li id ="'.$login.'">
<div class="img-course">'.$course_image.'</div>
<div class="text-course">
<p>'.$entered.'</p>
</div>
</li>';
$count++;
}
}
$content = Tracking::showUserProgress(
$user_id,
$sessionId,
'',
true,
true,
false,
$showGraph
);
$showAllSessionCourses = api_get_configuration_value('my_progress_session_show_all_courses');
if ($showAllSessionCourses && !empty($sessionId) && empty($courseCode)) {
$userSessionCourses = UserManager::get_courses_list_by_session($user_id, $sessionId);
foreach ($userSessionCourses as $userSessionCourse) {
$content .= Tracking::show_course_detail(
$user_id,
$userSessionCourse['course_code'],
$sessionId,
$isAllowedToEdit
);
}
} else {
$content .= Tracking::show_course_detail($user_id, $courseCode, $sessionId, $isAllowedToEdit);
}
if (!empty($dates)) {
$content .= Display::page_subheader(get_lang('Timeline'));
$content .= '
<div class="row">
<div class="col-md-12">
<div id="my_timeline">
<ul id="dates">'.$dates.'</ul>
<ul id="issues">'.$issues.'</ul>
<div id="grad_left"></div>
<div id="grad_right"></div>
<a href="#" id="prev"></a>
<a href="#" id="next"></a>
</div>
</div>
</div>
';
}
if (api_get_configuration_value('private_messages_about_user_visible_to_user') === true) {
$allowMessages = api_get_configuration_value('private_messages_about_user');
if ($allowMessages === true) {
$content .= Display::page_subheader2(get_lang('Messages'));
$content .= MessageManager::getMessagesAboutUserToString(api_get_user_info());
}
}
$message = null;
if (empty($content)) {
$message = Display::return_message(get_lang('NoDataAvailable'), 'warning');
}
$tpl = new Template($nameTools);
$tpl->assign('message', $message);
$tpl->assign('content', $content);
$tpl->display_one_col_template();

View File

@@ -0,0 +1,27 @@
<?php
/* For license terms, see /license.txt */
use OneLogin\Saml2\Settings;
require_once '../../../main/inc/global.inc.php';
/**
* SAML Metadata view.
*/
require_once 'settings.php';
try {
// Now we only validate SP settings
$settings = new Settings($settingsInfo, true);
$metadata = $settings->getSPMetadata();
$errors = $settings->validateMetadata($metadata);
if (empty($errors)) {
header('Content-Type: text/xml');
echo $metadata;
} else {
throw new OneLogin\Saml2\Error('Invalid SP metadata: '.implode(', ', $errors), OneLogin\Saml2\Error::METADATA_SP_INVALID);
}
} catch (Exception $e) {
echo $e->getMessage();
}

15
main/auth/okn/report.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
require_once '../../../main/inc/global.inc.php';
if (isset($_REQUEST['key']) && isset($_REQUEST['username'])) {
$securityKey = api_get_configuration_value('security_key');
$result = api_is_valid_secret_key($_REQUEST['key'], $securityKey);
if ($result) {
$userInfo = api_get_user_info_from_username($_REQUEST['username']);
if ($userInfo) {
$result = Tracking::getCourseLpProgress($userInfo['id'], 0);
echo json_encode($result);
}
}
}

View File

@@ -0,0 +1,157 @@
<?php
exit;
require_once '../../../main/inc/global.inc.php';
$spBaseUrl = api_get_path(WEB_CODE_PATH).'auth/okn/';
$url = 'https://example.es/';
$realm = 'master';
$path = '/path';
//$certificate = file_get_contents($path);
$settingsInfo = [
'course_list' => ['ABC', 'CDE'],
'strict' => false,
'debug' => true,
'sp' => [
'entityId' => $spBaseUrl.'metadata.php',
'assertionConsumerService' => [
'url' => $spBaseUrl.'start.php?acs',
],
'singleLogoutService' => [
'url' => $spBaseUrl.'start.php?sls',
],
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
],
'idp' => [
'entityId' => $url.'auth/realms/'.$realm, // Example http://localhost:8080/auth/realms/master
'singleSignOnService' => [
'url' => $url.'auth/realms/'.$realm.'/protocol/saml', // example http://localhost:8080/auth/realms/master/protocol/saml
],
'singleLogoutService' => [
'url' => $url.'auth/realms/'.$realm.'/protocol/saml', // example http://localhost:8080/auth/realms/master/protocol/saml
],
//'x509cert' => $certificate,
],
];
// advanced settings
//
//
//// Compression settings
//'compress' => array (
// 'requests' => true,
// 'responses' => true
//),
// // Security settings
// 'security' => array (
//
// /** signatures and encryptions offered */
//
// // Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
// // will be encrypted.
// 'nameIdEncrypted' => false,
//
// // Indicates whether the <samlp:AuthnRequest> messages sent by this SP
// // will be signed. [Metadata of the SP will offer this info]
// 'authnRequestsSigned' => false,
//
// // Indicates whether the <samlp:logoutRequest> messages sent by this SP
// // will be signed.
// 'logoutRequestSigned' => false,
//
// // Indicates whether the <samlp:logoutResponse> messages sent by this SP
// // will be signed.
// 'logoutResponseSigned' => false,
//
// /* Sign the Metadata
// False || True (use sp certs) || array (
// keyFileName => 'metadata.key',
// certFileName => 'metadata.crt'
// )
// */
// 'signMetadata' => false,
//
// /** signatures and encryptions required **/
//
// // Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest>
// // and <samlp:LogoutResponse> elements received by this SP to be signed.
// 'wantMessagesSigned' => false,
//
// // Indicates a requirement for the <saml:Assertion> elements received by
// // this SP to be encrypted.
// 'wantAssertionsEncrypted' => false,
//
// // Indicates a requirement for the <saml:Assertion> elements received by
// // this SP to be signed. [Metadata of the SP will offer this info]
// 'wantAssertionsSigned' => false,
//
// // Indicates a requirement for the NameID element on the SAMLResponse
// // received by this SP to be present.
// 'wantNameId' => true,
//
// // Indicates a requirement for the NameID received by
// // this SP to be encrypted.
// 'wantNameIdEncrypted' => false,
//
// // Authentication context.
// // Set to false and no AuthContext will be sent in the AuthNRequest.
// // Set true or don't present this parameter and you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'.
// // Set an array with the possible auth context values: array ('urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509').
// 'requestedAuthnContext' => true,
//
// // Indicates if the SP will validate all received xmls.
// // (In order to validate the xml, 'strict' and 'wantXMLValidation' must be true).
// 'wantXMLValidation' => true,
//
// // If true, SAMLResponses with an empty value at its Destination
// // attribute will not be rejected for this fact.
// 'relaxDestinationValidation' => false,
//
// // Algorithm that the toolkit will use on signing process. Options:
// // 'http://www.w3.org/2000/09/xmldsig#rsa-sha1'
// // 'http://www.w3.org/2000/09/xmldsig#dsa-sha1'
// // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
// // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
// // 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
// // Notice that sha1 is a deprecated algorithm and should not be used
// 'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
//
// // Algorithm that the toolkit will use on digest process. Options:
// // 'http://www.w3.org/2000/09/xmldsig#sha1'
// // 'http://www.w3.org/2001/04/xmlenc#sha256'
// // 'http://www.w3.org/2001/04/xmldsig-more#sha384'
// // 'http://www.w3.org/2001/04/xmlenc#sha512'
// // Notice that sha1 is a deprecated algorithm and should not be used
// 'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256',
//
// // ADFS URL-Encodes SAML data as lowercase, and the toolkit by default uses
// // uppercase. Turn it True for ADFS compatibility on signature verification
// 'lowercaseUrlencoding' => false,
//),
//
// // Contact information template, it is recommended to supply a
// // technical and support contacts.
// 'contactPerson' => array (
// 'technical' => array (
// 'givenName' => 'example',
// 'emailAddress' => 'test@example.org'
// ),
// 'support' => array (
// 'givenName' => 'example',
// 'emailAddress' => 'test@example.org'
// ),
//),
//
// // Organization information template, the info in en_US lang is
// // recomended, add more if required.
// 'organization' => array (
// 'en-US' => array(
// 'name' => 'chamilo',
// 'displayname' => 'chamilo',
// 'url' => 'chamilo.org'
// ),
//),

254
main/auth/okn/start.php Normal file
View File

@@ -0,0 +1,254 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
use OneLogin\Saml2\Auth;
use OneLogin\Saml2\AuthnRequest;
use OneLogin\Saml2\Settings;
require_once '../../../main/inc/global.inc.php';
// Create a settings.dist.php
if (file_exists('settings.php')) {
require_once 'settings.php';
} else {
$message = '';
if (api_is_platform_admin()) {
$message = 'Create a settings.php';
}
api_not_allowed(true, $message);
}
$content = '';
$auth = new Auth($settingsInfo);
$settings = new Settings($settingsInfo);
$authRequest = new AuthnRequest($settings);
$samlRequest = $authRequest->getRequest(true);
$idpData = $settings->getIdPData();
if (isset($_GET['email']) || isset($_GET['email_bis'])) {
$auth->login();
// If AuthNRequest ID need to be saved in order to later validate it, do instead
/*$ssoBuiltUrl = $auth->login(null, [], false, false, true);
$_SESSION['AuthNRequestID'] = $auth->getLastRequestID();
header('Pragma: no-cache');
header('Cache-Control: no-cache, must-revalidate');
header('Location: ' . $ssoBuiltUrl);
exit();*/
} elseif (isset($_GET['slo'])) {
$returnTo = null;
$parameters = [];
$nameId = Session::read('samlNameId');
$sessionIndex = Session::read('samlSessionIndex');
$nameIdFormat = Session::read('samlNameIdFormat');
$auth->logout($returnTo, $parameters, $nameId, $sessionIndex, false, $nameIdFormat);
} elseif (isset($_GET['acs'])) {
$requestID = Session::read('AuthNRequestID');
$auth->processResponse($requestID);
$errors = $auth->getErrors();
if (!empty($errors)) {
$content .= '<p>'.implode(', ', $errors).'</p>';
}
if (!$auth->isAuthenticated()) {
api_not_allowed(true, $content.'<p>Not authenticated</p>');
exit;
}
$attributes = $auth->getAttributes();
$valueList = [];
$attributeNameList = [
'email',
'username',
'firstname',
'lastname1',
'lastname2',
'courses',
];
// Check normal params
foreach ($attributeNameList as $name) {
if (isset($attributes[$name]) && !empty($attributes[$name])) {
$valueList[$name] = $attributes[$name];
}
}
// Check bis params
foreach ($attributeNameList as $name) {
$bisName = $name.'_bis';
if (isset($attributes[$bisName]) && !empty($attributes[$bisName])) {
$valueList[$name] = $attributes[$bisName];
}
}
$attributes = $valueList;
if (!isset($attributes['email']) ||
!isset($attributes['firstname']) ||
!isset($attributes['lastname1']) ||
!isset($attributes['lastname2']) ||
!isset($attributes['username'])
) {
echo 'Not enough parameters, current values: ';
echo '<pre>';
var_dump($auth->getAttributes());
exit;
}
foreach ($attributes as &$attribute) {
$attribute = implode('', $attribute);
}
$username = $attributes['username'];
$userInfo = api_get_user_info_from_username($username);
$userId = null;
if (empty($userInfo)) {
$lastName = $attributes['lastname1'].' '.$attributes['lastname2'];
$userId = UserManager::create_user(
$attributes['firstname'],
$lastName,
STUDENT,
$attributes['email'],
$username,
'',
'',
'',
'',
'',
'okn'
);
if ($userId) {
$userInfo = api_get_user_info($userId);
} else {
echo "Error cannot create user: $username";
}
} else {
// Only load users that were created using this method.
if ($userInfo['auth_source'] === 'okn') {
$userId = $userInfo['user_id'];
} else {
echo "Error cannot handle user $username, because it was not created by okn";
exit;
}
}
if (!empty($userId)) {
$courseCode = null;
if (isset($settingsInfo['course_list']) && !empty($settingsInfo['course_list'])) {
foreach ($settingsInfo['course_list'] as $courseCode) {
CourseManager::subscribeUser($userId, $courseCode, STUDENT, 0, 0, false);
}
}
if (isset($attributes['courses']) && !empty($attributes['courses'])) {
$courses = explode(',', $attributes['courses']);
$firstCourseCode = '';
if (!empty($courses)) {
$counter = 1;
foreach ($courses as $course) {
$courseInfo = api_get_course_info($course);
if ($courseInfo) {
if ($counter == 1) {
$firstCourseCode = $course;
}
CourseManager::subscribeUser($userId, $courseInfo['code'], STUDENT, 0, 0, false);
}
$counter++;
}
$courseCode = $firstCourseCode;
}
}
// Clean flash messages
Session::write('flash_messages', '');
// Set chamilo sessions
Session::write('samlUserdata', $auth->getAttributes());
Session::write('samlNameId', $auth->getNameId());
Session::write('samlNameIdFormat', $auth->getNameIdFormat());
Session::write('samlSessionIndex', $auth->getSessionIndex());
Session::erase('AuthNRequestID');
// Filling session variables with new data
Session::write('_uid', $userId);
Session::write('_user', $userInfo);
Session::write('is_platformAdmin', false);
Session::write('is_allowedCreateCourse', false);
Event::eventLogin($userId);
if (!empty($courseCode)) {
$courseInfo = api_get_course_info($courseCode);
header('Location: '.$courseInfo['course_public_url']);
exit;
}
header('Location: '.api_get_path(WEB_PATH).'user_portal.php');
exit;
} else {
echo 'User not found';
}
exit;
if (!empty($userId)) {
} else {
Display::addFlash(Display::return_message(get_lang('InvalidId')));
}
/*if (isset($_POST['RelayState']) && \OneLogin\Saml2\Utils::getSelfURL() != $_POST['RelayState']) {
$auth->redirectTo($_POST['RelayState']);
}*/
header('Location: '.api_get_path(WEB_PATH));
exit;
} elseif (isset($_GET['sls'])) {
$requestID = Session::read('LogoutRequestID');
$auth->processSLO(false, $requestID);
$errors = $auth->getErrors();
if (empty($errors)) {
Session::erase('samlNameId');
Session::erase('samlSessionIndex');
Session::erase('samlNameIdFormat');
Session::erase('samlUserdata');
Session::erase('AuthNRequestID');
Session::erase('LogoutRequestID');
Display::addFlash(Display::return_message('Sucessfully logged out'));
header('Location: '.api_get_path(WEB_PATH));
exit;
} else {
api_not_allowed(true, implode(', ', $errors));
}
}
$template = new Template('');
if (isset($_SESSION['samlUserdata'])) {
$attributes = Session::read('samlUserdata');
$params = [];
if (!empty($attributes)) {
$content .= 'You have the following attributes:<br>';
$content .= '<table class="table"><thead><th>Name</th><th>Values</th></thead><tbody>';
foreach ($attributes as $attributeName => $attributeValues) {
$content .= '<tr><td>'.htmlentities($attributeName).'</td><td><ul>';
foreach ($attributeValues as $attributeValue) {
$content .= '<li>'.htmlentities($attributeValue).'</li>';
}
$content .= '</ul></td></tr>';
}
$content .= '</tbody></table>';
} else {
$content .= "<p>You don't have any attribute</p>";
}
$content .= '<p><a href="?slo" >Logout</a></p>';
} else {
$content .= '<p><a href="?sso" >Login</a></p>';
//$content .= '<p><a href="?sso2" >Login and access to attrs.php page</a></p>';
}
$template->assign('content', $content);
$template->display_one_col_template();

490
main/auth/openid/login.php Normal file
View File

@@ -0,0 +1,490 @@
<?php
/* For licensing terms, see /license.txt */
/**
* OpenID login method
*
* The OpenID login method relies on authentication servers providing a public
* URL that can confirm the identity of a person, thus avoiding the spread
* use of password transmissions over non-secure lines (for Dokeos, it is a
* good way of avoiding password theft)
* @package chamilo.auth.openid
*/
require_once 'openid.lib.php';
require_once 'xrds.lib.php';
function openid_form(): FormValidator
{
$form = new FormValidator(
'openid_login',
'post',
null,
null,
array('class' => 'form-vertical form_login')
);
$form -> addElement('text', 'openid_url', array(get_lang('OpenIDURL'), Display::url(get_lang('OpenIDWhatIs'), 'main/auth/openid/whatis.php')), array('class' => 'openid_input'));
$form -> addElement('button', 'submit', get_lang('Login'));
$form->applyFilter('openid_url', 'trim');
$form->protect();
return $form;
}
/**
* The initial step of OpenID authentication responsible for the following:
* - Perform discovery on the claimed OpenID.
* - If possible, create an association with the Provider's endpoint.
* - Create the authentication request.
* - Perform the appropriate redirect.
*
* @param $claimed_id The OpenID to authenticate
* @param $return_to The endpoint to return to from the OpenID Provider
*/
function openid_begin($claimed_id, $return_to = '', $form_values = array()) {
$claimed_id = _openid_normalize($claimed_id);
$services = openid_discovery($claimed_id);
if (count($services) == 0) {
echo 'Sorry, that is not a valid OpenID. Please ensure you have spelled your ID correctly.';
return;
}
$op_endpoint = $services[0]['uri'];
// Store the discovered endpoint in the session (so we don't have to rediscover).
$_SESSION['openid_op_endpoint'] = $op_endpoint;
// Store the claimed_id in the session (for handling delegation).
$_SESSION['openid_claimed_id'] = $claimed_id;
// Store the login form values so we can pass them to
// user_exteral_login later.
$_SESSION['openid_user_login_values'] = $form_values;
// If bcmath is present, then create an association
$assoc_handle = '';
if (function_exists('bcadd')) {
$assoc_handle = openid_association($op_endpoint);
}
// Now that there is an association created, move on
// to request authentication from the IdP
$identity = (!empty($services[0]['delegate'])) ? $services[0]['delegate'] : $claimed_id;
if (isset($services[0]['types']) && is_array($services[0]['types']) && in_array(OPENID_NS_2_0 . '/server', $services[0]['types'])) {
$identity = 'http://openid.net/identifier_select/2.0';
}
$authn_request = openid_authentication_request($claimed_id, $identity, $return_to, $assoc_handle, $services[0]['version']);
if ($services[0]['version'] == 2) {
echo openid_redirect($op_endpoint, $authn_request);
} else {
echo openid_redirect_http($op_endpoint, $authn_request);
}
}
/**
* Completes OpenID authentication by validating returned data from the OpenID
* Provider.
* @param array $response Array of returned from the OpenID provider (typically $_REQUEST).
* @return array $response Response values for further processing with $response['status'] set to one of 'success', 'failed' or 'cancel'.
*/
function openid_complete($response) {
// Default to failed response
$response['status'] = 'failed';
if (isset($_SESSION['openid_op_endpoint']) && isset($_SESSION['openid_claimed_id'])) {
_openid_fix_post($response);
$op_endpoint = $_SESSION['openid_op_endpoint'];
$claimed_id = $_SESSION['openid_claimed_id'];
unset($_SESSION['openid_op_endpoint']);
unset($_SESSION['openid_claimed_id']);
if (isset($response['openid.mode'])) {
if ($response['openid.mode'] == 'cancel') {
$response['status'] = 'cancel';
} else {
if (openid_verify_assertion($op_endpoint, $response)) {
$response['openid.identity'] = $claimed_id;
$response['status'] = 'success';
}
}
}
}
return $response;
}
/**
* Perform discovery on a claimed ID to determine the OpenID provider endpoint.
*
* @param $claimed_id The OpenID URL to perform discovery on.
*
* @return Array of services discovered (including OpenID version, endpoint
* URI, etc).
*/
function openid_discovery($claimed_id)
{
$services = array();
$xrds_url = $claimed_id;
if (_openid_is_xri($claimed_id)) {
$xrds_url = 'http://xri.net/' . $claimed_id;
}
$url = @parse_url($xrds_url);
if ($url['scheme'] == 'http' || $url['scheme'] == 'https') {
// For regular URLs, try Yadis resolution first, then HTML-based discovery
$headers = array('Accept' => 'application/xrds+xml');
//TODO
$result = openid_http_request($xrds_url, $headers);
if (!isset($result->error)) {
if (isset($result->headers['Content-Type']) && preg_match("/application\/xrds\+xml/", $result->headers['Content-Type'])) {
// Parse XML document to find URL
$services = xrds_parse($result->data);
} else {
$xrds_url = NULL;
if (isset($result->headers['X-XRDS-Location'])) {
$xrds_url = $result->headers['X-XRDS-Location'];
} else {
// Look for meta http-equiv link in HTML head
$xrds_url = _openid_meta_httpequiv('X-XRDS-Location', $result->data);
}
if (!empty($xrds_url)) {
$headers = array('Accept' => 'application/xrds+xml');
//TODO
$xrds_result = openid_http_request($xrds_url, $headers);
if (!isset($xrds_result->error)) {
$services = xrds_parse($xrds_result->data);
}
}
}
// Check for HTML delegation
if (count($services) == 0) {
// Look for 2.0 links
$uri = _openid_link_href('openid2.provider', $result->data);
$delegate = _openid_link_href('openid2.local_id', $result->data);
$version = 2;
// 1.0 links
if (empty($uri)) {
$uri = _openid_link_href('openid.server', $result->data);
$delegate = _openid_link_href('openid.delegate', $result->data);
$version = 1;
}
if (!empty($uri)) {
$services[] = array('uri' => $uri, 'delegate' => $delegate, 'version' => $version);
}
}
}
}
return $services;
}
/**
* Attempt to create a shared secret with the OpenID Provider.
* @param $op_endpoint URL of the OpenID Provider endpoint.
* @return object $assoc_handle The association handle.
*/
function openid_association($op_endpoint) {
//@todo Remove Old Associations:
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION);
$sql = "DELETE FROM $openid_association
WHERE created + expires_in < '" . api_get_utc_datetime() . "'";
Database::query($sql);
// Check to see if we have an association for this IdP already
$op_endpoint = Database::escape_string($op_endpoint);
$sql = "SELECT assoc_handle
FROM $openid_association
WHERE idp_endpoint_uri = '$op_endpoint'";
$assoc_handle = Database::query($sql);
if (Database::num_rows($assoc_handle) <= 1) {
$mod = OPENID_DH_DEFAULT_MOD;
$gen = OPENID_DH_DEFAULT_GEN;
$r = _openid_dh_rand($mod);
$private = bcadd($r, 1);
$public = bcpowmod($gen, $private, $mod);
// If there is no existing association, then request one
$assoc_request = openid_association_request($public);
$assoc_message = _openid_encode_message(_openid_create_message($assoc_request));
$assoc_headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8');
//TODO
$assoc_result = openid_http_request($op_endpoint, $assoc_headers, 'POST', $assoc_message);
if (isset($assoc_result->error)) {
return FALSE;
}
$assoc_response = _openid_parse_message($assoc_result->data);
if (isset($assoc_response['mode']) && $assoc_response['mode'] == 'error') {
return FALSE;
}
if ($assoc_response['session_type'] == 'DH-SHA1') {
$spub = _openid_dh_base64_to_long($assoc_response['dh_server_public']);
$enc_mac_key = base64_decode($assoc_response['enc_mac_key']);
$shared = bcpowmod($spub, $private, $mod);
$assoc_response['mac_key'] = base64_encode(_openid_dh_xorsecret($shared, $enc_mac_key));
}
//TODO
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION);
Database::query(sprintf("INSERT INTO $openid_association (idp_endpoint_uri, session_type, assoc_handle, assoc_type, expires_in, mac_key, created) VALUES('%s', '%s', '%s', '%s', %d, '%s', %d)", $op_endpoint, $assoc_response['session_type'], $assoc_response['assoc_handle'], $assoc_response['assoc_type'], $assoc_response['expires_in'], $assoc_response['mac_key'], api_get_utc_datetime()));
$assoc_handle = $assoc_response['assoc_handle'];
}
return $assoc_handle;
}
/**
* ?
*/
function openid_association_request($public) {
$request = array(
'openid.ns' => OPENID_NS_2_0,
'openid.mode' => 'associate',
'openid.session_type' => 'DH-SHA1',
'openid.assoc_type' => 'HMAC-SHA1'
);
if ($request['openid.session_type'] == 'DH-SHA1' || $request['openid.session_type'] == 'DH-SHA256') {
$cpub = _openid_dh_long_to_base64($public);
$request['openid.dh_consumer_public'] = $cpub;
}
return $request;
}
/**
*
*/
function openid_authentication_request($claimed_id, $identity, $return_to = '', $assoc_handle = '', $version = 2) {
$realm = ($return_to) ? $return_to : api_get_self();
$ns = ($version == 2) ? OPENID_NS_2_0 : OPENID_NS_1_0;
$request = array(
'openid.ns' => $ns,
'openid.mode' => 'checkid_setup',
'openid.identity' => $identity,
'openid.claimed_id' => $claimed_id,
'openid.assoc_handle' => $assoc_handle,
'openid.return_to' => $return_to,
);
if ($version == 2) {
$request['openid.realm'] = $realm;
} else {
$request['openid.trust_root'] = $realm;
}
// Simple Registration - we don't ask lastname and firstname because the only
// available similar data is "fullname" and we would have to guess where to split
$request['openid.sreg.required'] = 'nickname,email';
$request['openid.ns.sreg'] = "http://openid.net/extensions/sreg/1.1";
//$request = array_merge($request, module_invoke_all('openid', 'request', $request));
//$request = array_merge($request);
return $request;
}
/**
* Attempt to verify the response received from the OpenID Provider.
*
* @param $op_endpoint The OpenID Provider URL.
* @param $response Array of repsonse values from the provider.
*
* @return boolean
*/
function openid_verify_assertion($op_endpoint, $response) {
$valid = FALSE;
//TODO
$openid_association = Database::get_main_table(TABLE_MAIN_OPENID_ASSOCIATION);
$sql = sprintf("SELECT * FROM $openid_association WHERE assoc_handle = '%s'", $response['openid.assoc_handle']);
$res = Database::query($sql);
$association = Database::fetch_object($res);
if ($association && isset($association->session_type)) {
$keys_to_sign = explode(',', $response['openid.signed']);
$self_sig = _openid_signature($association, $response, $keys_to_sign);
if ($self_sig == $response['openid.sig']) {
$valid = TRUE;
} else {
$valid = FALSE;
}
} else {
$request = $response;
$request['openid.mode'] = 'check_authentication';
$message = _openid_create_message($request);
$headers = array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8');
$result = openid_http_request($op_endpoint, $headers, 'POST', _openid_encode_message($message));
if (!isset($result->error)) {
$response = _openid_parse_message($result->data);
if (strtolower(trim($response['is_valid'])) == 'true') {
$valid = TRUE;
} else {
$valid = FALSE;
}
}
}
return $valid;
}
/**
* Make a HTTP request - This function has been copied straight over from Drupal 6 code (drupal_http_request)
* @param string $data
*/
function openid_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) {
$result = new stdClass();
// Parse the URL and make sure we can handle the schema.
$uri = parse_url($url);
switch ($uri['scheme']) {
case 'http':
$port = isset($uri['port']) ? $uri['port'] : 80;
$host = $uri['host'] . ($port != 80 ? ':' . $port : '');
$fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
break;
case 'https':
// Note: Only works for PHP 4.3 compiled with OpenSSL.
$port = isset($uri['port']) ? $uri['port'] : 443;
$host = $uri['host'] . ($port != 443 ? ':' . $port : '');
$fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr, 20);
break;
default:
$result->error = 'invalid schema ' . $uri['scheme'];
return $result;
}
// Make sure the socket opened properly.
if (!$fp) {
// When a network error occurs, we make sure that it is a negative number so
// it can clash with the HTTP status codes.
$result->code = -$errno;
$result->error = trim($errstr);
return $result;
}
// Construct the path to act on.
$path = isset($uri['path']) ? $uri['path'] : '/';
if (isset($uri['query'])) {
$path .= '?' . $uri['query'];
}
// Create HTTP request.
$defaults = array(
// RFC 2616: "non-standard ports MUST, default ports MAY be included".
// We don't add the port to prevent from breaking rewrite rules checking the
// host that do not take into account the port number.
'Host' => "Host: $host",
'User-Agent' => 'User-Agent: Chamilo (+http://www.chamilo.org/)',
'Content-Length' => 'Content-Length: ' . strlen($data)
);
// If the server url has a user then attempt to use basic authentication
if (isset($uri['user'])) {
$defaults['Authorization'] = 'Authorization: Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : ''));
}
foreach ($headers as $header => $value) {
$defaults[$header] = $header . ': ' . $value;
}
$request = $method . ' ' . $path . " HTTP/1.0\r\n";
$request .= implode("\r\n", $defaults);
$request .= "\r\n\r\n";
if ($data) {
$request .= $data . "\r\n";
}
$result->request = $request;
fwrite($fp, $request);
// Fetch response.
$response = '';
while (!feof($fp) && $chunk = fread($fp, 1024)) {
$response .= $chunk;
}
fclose($fp);
// Parse response.
list($split, $result->data) = explode("\r\n\r\n", $response, 2);
$split = preg_split("/\r\n|\n|\r/", $split);
list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3);
$result->headers = array();
// Parse headers.
while ($line = trim(array_shift($split))) {
list($header, $value) = explode(':', $line, 2);
if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
// RFC 2109: the Set-Cookie response header comprises the token Set-
// Cookie:, followed by a comma-separated list of one or more cookies.
$result->headers[$header] .= ',' . trim($value);
} else {
$result->headers[$header] = trim($value);
}
}
$responses = array(
100 => 'Continue', 101 => 'Switching Protocols',
200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed',
500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
);
// RFC 2616 states that all unknown HTTP codes must be treated the same as the
// base code in their class.
if (!isset($responses[$code])) {
$code = floor($code / 100) * 100;
}
switch ($code) {
case 200: // OK
case 304: // Not modified
break;
case 301: // Moved permanently
case 302: // Moved temporarily
case 307: // Moved temporarily
$location = $result->headers['Location'];
if ($retry) {
$result = openid_http_request($result->headers['Location'], $headers, $method, $data, --$retry);
$result->redirect_code = $result->code;
}
$result->redirect_url = $location;
break;
default:
$result->error = $text;
}
$result->code = $code;
return $result;
}
function openid_is_allowed_provider($identityUrl): bool
{
$allowedProviders = api_get_configuration_value('auth_openid_allowed_providers');
if (false === $allowedProviders) {
return true;
}
$host = parse_url($identityUrl, PHP_URL_HOST) ?: $identityUrl;
foreach ($allowedProviders as $provider) {
if (strpos($provider, '*') !== false) {
$regex = '/^' . str_replace('\*', '.*', preg_quote($provider, '/')) . '$/';
if (preg_match($regex, $host)) {
return true;
}
} else {
if ($host === $provider) {
return true;
}
}
}
return false;
}

View File

@@ -0,0 +1,420 @@
<?php
/* For licensing terms, see /license.txt */
/**
* OpenID utility functions. Taken from Drupal 6 code (from dries)
* @package chamilo.auth.openid
*/
/**
* Code
*/
// Diffie-Hellman Key Exchange Default Value.
define('OPENID_DH_DEFAULT_MOD', '155172898181473697471232257763715539915724801' .
'966915404479707795314057629378541917580651227423698188993727816152646631' .
'438561595825688188889951272158842675419950341258706556549803580104870537' .
'681476726513255747040765857479291291572334510643245094715007229621094194' .
'349783925984760375594985848253359305585439638443');
// Constants for Diffie-Hellman key exchange computations.
define('OPENID_DH_DEFAULT_GEN', '2');
define('OPENID_SHA1_BLOCKSIZE', 64);
define('OPENID_RAND_SOURCE', '/dev/urandom');
// OpenID namespace URLs
define('OPENID_NS_2_0', 'http://specs.openid.net/auth/2.0');
define('OPENID_NS_1_1', 'http://openid.net/signon/1.1');
define('OPENID_NS_1_0', 'http://openid.net/signon/1.0');
/**
* Performs an HTTP 302 redirect (for the 1.x protocol).
* This function should be deprecated for 1.8.6.2 needs documentation
*/
function openid_redirect_http($url, $message) {
$query = array();
foreach ($message as $key => $val) {
$query[] = $key . '=' . urlencode($val);
}
$sep = (strpos($url, '?') === FALSE) ? '?' : '&';
header('Location: ' . $url . $sep . implode('&', $query), TRUE, 302);
//exit;
}
/**
* Creates a js auto-submit redirect for (for the 2.x protocol)
* This function should be deprecated for 1.8.6.2 needs documentation
*/
function openid_redirect($url, $message) {
$output = '<html><head><title>' . get_lang('OpenIDRedirect') . "</title></head>\n<body>";
$output .= '<form method="post" action="' . $url . '" id="openid-redirect-form">';
foreach ($message as $key => $value) {
$output .='<input type="hidden" name="' . $key . '" value="' . $value . '">';
}
$output .= '<noscript><input type="submit" name="submit" value="' . get_lang('Send') . '"/></noscript>';
$output .= '</form>';
$output .= '<script type="text/javascript">document.getElementById("openid-redirect-form").submit();</script>';
$output .= "</body></html>";
return $output;
}
/**
* Determine if the given identifier is an XRI ID.
*/
function _openid_is_xri($identifier) {
$firstchar = substr($identifier, 0, 1);
if ($firstchar == "@" || $firstchar == "=")
return TRUE;
if (stristr($identifier, 'xri://') !== FALSE) {
return TRUE;
}
return FALSE;
}
/**
* Normalize the given identifier as per spec.
*/
function _openid_normalize($identifier) {
if (_openid_is_xri($identifier)) {
return _openid_normalize_xri($identifier);
} else {
return _openid_normalize_url($identifier);
}
}
function _openid_normalize_xri($xri) {
$normalized_xri = $xri;
if (stristr($xri, 'xri://') !== FALSE) {
$normalized_xri = substr($xri, 6);
}
return $normalized_xri;
}
function _openid_normalize_url($url) {
$normalized_url = $url;
if (stristr($url, '://') === FALSE) {
$normalized_url = 'http://' . $url;
}
if (substr_count($normalized_url, '/') < 3) {
$normalized_url .= '/';
}
return $normalized_url;
}
/**
* Create a serialized message packet as per spec: $key:$value\n .
*/
function _openid_create_message($data) {
$serialized = '';
foreach ($data as $key => $value) {
if ((strpos($key, ':') !== FALSE) || (strpos($key, "\n") !== FALSE) || (strpos($value, "\n") !== FALSE)) {
return null;
}
$serialized .= "$key:$value\n";
}
return $serialized;
}
/**
* Encode a message from _openid_create_message for HTTP Post
* @param null|string $message
*/
function _openid_encode_message($message) {
$encoded_message = '';
$items = explode("\n", $message);
foreach ($items as $item) {
$parts = explode(':', $item, 2);
if (count($parts) == 2) {
if ($encoded_message != '') {
$encoded_message .= '&';
}
$encoded_message .= rawurlencode(trim($parts[0])) . '=' . rawurlencode(trim($parts[1]));
}
}
return $encoded_message;
}
/**
* Convert a direct communication message
* into an associative array.
*/
function _openid_parse_message($message) {
$parsed_message = array();
$items = explode("\n", $message);
foreach ($items as $item) {
$parts = explode(':', $item, 2);
if (count($parts) == 2) {
$parsed_message[$parts[0]] = $parts[1];
}
}
return $parsed_message;
}
/**
* Return a nonce value - formatted per OpenID spec.
*/
function _openid_nonce() {
// YYYY-MM-DDThh:mm:ssTZD UTC, plus some optional extra unique chars
return gmstrftime('%Y-%m-%dT%H:%M:%S%Z') .
chr(mt_rand(0, 25) + 65) .
chr(mt_rand(0, 25) + 65) .
chr(mt_rand(0, 25) + 65) .
chr(mt_rand(0, 25) + 65);
}
/**
* Pull the href attribute out of an html link element.
* @param string $rel
*/
function _openid_link_href($rel, $html) {
$rel = preg_quote($rel);
preg_match('|<link\s+rel=["\'](.*)' . $rel . '(.*)["\'](.*)/?>|iU', $html, $matches);
if (isset($matches[3])) {
preg_match('|href=["\']([^"]+)["\']|iU', $matches[0], $href);
return trim($href[1]);
}
return FALSE;
}
/**
* Pull the http-equiv attribute out of an html meta element
* @param string $equiv
*/
function _openid_meta_httpequiv($equiv, $html) {
preg_match('|<meta\s+http-equiv=["\']' . $equiv . '["\'](.*)/?>|iU', $html, $matches);
if (isset($matches[1])) {
preg_match('|content=["\']([^"]+)["\']|iU', $matches[1], $content);
return $content[1];
}
return FALSE;
}
/**
* Sign certain keys in a message
* @param $association - object loaded from openid_association or openid_server_association table
* - important fields are ->assoc_type and ->mac_key
* @param $message_array - array of entire message about to be sent
* @param $keys_to_sign - keys in the message to include in signature (without
* 'openid.' appended)
*/
function _openid_signature($association, $message_array, $keys_to_sign) {
$signature = '';
$sign_data = array();
foreach ($keys_to_sign as $key) {
if (isset($message_array['openid.' . $key])) {
$sign_data[$key] = $message_array['openid.' . $key];
}
}
$message = _openid_create_message($sign_data);
$secret = base64_decode($association->mac_key);
$signature = _openid_hmac($secret, $message);
return base64_encode($signature);
}
/**
* @param string $key
* @param null|string $text
*/
function _openid_hmac($key, $text) {
if (strlen($key) > OPENID_SHA1_BLOCKSIZE) {
$key = _openid_sha1($key, true);
}
$key = str_pad($key, OPENID_SHA1_BLOCKSIZE, chr(0x00));
$ipad = str_repeat(chr(0x36), OPENID_SHA1_BLOCKSIZE);
$opad = str_repeat(chr(0x5c), OPENID_SHA1_BLOCKSIZE);
$hash1 = _openid_sha1(($key ^ $ipad) . $text, true);
$hmac = _openid_sha1(($key ^ $opad) . $hash1, true);
return $hmac;
}
function _openid_sha1($text) {
$hex = sha1($text);
$raw = '';
for ($i = 0; $i < 40; $i += 2) {
$hexcode = substr($hex, $i, 2);
$charcode = (int) base_convert($hexcode, 16, 10);
$raw .= chr($charcode);
}
return $raw;
}
function _openid_dh_base64_to_long($str) {
$b64 = base64_decode($str);
return _openid_dh_binary_to_long($b64);
}
function _openid_dh_long_to_base64($str) {
return base64_encode(_openid_dh_long_to_binary($str));
}
/**
* @param string $str
*/
function _openid_dh_binary_to_long($str) {
$bytes = array_merge(unpack('C*', $str));
$n = 0;
foreach ($bytes as $byte) {
$n = bcmul($n, pow(2, 8));
$n = bcadd($n, $byte);
}
return $n;
}
function _openid_dh_long_to_binary($long) {
$cmp = bccomp($long, 0);
if ($cmp < 0) {
return FALSE;
}
if ($cmp == 0) {
return "\x00";
}
$bytes = array();
while (bccomp($long, 0) > 0) {
array_unshift($bytes, bcmod($long, 256));
$long = bcdiv($long, pow(2, 8));
}
if ($bytes && ($bytes[0] > 127)) {
array_unshift($bytes, 0);
}
$string = '';
foreach ($bytes as $byte) {
$string .= pack('C', $byte);
}
return $string;
}
/**
* @param string $secret
*/
function _openid_dh_xorsecret($shared, $secret) {
$dh_shared_str = _openid_dh_long_to_binary($shared);
$sha1_dh_shared = _openid_sha1($dh_shared_str);
$xsecret = "";
for ($i = 0; $i < strlen($secret); $i++) {
$xsecret .= chr(ord($secret[$i]) ^ ord($sha1_dh_shared[$i]));
}
return $xsecret;
}
/**
* @param string $stop
*/
function _openid_dh_rand($stop) {
static $duplicate_cache = array();
// Used as the key for the duplicate cache
$rbytes = _openid_dh_long_to_binary($stop);
if (array_key_exists($rbytes, $duplicate_cache)) {
list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
} else {
if ($rbytes[0] == "\x00") {
$nbytes = strlen($rbytes) - 1;
} else {
$nbytes = strlen($rbytes);
}
$mxrand = bcpow(256, $nbytes);
// If we get a number less than this, then it is in the
// duplicated range.
$duplicate = bcmod($mxrand, $stop);
if (count($duplicate_cache) > 10) {
$duplicate_cache = array();
}
$duplicate_cache[$rbytes] = array($duplicate, $nbytes);
}
do {
$bytes = "\x00" . _openid_get_bytes($nbytes);
$n = _openid_dh_binary_to_long($bytes);
// Keep looping if this value is in the low duplicated range.
} while (bccomp($n, $duplicate) < 0);
return bcmod($n, $stop);
}
function _openid_get_bytes($num_bytes) {
static $f = null;
$bytes = '';
if (!isset($f)) {
$f = @fopen(OPENID_RAND_SOURCE, "r");
}
if (!$f) {
// pseudorandom used
$bytes = '';
for ($i = 0; $i < $num_bytes; $i += 4) {
$bytes .= pack('L', mt_rand());
}
$bytes = substr($bytes, 0, $num_bytes);
} else {
$bytes = fread($f, $num_bytes);
}
return $bytes;
}
/**
* Fix PHP's habit of replacing '.' by '_' in posted data.
*/
function _openid_fix_post(&$post) {
//$extensions = module_invoke_all('openid', 'extension');
foreach ($post as $key => $value) {
if (strpos($key, 'openid_') === 0) {
$fixed_key = str_replace('openid_', 'openid.', $key);
$fixed_key = str_replace('openid.ns_', 'openid.ns.', $fixed_key);
$fixed_key = str_replace('openid.sreg_', 'openid.sreg.', $fixed_key);
//foreach ($extensions as $ext) {
// $fixed_key = str_replace('openid.'.$ext.'_', 'openid.'.$ext.'.', $fixed_key);
//}
unset($post[$key]);
$post[$fixed_key] = $value;
}
}
}
/**
* Provide bcpowmod support for PHP4.
*/
if (!function_exists('bcpowmod')) {
function bcpowmod($base, $exp, $mod) {
$square = bcmod($base, $mod);
$result = 1;
while (bccomp($exp, 0) > 0) {
if (bcmod($exp, 2)) {
$result = bcmod(bcmul($result, $square), $mod);
}
$square = bcmod(bcmul($square, $square), $mod);
$exp = bcdiv($exp, 2);
}
return $result;
}
}

View File

@@ -0,0 +1,14 @@
<?php
/* For licensing terms, see /license.txt */
/**
* OpenID
* @package chamilo.auth.openid
*/
/**
* Code
*/
require_once '../../inc/global.inc.php';
Display::display_header('OpenID', NULL);
echo Display::page_header(get_lang('OpenIDWhatIs'));
echo get_lang('OpenIDDescription');
Display::display_footer();

View File

@@ -0,0 +1,85 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Parsing library for OpenID
* @package chamilo.auth.openid
*/
/**
* Code
*/
// Global variables to track parsing state
$xrds_open_elements = array();
$xrds_services = array();
$xrds_current_service = array();
/**
* Main entry point for parsing XRDS documents
*/
function xrds_parse($xml) {
global $xrds_services;
$parser = xml_parser_create_ns();
xml_set_element_handler($parser, '_xrds_element_start', '_xrds_element_end');
xml_set_character_data_handler($parser, '_xrds_cdata');
xml_parse($parser, $xml);
xml_parser_free($parser);
return $xrds_services;
}
/**
* Parser callback functions
*/
function _xrds_element_start(&$parser, $name, $attribs) {
global $xrds_open_elements;
$xrds_open_elements[] = _xrds_strip_namespace($name);
}
function _xrds_element_end(&$parser, $name) {
global $xrds_open_elements, $xrds_services, $xrds_current_service;
$name = _xrds_strip_namespace($name);
if ($name == 'SERVICE') {
if (in_array(OPENID_NS_2_0 .'/signon', $xrds_current_service['types']) ||
in_array(OPENID_NS_2_0 .'/server', $xrds_current_service['types'])) {
$xrds_current_service['version'] = 2;
}
elseif (in_array(OPENID_NS_1_1, $xrds_current_service['types']) ||
in_array(OPENID_NS_1_0, $xrds_current_service['types'])) {
$xrds_current_service['version'] = 1;
}
if (!empty($xrds_current_service['version'])) {
$xrds_services[] = $xrds_current_service;
}
$xrds_current_service = array();
}
array_pop($xrds_open_elements);
}
function _xrds_cdata(&$parser, $data) {
global $xrds_open_elements, $xrds_services, $xrds_current_service;
$path = strtoupper(implode('/', $xrds_open_elements));
switch ($path) {
case 'XRDS/XRD/SERVICE/TYPE':
$xrds_current_service['types'][] = $data;
break;
case 'XRDS/XRD/SERVICE/URI':
$xrds_current_service['uri'] = $data;
break;
case 'XRDS/XRD/SERVICE/DELEGATE':
$xrds_current_service['delegate'] = $data;
break;
}
}
function _xrds_strip_namespace($name) {
// Strip namespacing.
$pos = strrpos($name, ':');
if ($pos !== FALSE) {
$name = substr($name, $pos + 1, strlen($name));
}
return $name;
}

View File

@@ -0,0 +1,83 @@
<?php
/* For licensing terms, see /license.txt */
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
api_block_anonymous_users(true);
$allow = api_get_plugin_setting('pausetraining', 'tool_enable') === 'true';
$allowPauseFormation = api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation') === 'true';
if (false === $allow || false === $allowPauseFormation) {
api_not_allowed(true);
}
$userId = api_get_user_id();
$userInfo = api_get_user_info($userId);
$justification = '';
$plugin = PauseTraining::create();
$form = new FormValidator('pausetraining');
$form->addHeader($plugin->get_lang('PauseTraining'));
$extraField = new ExtraField('user');
$return = $extraField->addElements(
$form,
$userId,
[],
false,
false,
['pause_formation', 'start_pause_date', 'end_pause_date'],
[],
[],
false,
true
);
$form->addRule(
['extra_start_pause_date', 'extra_end_pause_date'],
get_lang('StartDateShouldBeBeforeEndDate'),
'date_compare',
'lte'
);
$form->addButtonSend(get_lang('Update'));
if ($form->validate()) {
$values = $form->getSubmitValues(1);
$values['item_id'] = $userId;
if (!isset($values['extra_pause_formation'])) {
$values['extra_pause_formation'] = 0;
}
$extraField = new ExtraFieldValue('user');
$extraField->saveFieldValues($values, true, false, [], [], true);
Display::addFlash(Display::return_message(get_lang('Update')));
header('Location: '.api_get_self());
exit;
}
$tabs = SocialManager::getHomeProfileTabs('pausetraining');
$content = $tabs.$form->returnForm();
$tpl = new Template(get_lang('ModifyProfile'));
SocialManager::setSocialUserBlock($tpl, api_get_user_id(), 'home');
$menu = SocialManager::show_social_menu(
'home',
null,
api_get_user_id(),
false,
false
);
$tpl->assign('social_menu_block', $menu);
$tpl->assign('social_right_content', $content);
$social_layout = $tpl->get_template('social/edit_profile.tpl');
$tpl->display($social_layout);

928
main/auth/profile.php Normal file
View File

@@ -0,0 +1,928 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\UserBundle\Entity\User;
use ChamiloSession as Session;
/**
* This file displays the user's profile,
* optionally it allows users to modify their profile as well.
*
* See inc/conf/profile.conf.php to modify settings
*/
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
api_block_inactive_user();
$this_section = SECTION_MYPROFILE;
$allowSocialTool = api_get_setting('allow_social_tool') === 'true';
if ($allowSocialTool) {
$this_section = SECTION_SOCIAL;
}
$logInfo = [
'tool' => 'profile',
'action' => $this_section,
];
Event::registerLog($logInfo);
$_SESSION['this_section'] = $this_section;
if (!(isset($_user['user_id']) && $_user['user_id']) || api_is_anonymous($_user['user_id'], true)) {
api_not_allowed(true);
}
$htmlHeadXtra[] = api_get_password_checker_js('#username', '#password1');
$htmlHeadXtra[] = api_get_css_asset('cropper/dist/cropper.min.css');
$htmlHeadXtra[] = api_get_asset('cropper/dist/cropper.min.js');
$htmlHeadXtra[] = '<script>
$(function() {
$("#id_generate_api_key").on("click", function (e) {
e.preventDefault();
$.ajax({
contentType: "application/x-www-form-urlencoded",
type: "POST",
url: "'.api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=generate_api_key",
data: "num_key_id="+"",
success: function(datos) {
$("#div_api_key").html(datos);
}
});
});
});
function confirmation(name) {
if (confirm("'.get_lang('AreYouSureToDeleteJS', '').' " + name + " ?")) {
document.forms["profile"].submit();
} else {
return false;
}
}
function show_image(image,width,height) {
width = parseInt(width) + 20;
height = parseInt(height) + 20;
window_x = window.open(image,\'windowX\',\'width=\'+ width + \', height=\'+ height + \'\');
}
</script>';
$jquery_ready_content = '';
if (api_get_setting('allow_message_tool') === 'true') {
$jquery_ready_content = <<<EOF
$(".message-content .message-delete").click(function(){
$(this).parents(".message-content").animate({ opacity: "hide" }, "slow");
$(".message-view").animate({ opacity: "show" }, "slow");
});
EOF;
}
$tool_name = is_profile_editable() ? get_lang('ModifProfile') : get_lang('ViewProfile');
$table_user = Database::get_main_table(TABLE_MAIN_USER);
/*
* Get initial values for all fields.
*/
$user_data = $originalUserInfo = api_get_user_info(
api_get_user_id(),
false,
false,
false,
false,
true,
true
);
$currentUser = api_get_user_entity($user_data['id']);
$array_list_key = UserManager::get_api_keys(api_get_user_id());
$id_temp_key = UserManager::get_api_key_id(api_get_user_id(), 'dokeos');
$value_array = [];
if (isset($array_list_key[$id_temp_key])) {
$value_array = $array_list_key[$id_temp_key];
}
$user_data['api_key_generate'] = $value_array;
if ($user_data !== false) {
if (api_get_setting('login_is_email') === 'true') {
$user_data['username'] = $user_data['email'];
}
if (is_null($user_data['language'])) {
$user_data['language'] = api_get_setting('platformLanguage');
}
}
$form = new FormValidator('profile');
if (api_is_western_name_order()) {
// FIRST NAME and LAST NAME
$form->addElement('text', 'firstname', get_lang('FirstName'), ['size' => 40]);
$form->addElement('text', 'lastname', get_lang('LastName'), ['size' => 40]);
} else {
// LAST NAME and FIRST NAME
$form->addElement('text', 'lastname', get_lang('LastName'), ['size' => 40]);
$form->addElement('text', 'firstname', get_lang('FirstName'), ['size' => 40]);
}
if (api_get_setting('profile', 'name') !== 'true') {
$form->freeze(['lastname', 'firstname']);
}
$form->applyFilter(['lastname', 'firstname'], 'stripslashes');
$form->applyFilter(['lastname', 'firstname'], 'trim');
$form->applyFilter(['lastname', 'firstname'], 'html_filter');
$form->addRule('lastname', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule('firstname', get_lang('ThisFieldIsRequired'), 'required');
// USERNAME
$form->addElement(
'text',
'username',
get_lang('UserName'),
[
'id' => 'username',
'maxlength' => USERNAME_MAX_LENGTH,
'size' => USERNAME_MAX_LENGTH,
]
);
if (api_get_setting('profile', 'login') !== 'true' || api_get_setting('login_is_email') === 'true') {
$form->freeze('username');
}
$form->applyFilter('username', 'stripslashes');
$form->applyFilter('username', 'trim');
$form->addRule('username', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule('username', get_lang('UsernameWrong'), 'username');
$form->addRule('username', get_lang('UserTaken'), 'username_available', $user_data['username']);
// OFFICIAL CODE
if (defined('CONFVAL_ASK_FOR_OFFICIAL_CODE') && CONFVAL_ASK_FOR_OFFICIAL_CODE === true) {
$form->addElement('text', 'official_code', get_lang('OfficialCode'), ['size' => 40]);
if (api_get_setting('profile', 'officialcode') !== 'true') {
$form->freeze('official_code');
}
$form->applyFilter('official_code', 'stripslashes');
$form->applyFilter('official_code', 'trim');
$form->applyFilter('official_code', 'html_filter');
if (api_get_setting('registration', 'officialcode') === 'true' &&
api_get_setting('profile', 'officialcode') === 'true'
) {
$form->addRule('official_code', get_lang('ThisFieldIsRequired'), 'required');
}
}
// EMAIL
$form->addElement('email', 'email', get_lang('Email'), ['size' => 40]);
if (api_get_setting('profile', 'email') !== 'true') {
$form->freeze('email');
}
if (api_get_setting('registration', 'email') === 'true' && api_get_setting('profile', 'email') === 'true') {
$form->applyFilter('email', 'stripslashes');
$form->applyFilter('email', 'trim');
$form->addRule('email', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule('email', get_lang('EmailWrong'), 'email');
}
// OPENID URL
if (is_profile_editable() && api_get_setting('openid_authentication') === 'true') {
$form->addElement('text', 'openid', get_lang('OpenIDURL'), ['size' => 40]);
if (api_get_setting('profile', 'openid') !== 'true') {
$form->freeze('openid');
}
$form->applyFilter('openid', 'trim');
}
// PHONE
$form->addElement('text', 'phone', get_lang('Phone'), ['size' => 20]);
if (api_get_setting('profile', 'phone') !== 'true') {
$form->freeze('phone');
}
$form->applyFilter('phone', 'stripslashes');
$form->applyFilter('phone', 'trim');
$form->applyFilter('phone', 'html_filter');
// PICTURE
if (is_profile_editable() && api_get_setting('profile', 'picture') == 'true') {
$form->addFile(
'picture',
[
$user_data['picture_uri'] != '' ? get_lang('UpdateImage') : get_lang('AddImage'),
get_lang('OnlyImagesAllowed'),
],
[
'id' => 'picture',
'class' => 'picture-form',
'crop_image' => true,
'crop_ratio' => '1 / 1',
'accept' => 'image/*',
]
);
$form->addProgress();
if (!empty($user_data['picture_uri'])) {
$form->addElement('checkbox', 'remove_picture', null, get_lang('DelImage'));
}
$allowed_picture_types = api_get_supported_image_extensions(false);
$form->addRule(
'picture',
get_lang('OnlyImagesAllowed').' ('.implode(', ', $allowed_picture_types).')',
'filetype',
$allowed_picture_types
);
}
// LANGUAGE
$form->addSelectLanguage('language', get_lang('Language'));
if (api_get_setting('profile', 'language') !== 'true') {
$form->freeze('language');
}
// THEME
if (is_profile_editable() && api_get_setting('user_selected_theme') === 'true') {
$form->addElement('SelectTheme', 'theme', get_lang('Theme'));
if (api_get_setting('profile', 'theme') !== 'true') {
$form->freeze('theme');
}
$form->applyFilter('theme', 'trim');
}
// EXTENDED PROFILE this make the page very slow!
if (api_get_setting('extended_profile') === 'true') {
$width_extended_profile = 500;
// MY PERSONAL OPEN AREA
$form->addHtmlEditor(
'openarea',
[get_lang('MyPersonalOpenArea'), get_lang('MyPersonalOpenAreaHelp')],
false,
false,
[
'ToolbarSet' => 'Profile',
'Width' => $width_extended_profile,
'Height' => '350',
]
);
// MY COMPETENCES
$form->addHtmlEditor(
'competences',
[get_lang('MyCompetences'), get_lang('MyCompetencesHelp')],
false,
false,
[
'ToolbarSet' => 'Profile',
'Width' => $width_extended_profile,
'Height' => '130',
]
);
// MY DIPLOMAS
$form->addHtmlEditor(
'diplomas',
[get_lang('MyDiplomas'), get_lang('MyDiplomasHelp')],
false,
false,
[
'ToolbarSet' => 'Profile',
'Width' => $width_extended_profile,
'Height' => '130',
]
);
// WHAT I AM ABLE TO TEACH
$form->addHtmlEditor(
'teach',
[get_lang('MyTeach'), get_lang('MyTeachingCapabilitiesHelp')],
false,
false,
[
'ToolbarSet' => 'Profile',
'Width' => $width_extended_profile,
'Height' => '130',
]
);
// MY PRODUCTIONS
$form->addElement('file', 'production', [get_lang('MyProductions'), get_lang('MyProductionsHelp')]);
if ($production_list = UserManager::build_production_list(api_get_user_id(), '', true)) {
$form->addElement('static', 'productions_list', null, $production_list);
}
// openarea is untrimmed for maximum openness
$form->applyFilter(['competences', 'diplomas', 'teach', 'openarea'], 'stripslashes');
$form->applyFilter(['competences', 'diplomas', 'teach'], 'trim');
}
$showPassword = is_platform_authentication();
$links = api_get_configuration_value('auth_password_links');
$extraLink = '';
if (!empty($links) &&
isset($links['profiles']) &&
isset($links['profiles'][$user_data['status']]) &&
isset($links['profiles'][$user_data['status']][$user_data['auth_source']])
) {
$extraUserConditions = $links['profiles'][$user_data['status']][$user_data['auth_source']];
if (isset($extraUserConditions['show_password_field'])) {
$showPassword = $extraUserConditions['show_password_field'];
}
if (isset($extraUserConditions['extra_link'])) {
$extraLink = $extraUserConditions['extra_link'];
}
}
// PASSWORD, if auth_source is platform
$allow_users_to_change_email_with_no_password = true;
if (is_platform_authentication() &&
api_get_setting('allow_users_to_change_email_with_no_password') == 'false'
) {
$allow_users_to_change_email_with_no_password = false;
}
if (!$allow_users_to_change_email_with_no_password) {
$passwordExtraCommentForPasswordChange = get_lang('ToChangeYourEmailMustTypeYourPassword').". ";
}
if ($showPassword &&
is_profile_editable() &&
api_get_setting('profile', 'password') === 'true'
) {
$form->addElement(
'password',
'password0',
[get_lang('Pass'), $passwordExtraCommentForPasswordChange.get_lang('TypeCurrentPassword')],
[
'size' => 40,
'show_hide' => true,
]
);
$form->addElement(
'password',
'password1',
get_lang('NewPass'),
[
'id' => 'password1',
'size' => 40,
'show_hide' => true,
'placeholder' => get_lang('EnterYourNewPassword'),
]
);
$form->addElement(
'password',
'password2',
[get_lang('Confirmation'), get_lang('RepeatYourNewPassword')],
[
'size' => 40,
'show_hide' => true,
]
);
// user must enter identical password twice so we can prevent some user errors
$form->addRule(['password1', 'password2'], get_lang('PassTwo'), 'compare');
$form->addPasswordRule('password1');
$form->addNoSamePasswordRule('password1', $currentUser);
} elseif (!$allow_users_to_change_email_with_no_password) {
$form->addElement(
'password',
'password0',
[get_lang('Pass'), $passwordExtraCommentForPasswordChange],
[
'size' => 40,
'show_hide' => true,
]
);
}
$form->addHtml($extraLink);
$extraField = new ExtraField('user');
$return = $extraField->addElements($form, api_get_user_id(), ['pause_formation', 'start_pause_date', 'end_pause_date']);
$jquery_ready_content = $return['jquery_ready_content'];
// the $jquery_ready_content variable collects all functions that
// will be load in the $(document).ready javascript function
$htmlHeadXtra[] = '<script>
$(function() {
'.$jquery_ready_content.'
});
</script>';
if (api_get_setting('profile', 'apikeys') == 'true') {
$form->addElement('html', '<div id="div_api_key">');
$form->addElement(
'text',
'api_key_generate',
get_lang('MyApiKey'),
['size' => 40, 'id' => 'id_api_key_generate']
);
$form->addElement('html', '</div>');
$form->addButton(
'generate_api_key',
get_lang('GenerateApiKey'),
'cogs',
'default',
'default',
null,
['id' => 'id_generate_api_key']
);
}
$form->addHidden('origin', 'profile');
// SUBMIT
if (is_profile_editable()) {
$form->addButtonUpdate(get_lang('SaveSettings'), 'apply_change');
} else {
$form->freeze();
}
// Student cannot modified their user conditions
$extraConditions = api_get_configuration_value('show_conditions_to_user');
if ($extraConditions && isset($extraConditions['conditions'])) {
$extraConditions = $extraConditions['conditions'];
foreach ($extraConditions as $condition) {
$element = $form->getElement('extra_'.$condition['variable']);
if ($element) {
$element->freeze();
}
}
}
$form->setDefaults($user_data);
$filtered_extension = false;
if ($form->validate()) {
$hook = HookUpdateUser::create();
if ($hook) {
$hook->notifyUpdateUser(HOOK_EVENT_TYPE_PRE);
}
$wrong_current_password = false;
$user_data = $form->getSubmitValues(1);
$user_data['item_id'] = api_get_user_id();
/** @var User $user */
$user = UserManager::getRepository()->find(api_get_user_id());
// set password if a new one was provided
$validPassword = false;
$passwordWasChecked = false;
if ($user &&
(!empty($user_data['password0']) &&
!empty($user_data['password1'])) ||
(!empty($user_data['password0']) &&
api_get_setting('profile', 'email') == 'true')
) {
$passwordWasChecked = true;
$validPassword = UserManager::checkPassword(
$user->getPassword(),
$user_data['password0'],
$user->getSalt(),
$user->getId()
);
if ($validPassword) {
$password = $user_data['password1'];
} else {
Display::addFlash(
Display::return_message(
get_lang('CurrentPasswordEmptyOrIncorrect'),
'warning',
false
)
);
}
}
$allow_users_to_change_email_with_no_password = true;
if (is_platform_authentication() &&
api_get_setting('allow_users_to_change_email_with_no_password') == 'false'
) {
$allow_users_to_change_email_with_no_password = false;
}
// If user sending the email to be changed (input available and not frozen )
if (api_get_setting('profile', 'email') == 'true') {
if ($allow_users_to_change_email_with_no_password) {
if (!check_user_email($user_data['email'])) {
$changeemail = $user_data['email'];
}
} else {
// Normal behaviour
if (!check_user_email($user_data['email']) && $validPassword) {
$changeemail = $user_data['email'];
}
if (!check_user_email($user_data['email']) && empty($user_data['password0'])) {
Display::addFlash(
Display::return_message(
get_lang('ToChangeYourEmailMustTypeYourPassword'),
'error',
false
)
);
}
}
}
// Upload picture if a new one is provided
if ($_FILES['picture']['size']) {
$new_picture = UserManager::update_user_picture(
api_get_user_id(),
$_FILES['picture']['name'],
$_FILES['picture']['tmp_name'],
$user_data['picture_crop_result']
);
if ($new_picture) {
$user_data['picture_uri'] = $new_picture;
Display::addFlash(
Display::return_message(
get_lang('PictureUploaded'),
'normal',
false
)
);
}
} elseif (!empty($user_data['remove_picture'])) {
// remove existing picture if asked
UserManager::deleteUserPicture(api_get_user_id());
$user_data['picture_uri'] = '';
}
// Remove production.
if (isset($user_data['remove_production']) &&
is_array($user_data['remove_production'])
) {
foreach (array_keys($user_data['remove_production']) as $production) {
UserManager::remove_user_production(api_get_user_id(), urldecode($production));
}
if ($production_list = UserManager::build_production_list(api_get_user_id(), true, true)) {
$form->insertElementBefore(
$form->createElement('static', null, null, $production_list),
'productions_list'
);
}
$form->removeElement('productions_list');
Display::addFlash(
Display::return_message(get_lang('FileDeleted'), 'normal', false)
);
}
// upload production if a new one is provided
if (isset($_FILES['production']) && $_FILES['production']['size']) {
$res = upload_user_production(api_get_user_id());
if (!$res) {
//it's a bit excessive to assume the extension is the reason why
// upload_user_production() returned false, but it's true in most cases
$filtered_extension = true;
} else {
Display::addFlash(
Display::return_message(
get_lang('ProductionUploaded'),
'normal',
false
)
);
}
}
// remove values that shouldn't go in the database
unset(
$user_data['password0'],
$user_data['password1'],
$user_data['password2'],
$user_data['MAX_FILE_SIZE'],
$user_data['remove_picture'],
$user_data['apply_change'],
$user_data['email']
);
// Following RFC2396 (http://www.faqs.org/rfcs/rfc2396.html), a URI uses ':' as a reserved character
// we can thus ensure the URL doesn't contain any scheme name by searching for ':' in the string
$my_user_openid = isset($user_data['openid']) ? $user_data['openid'] : '';
if (!preg_match('/^[^:]*:\/\/.*$/', $my_user_openid)) {
//ensure there is at least a http:// scheme in the URI provided
$user_data['openid'] = 'http://'.$my_user_openid;
}
$extras = [];
//Checking the user language
$languages = api_get_languages();
if (!in_array($user_data['language'], $languages['folder'])) {
$user_data['language'] = api_get_setting('platformLanguage');
}
$_SESSION['_user']['language'] = $user_data['language'];
//Only update values that are request by the "profile" setting
$profile_list = api_get_setting('profile');
//Adding missing variables
$available_values_to_modify = [];
foreach ($profile_list as $key => $status) {
if ($status == 'true') {
switch ($key) {
case 'login':
$available_values_to_modify[] = 'username';
break;
case 'name':
$available_values_to_modify[] = 'firstname';
$available_values_to_modify[] = 'lastname';
break;
case 'picture':
$available_values_to_modify[] = 'picture_uri';
break;
default:
$available_values_to_modify[] = $key;
break;
}
}
}
//Fixing missing variables
$available_values_to_modify = array_merge(
$available_values_to_modify,
['competences', 'diplomas', 'openarea', 'teach', 'openid', 'address']
);
// build SQL query
$sql = "UPDATE $table_user SET";
unset($user_data['api_key_generate']);
foreach ($user_data as $key => $value) {
if (substr($key, 0, 6) === 'extra_') { //an extra field
continue;
} elseif (strpos($key, 'remove_extra_') !== false) {
} else {
if (in_array($key, $available_values_to_modify)) {
$sql .= " $key = '".Database::escape_string($value)."',";
}
}
}
$changePassword = false;
// Change email
if ($allow_users_to_change_email_with_no_password) {
if (isset($changeemail) && in_array('email', $available_values_to_modify)) {
$sql .= " email = '".Database::escape_string($changeemail)."' ";
}
if (isset($password) && in_array('password', $available_values_to_modify)) {
$changePassword = true;
}
} else {
if (isset($changeemail) && !isset($password) && in_array('email', $available_values_to_modify)) {
$sql .= " email = '".Database::escape_string($changeemail)."'";
} else {
if (isset($password) && in_array('password', $available_values_to_modify)) {
if (isset($changeemail) && in_array('email', $available_values_to_modify)) {
$sql .= " email = '".Database::escape_string($changeemail)."' ";
}
$changePassword = true;
}
}
}
$sql = rtrim($sql, ',');
if ($changePassword && !empty($password)) {
UserManager::updatePassword(api_get_user_id(), $password);
if (api_get_configuration_value('security_password_rotate_days') > 0) {
$date = api_get_local_time(
null,
'UTC',
'UTC',
null,
null,
null,
'Y-m-d H:i:s'
);
$extraFieldValue = new ExtraFieldValue('user');
$extraFieldValue->save(
[
'item_id' => $user->getId(),
'variable' => 'password_updated_at',
'value' => $date,
]
);
}
}
if (api_get_setting('profile', 'officialcode') === 'true' &&
isset($user_data['official_code'])
) {
$sql .= ", official_code = '".Database::escape_string($user_data['official_code'])."'";
}
$sql .= " WHERE id = '".api_get_user_id()."'";
Database::query($sql);
if ($passwordWasChecked == false) {
Display::addFlash(
Display::return_message(get_lang('ProfileReg'), 'normal', false)
);
} else {
if ($validPassword) {
Display::addFlash(
Display::return_message(get_lang('ProfileReg'), 'normal', false)
);
}
}
$extraField = new ExtraFieldValue('user');
$extraField->saveFieldValues($user_data);
$userInfo = api_get_user_info(
api_get_user_id(),
false,
false,
false,
false,
true,
true
);
Session::write('_user', $userInfo);
$notification = api_get_configuration_value('user_notification_settings');
if (!empty($notification)) {
foreach ($notification as $label => $notificationSettings) {
$sendMessage = false;
if (isset($notificationSettings['if_field_changes'])) {
foreach ($notificationSettings['if_field_changes'] as $field) {
if ($originalUserInfo[$field] != $userInfo[$field]) {
$sendMessage = true;
break;
}
}
}
if ($sendMessage) {
$subject = $notificationSettings['subject'];
$content = $notificationSettings['content'];
$userInfo['extra_fields'] = UserManager::get_extra_user_data(api_get_user_id());
$template = new Template();
$template->assign('old', $originalUserInfo);
$template->assign('new', $userInfo);
$content = $template->fetch($template->get_template($content));
$emails = explode(',', $notificationSettings['email']);
foreach ($emails as $email) {
api_mail_html(
'',
$email,
$subject,
$content,
$userInfo['complete_name'],
$notificationSettings['sender_email'],
[
'reply_to' => [
'mail' => $userInfo['mail'],
'name' => $userInfo['complete_name'],
],
]
);
}
}
}
}
if ($hook) {
Database::getManager()->clear(User::class); // Avoid cache issue (user entity is used before)
$user = api_get_user_entity(api_get_user_id()); // Get updated user info for hook event
$hook->setEventData(['user' => $user]);
$hook->notifyUpdateUser(HOOK_EVENT_TYPE_POST);
}
Session::erase('system_timezone');
$url = api_get_self();
header("Location: $url");
exit;
}
$actions = '';
if ($allowSocialTool) {
if (api_get_setting('extended_profile') === 'true') {
if (api_get_setting('allow_message_tool') === 'true') {
$actions .= '<a href="'.api_get_path(WEB_PATH).'main/social/profile.php">'.
Display::return_icon('shared_profile.png', get_lang('ViewSharedProfile')).'</a>';
$actions .= '<a href="'.api_get_path(WEB_PATH).'main/messages/inbox.php">'.
Display::return_icon('inbox.png', get_lang('Messages')).'</a>';
}
$show = isset($_GET['show']) ? '&show='.(int) $_GET['show'] : '';
if (isset($_GET['type']) && $_GET['type'] === 'extended') {
$actions .= '<a href="profile.php?type=reduced'.$show.'">'.
Display::return_icon('edit.png', get_lang('EditNormalProfile'), '', 16).'</a>';
} else {
$actions .= '<a href="profile.php?type=extended'.$show.'">'.
Display::return_icon('edit.png', get_lang('EditExtendProfile'), '', 16).'</a>';
}
}
}
$show_delete_account_button = api_get_setting('platform_unsubscribe_allowed') === 'true' ? true : false;
$tpl = new Template(get_lang('ModifyProfile'));
if ($actions) {
$tpl->assign(
'actions',
Display::toolbarAction('toolbar', [$actions])
);
}
SocialManager::setSocialUserBlock($tpl, api_get_user_id(), 'messages');
$tabs = SocialManager::getHomeProfileTabs('profile');
if ($allowSocialTool) {
SocialManager::setSocialUserBlock($tpl, api_get_user_id(), 'home');
$menu = SocialManager::show_social_menu(
'home',
null,
api_get_user_id(),
false,
$show_delete_account_button
);
$tpl->assign('social_menu_block', $menu);
$tpl->assign('social_right_content', $tabs.$form->returnForm());
$social_layout = $tpl->get_template('social/edit_profile.tpl');
$tpl->display($social_layout);
} else {
$bigImage = UserManager::getUserPicture(api_get_user_id(), USER_IMAGE_SIZE_BIG);
$normalImage = UserManager::getUserPicture(api_get_user_id(), USER_IMAGE_SIZE_ORIGINAL);
$imageToShow = '<div id="image-message-container">';
$imageToShow .= '<a class="expand-image pull-right" href="'.$bigImage.'" /><img src="'.$normalImage.'"></a>';
$imageToShow .= '</div>';
$content = $imageToShow.$form->returnForm().$tabs;
$tpl->assign('content', $content);
$tpl->display_one_col_template();
}
// Helper functions defined below this point
/**
* Is user auth_source is platform ?
*
* @return bool Whether auth_source is 'platform' or not
*/
function is_platform_authentication()
{
$tabUserInfo = api_get_user_info(api_get_user_id());
return $tabUserInfo['auth_source'] == PLATFORM_AUTH_SOURCE;
}
/**
* Can a user edit his/her profile?
*
* @return bool Whether the profile can be edited by the user or not
*/
function is_profile_editable()
{
if (isset($GLOBALS['profileIsEditable'])) {
return (bool) $GLOBALS['profileIsEditable'];
}
return true;
}
/**
* Upload a submitted user production.
*
* @param int $userId User id
*
* @return mixed The filename of the new production or FALSE if the upload has failed
*/
function upload_user_production($userId)
{
$productionRepository = UserManager::getUserPathById($userId, 'system');
if (!file_exists($productionRepository)) {
@mkdir($productionRepository, api_get_permissions_for_new_directories(), true);
}
$filename = api_replace_dangerous_char($_FILES['production']['name']);
$filename = disable_dangerous_file($filename);
if (filter_extension($filename)) {
if (@move_uploaded_file($_FILES['production']['tmp_name'], $productionRepository.$filename)) {
return $filename;
}
}
return false; // this should be returned if anything went wrong with the upload
}
/**
* Check current user's current password.
*
* @param string $email E-mail
*
* @return bool Whether this e-mail is already in use or not
*/
function check_user_email($email)
{
$userId = api_get_user_id();
if ($userId != strval(intval($userId)) || empty($email)) {
return false;
}
$tableUser = Database::get_main_table(TABLE_MAIN_USER);
$email = Database::escape_string($email);
$sql = "SELECT * FROM $tableUser WHERE user_id = $userId AND email = '$email'";
$result = Database::query($sql);
return Database::num_rows($result) != 0;
}

View File

@@ -0,0 +1,27 @@
<?php
/* For license terms, see /license.txt */
require_once __DIR__.'/../inc/global.inc.php';
// Build the form
$form = new FormValidator('resend');
$form->addHeader(get_lang('ReSendConfirmationMail'));
$form->addText('user', get_lang('UserName'), true);
$form->addButtonSend(get_lang('Send'));
if ($form->validate()) {
$values = $form->exportValues();
$user = UserManager::getManager()->findUserByUsername($values['user']);
if ($user) {
UserManager::sendUserConfirmationMail($user);
} else {
Display::addFlash(Display::return_message(get_lang('UserDoesNotExist')));
}
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$tpl = new Template(null);
$tpl->assign('content', $form->toHtml());
$tpl->display_one_col_template();

124
main/auth/reset.php Normal file
View File

@@ -0,0 +1,124 @@
<?php
/* For license terms, see /license.txt */
require_once __DIR__.'/../inc/global.inc.php';
$token = $_GET['token'] ?? '';
if (!ctype_alnum($token)) {
$token = '';
}
$user = UserManager::getManager()->findUserByConfirmationToken($token);
if (!$user) {
Display::addFlash(
Display::return_message(get_lang('LinkExpired'), 'error')
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}
// Build the form
$form = new FormValidator('reset', 'POST', api_get_self().'?token='.$token);
$form->addElement('header', get_lang('ResetPassword'));
$form->addHidden('token', $token);
if (!empty($_GET['rotate'])) {
$form->addElement('html', Display::return_message(get_lang('PasswordExpiredPleaseSetNewPassword'), 'warning'));
}
$form->addElement(
'password',
'pass1',
get_lang('Password'),
[
'show_hide' => true,
]
);
$form->addElement(
'password',
'pass2',
get_lang('Confirmation'),
['id' => 'pass2', 'size' => 20, 'autocomplete' => 'off']
);
$form->addRule('pass1', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule('pass2', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule(['pass1', 'pass2'], get_lang('PassTwo'), 'compare');
$form->addPasswordRule('pass1');
$form->addNoSamePasswordRule('pass1', $user);
$form->addButtonSave(get_lang('Update'));
$ttl = api_get_setting('user_reset_password_token_limit');
if (empty($ttl)) {
$ttl = 3600;
}
if ($form->validate()) {
$values = $form->exportValues();
$password = $values['pass1'];
$token = $values['token'];
/** @var \Chamilo\UserBundle\Entity\User $user */
$user = UserManager::getManager()->findUserByConfirmationToken($token);
if ($user) {
if (!$user->isPasswordRequestNonExpired($ttl)) {
Display::addFlash(Display::return_message(get_lang('LinkExpired')), 'warning');
header('Location: '.api_get_path(WEB_CODE_PATH).'auth/lostPassword.php');
exit;
}
$user->setPlainPassword($password);
$userManager = UserManager::getManager();
$userManager->updateUser($user, true);
$user->setConfirmationToken(null);
$user->setPasswordRequestedAt(null);
Database::getManager()->persist($user);
Database::getManager()->flush();
if (api_get_configuration_value('force_renew_password_at_first_login')) {
$extraFieldValue = new ExtraFieldValue('user');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($user->getId(), 'ask_new_password');
if (!empty($value) && isset($value['value']) && 1 === (int) $value['value']) {
$extraFieldValue->delete($value['id']);
}
}
if (api_get_configuration_value('security_password_rotate_days') > 0) {
$extraFieldValue = new ExtraFieldValue('user');
$date = api_get_local_time(
null,
'UTC',
'UTC',
null,
null,
null,
'Y-m-d H:i:s'
);
$extraFieldValue->save(
[
'item_id' => $user->getId(),
'variable' => 'password_updated_at',
'value' => $date,
]
);
}
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.api_get_path(WEB_PATH));
exit;
} else {
Display::addFlash(
Display::return_message(get_lang('LinkExpired'))
);
}
}
$htmlHeadXtra[] = api_get_password_checker_js('#username', '#reset_pass1');
$tpl = new Template(null);
$tpl->assign('content', $form->toHtml());
$tpl->display_one_col_template();

View File

@@ -0,0 +1,55 @@
<?php
/* For license terms, see /license.txt */
use ChamiloSession as Session;
/**
* This page aims at requesting a password from a user to access a course
* protected by password. If the password matches the course password, we
* store the fact that user can access it during its session.
*/
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
$this_section = SECTION_COURSES;
$courseId = isset($_GET['course_id']) ? intval($_GET['course_id']) : null;
$sessionId = isset($_GET['session_id']) ? intval($_GET['session_id']) : null;
$userId = api_get_user_id();
/**
* Security check.
*/
if (empty($courseId)) {
api_not_allowed();
}
$courseInfo = api_get_course_info_by_id($courseId);
// Build the form
$form = new FormValidator(
'set_temp_password',
'POST',
api_get_self().'?course_id='.$courseId.'&session_id='.$sessionId
);
$form->addElement('header', get_lang('CourseRequiresPassword'));
$form->addElement('hidden', 'course_id', $courseId);
$form->addElement('hidden', 'session_id', $sessionId);
$form->addElement('password', 'course_password', get_lang('Password'));
$form->addButtonSave(get_lang('Accept'));
if ($form->validate()) {
$formValues = $form->exportValues();
if (sha1($formValues['course_password']) === $courseInfo['registration_code']) {
Session::write('course_password_'.$courseInfo['real_id'], true);
header('Location: '.api_get_course_url($courseInfo['code'], $sessionId).
'&action=subscribe&sec_token='.Security::get_existing_token());
exit;
} else {
Display::addFlash(
Display::return_message(get_lang('CourseRegistrationCodeIncorrect'), 'error')
);
}
}
$tpl = new Template(null);
$tpl->assign('content', $form->toHtml());
$tpl->display_one_col_template();

View File

@@ -0,0 +1,10 @@
Shibboleth authentication module.
@license see /license.txt
@author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
To use install Shibboleth on your web server and secure the application url
with a web server security directive.
Modify configuration to your federation's needs.

View File

@@ -0,0 +1,158 @@
<?php
namespace Shibboleth;
use \Redirect;
use \Display;
use IndexManager;
/**
* Controller for the Shibboleth authentication system.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethController
{
/**
*
* @return ShibbolethController
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
/**
* Log user in with Shibboleth authentication
*/
function login()
{
if (Shibboleth::session()->is_logged_in())
{
Redirect::home();
}
$user = Shibboleth::store()->get_user();
if ($user->is_empty())
{
$message = get_lang('SystemCouldNotLogYouIn');
Shibboleth::display()->error_page($message);
}
$is_new_user = !User::store()->shibboleth_id_exists($user->unique_id);
if ($is_new_user && empty($user->email) && Shibboleth::config()->is_email_mandatory)
{
$form = ShibbolethEmailForm::instance();
if ($email = $form->get_email())
{
$user->email = $email;
}
else
{
$content = $form->display();
Shibboleth::display()->page($content);
}
}
Shibboleth::save($user);
$chamilo_user = User::store()->get_by_shibboleth_id($user->unique_id);
Shibboleth::session()->login($chamilo_user->user_id);
if ($is_new_user && $user->status_request)
{
Shibboleth::redirect('/main/auth/shibboleth/app/view/request.php');
}
else
{
Shibboleth::redirect();
}
}
/**
* Log user in using the standard Chamilo way of logging in.
* Useful when the normal login screen is removed from the user interface
* - replaced by Shibboleth login - and user want to login using a standard
* account
*/
public function admin_login()
{
$title = get_lang('InternalLogin');
if (Shibboleth::session()->is_logged_in())
{
$message = get_lang('AlreadyLoggedIn');
Shibboleth::display()->message_page($message, $title);
}
$index_manager = new IndexManager('');
$html = $index_manager->display_login_form();
Shibboleth::display()->page($html, $title);
}
/**
* Display the request new status page to administrator for new users.
*/
public function request_status()
{
/*
* That may happen if a user visit that url again.
*/
if (!Shibboleth::session()->is_logged_in())
{
Shibboleth::redirect();
}
$user = Shibboleth::session()->user();
if ($user['status'] == Shibboleth::TEACHER_STATUS)
{
//Maximum user right is reached.
Shibboleth::redirect();
}
$form = ShibbolethStatusRequestForm::instance();
if ($form->cancelled())
{
Shibboleth::redirect();
}
if ($reason = $form->get_reason())
{
$subject = get_lang('RequestStatus');
$status = $form->get_status();
$status = Shibboleth::format_status($status);
$message = <<<EOT
New status: $status
Reason:
$reason
EOT;
$success = Shibboleth::email_admin($subject, $message);
if ($success)
{
$request_submitted = get_lang('RequestSubmitted');
Shibboleth::display()->message_page($request_submitted);
}
else
{
$request_failed = get_lang('RequestFailed');
Shibboleth::display()->error_page($request_failed);
}
}
$title = get_lang('RequestStatus');
Display :: display_header($title);
echo $form->display();
Display :: display_footer();
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace Shibboleth;
require_once __DIR__.'/scaffold/admin.class.php';
/**
* A Chamilo admin. Model for the Admin table.
*
* Should be moved to the core. It only exists because it is not available through
* the API.
*
* The _Admin objet is generated by the scaffolder. Admin inherits from it to allow
* modifications without touching the generated file. Don't modify _Admin as
* it may change in the future. Instead add modifications to this class.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class Admin extends _Admin
{
}
/**
* Store for Admin objects. Interact with the database. Allows to save and retrieve
* admin objects.
*
* Should be moved to the core. It only exists because it is not available through
* the API.
*
* The _AdminStore objet is generated by the scaffolder. This class inherits from it to allow
* modifications without touching the generated file. Don't modify the _ object as
* it may change in the future. Instead add modifications to this class.
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class AdminStore extends _AdminStore
{
}

View File

@@ -0,0 +1,134 @@
<?php
namespace Shibboleth;
/**
* This file is autogenerated. Do not modifiy it.
*/
/**
*
* Model for table admin
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class _Admin
{
/**
* Store for Admin objects. Interact with the database.
*
* @return AdminStore
*/
public static function store()
{
static $result = false;
if (empty($result))
{
$result = new AdminStore();
}
return $result;
}
/**
*
* @return Admin
*/
public static function create($data = null)
{
return self::store()->create_object($data);
}
public $user_id = null;
/**
*
* @return bool
*/
public function save()
{
return self::store()->save($this);
}
}
/**
* Store for Admin objects. Interact with the database.
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class _AdminStore extends Store
{
/**
*
* @return AdminStore
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
public function __construct()
{
parent::__construct('admin', '\Shibboleth\Admin', 'user_id');
}
/**
*
* @return Admin
*/
public function get($w)
{
$args = func_get_args();
$f = array('parent', 'get');
return call_user_func_array($f, $args);
}
/**
*
* @return Admin
*/
public function create_object($data)
{
return parent::create_object($data);
}
/**
*
* @return Admin
*/
public function get_by_user_id($value)
{
return $this->get(array('user_id' => $value));
}
/**
*
* @return bool
*/
public function user_id_exists($value)
{
return $this->exist(array('user_id' => $value));
}
/**
*
* @return bool
*/
public function delete_by_user_id($value)
{
return $this->delete(array('user_id' => $value));
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace Shibboleth;
/**
* This file is autogenerated. Do not modifiy it.
*/
/**
*
* Model for table user
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class _User
{
/**
* Store for User objects. Interact with the database.
*
* @return UserStore
*/
public static function store()
{
static $result = false;
if (empty($result))
{
$result = new UserStore();
}
return $result;
}
/**
*
* @return User
*/
public static function create($data = null)
{
return self::store()->create_object($data);
}
public $user_id = null;
public $lastname = null;
public $firstname = null;
public $username = null;
public $password = null;
public $auth_source = null;
public $shibb_unique_id = null;
public $email = null;
public $status = null;
public $official_code = null;
public $phone = null;
public $picture_uri = null;
public $creator_id = null;
public $competences = null;
public $diplomas = null;
public $openarea = null;
public $teach = null;
public $productions = null;
public $language = null;
public $registration_date = null;
public $expiration_date = null;
public $active = null;
public $openid = null;
public $theme = null;
public $hr_dept_id = null;
public $shibb_persistent_id = null;
/**
*
* @return bool
*/
public function save()
{
return self::store()->save($this);
}
}
/**
* Store for User objects. Interact with the database.
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class _UserStore extends Store
{
/**
*
* @return UserStore
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
public function __construct()
{
parent::__construct('user', '\Shibboleth\User', 'user_id');
}
/**
*
* @return User
*/
public function get($w)
{
$args = func_get_args();
$f = array('parent', 'get');
return call_user_func_array($f, $args);
}
/**
*
* @return User
*/
public function create_object($data)
{
return parent::create_object($data);
}
/**
*
* @return User
*/
public function get_by_user_id($value)
{
return $this->get(array('user_id' => $value));
}
/**
*
* @return bool
*/
public function user_id_exists($value)
{
return $this->exist(array('user_id' => $value));
}
/**
*
* @return bool
*/
public function delete_by_user_id($value)
{
return $this->delete(array('user_id' => $value));
}
/**
*
* @return User
*/
public function get_by_username($value)
{
return $this->get(array('username' => $value));
}
/**
*
* @return bool
*/
public function username_exists($value)
{
return $this->exist(array('username' => $value));
}
/**
*
* @return bool
*/
public function delete_by_username($value)
{
return $this->delete(array('username' => $value));
}
}

View File

@@ -0,0 +1,197 @@
<?php
namespace Shibboleth;
/**
* Returns Shibboleth user's values based on Shibboleth's configuration.
* Shibboleth returns not only whether a user is authenticated but returns as
* well several paralemeter fields.
*
* If a user is not authenticated nothing is returned.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethStore
{
/**
*
* @return ShibbolethStore
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
/**
*
* @return ShibbolethConfig
*/
public static function config()
{
return Shibboleth::config();
}
public function get_unique_id()
{
return $this->get(__FUNCTION__);
}
/**
* If the user has more than one surname, it is possible depending of the user
* home organization that they are all given to the resource.
* In the case of the University of Geneva, with two surnames, three different values
* for the surname are sent. They are:
* 1) "givenname1"
* 2) "givenname2"
* 3) "givenname1 givenname2"
* meaning the string is as follow: "givenname1;givenname2;givenname1 givenname2"
*
* In such a case, the correct surname is the one which is followed by a space.
* This function tests if such a situation is encountered, and returns the first given name.
*
* @author Nicolas Rod
*/
public function get_firstname()
{
$result = $this->get(__FUNCTION__);
if (!is_array($result))
{
$result = ucfirst($result);
return $result;
}
foreach ($result as $name)
{
$parts = explode(' ', $name);
if (count($parts) > 1)
{
$result = reset($parts);
$result = ucfirst($result);
return $result;
}
}
$result = reset($result);
$result = ucfirst($result);
return $result;
}
public function get_lastname()
{
$result = $this->get(__FUNCTION__);
$result = ucfirst($result);
return $result;
}
public function get_email()
{
return $this->get(__FUNCTION__);
}
public function get_language()
{
return $this->get(__FUNCTION__);
}
public function get_gender()
{
return $this->get(__FUNCTION__);
}
public function get_address()
{
return $this->get(__FUNCTION__);
}
public function get_staff_category()
{
return $this->get(__FUNCTION__);
}
public function get_home_organization_type()
{
return $this->get(__FUNCTION__);
}
public function get_home_organization()
{
return $this->get(__FUNCTION__);
}
public function get_affiliation()
{
return $this->get(__FUNCTION__);
}
/**
* @return ShibbolethUser
*/
public function get_user()
{
$result = new ShibbolethUser();
foreach ($result as $key => $val)
{
$f = array($this, "get_$key");
if (is_callable($f))
{
$result->{$key} = call_user_func($f);
}
}
return $result;
}
/**
* Returns the shibboleth value stored in $_SERVER if it exists or $default if it is not the case.
*
* @param string $name the generic name. I.e. one of the class const.
* @param string $default default value if it is not provided by Shibboleth
* @return string
*/
public function get($name = '', $default = '')
{
$config = (array) Shibboleth::config();
if ($name)
{
$name = str_replace('get_', '', $name);
$shib_name = isset($config[$name]) ? $config[$name] : '';
if ($shib_name)
{
$result = isset($_SERVER[$shib_name]) ? $_SERVER[$shib_name] : $default;
$result = explode(';', $result);
if (empty($result))
{
$result = $default;
}
else if (count($result) == 1)
{
$result = reset($result);
}
else
{
$result = $result;
}
return $result;
}
}
$result = array();
foreach ($config as $key => $val)
{
$f = array($this, "get_$key");
if (is_callable($f))
{
$result[$key] = call_user_func($f);
}
}
return $result;
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Shibboleth;
/**
* Represent a Shibboleth user. Not to be missunderstand with a Chamilo user
* since they don't have the same attributes.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethUser
{
public $unique_id = '';
public $firstname = '';
public $lastname = '';
public $email = '';
public $language = '';
public $gender = '';
public $address = '';
public $staff_category = '';
public $home_organization_type = '';
public $home_organization = '';
public $affiliation = '';
public $persistent_id = '';
public function is_empty()
{
return empty($this->unique_id);
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace Shibboleth;
require_once __DIR__.'/scaffold/user.class.php';
/**
* A Chamilo user. Model for the User table.
*
* Should be moved to the core. It only exists because it is not available through
* the API.
*
* The _User objet is generated by the scaffolder. User inherits from it to allow
* modifications without touching the generated file. Don't modify _User as
* it may change in the future. Instead add modifications to this class.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class User extends _User
{
}
/**
* Store for User objects. Interact with the database. Allows to save and retrieve
* user objects.
*
* Should be moved to the core. It only exists because it is not available through
* the API.
*
* The _UserStore objet is generated by the scaffolder. This class inherits from it to allow
* modifications without touching the generated file. Don't modify the _ object as
* it may change in the future. Instead add modifications to this class.
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class UserStore extends _UserStore
{
function __construct()
{
parent::__construct();
ShibbolethUpgrade::update();
}
/**
*
* @param string $id
* @return User
*/
public function get_by_shibboleth_id($id)
{
return $this->get(array('shibb_unique_id' => $id));
}
/**
* @param string $id
*/
public function shibboleth_id_exists($id)
{
return $this->exist(array('shibb_unique_id' => $id));
}
/**
*
* @param User $object
*/
protected function before_save($object)
{
$object->username = $object->username ? $object->username : $this->generate_username();
$object->password = $object->password ? $object->password : api_generate_password();
$object->language = $object->language ? $object->language : $this->default_language();
}
function default_language()
{
return api_get_setting('platformLanguage');
}
function generate_username()
{
$result = uniqid('s', true);
$result = str_replace('.', '', $result);
while ($this->username_exists($result))
{
$result = uniqid('s', true);
$result = str_replace('.', '', $result);
}
return $result;
}
}

View File

@@ -0,0 +1,266 @@
<?php
namespace Shibboleth;
use \Redirect;
/**
* Shibboleth main class. Provides access to various Shibboleth sub components and
* provides the high level functionalities.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class Shibboleth
{
const NAME = 'shibboleth';
const UNKNOWN_STATUS = -1;
const TEACHER_STATUS = 1;
const STUDENT_STATUS = 5;
static $config = null;
public static function format_status($status)
{
if ($status == Shibboleth::TEACHER_STATUS) {
return 'Teacher';
} else if ($status == Shibboleth::STUDENT_STATUS) {
return 'Student';
} else if ($status == Shibboleth::UNKNOWN_STATUS) {
return 'Unknown';
} else {
return '???';
}
}
/**
*
* @return ShibbolethConfig
*/
public static function config()
{
self::$config = self::$config ? self::$config : new ShibbolethConfig();
return self::$config;
}
public static function set_config($config)
{
self::$config = $config;
}
/**
*
* @return ShibbolethSession
*/
public static function session()
{
return ShibbolethSession::instance();
}
/**
*
* @return ShibbolethStore
*/
public static function store()
{
return ShibbolethStore::instance();
}
/**
*
* @return ShibbolethDisplay
*/
public static function display()
{
return ShibbolethDisplay::instance();
}
public static function sys_path()
{
$path = __DIR__.'/../';
return $path;
}
public static function url($path = '')
{
$result = api_get_path('WEB_PATH');
$result .= '/main/auth/shibboleth/' . $path;
return $result;
}
public static function redirect($url = '')
{
if (empty($url)) {
$url = isset($_SESSION['shibb_direct_url']) ? $_SESSION['shibb_direct_url'] : '';
unset($_SESSION['shibb_direct_url']);
/*
* Tests if the user tried to login directly in a protected course before to come here
* (this variable could be set in the modified code of /chamilo/inc/lib/main_api.lib.php)
*
* Note:
* this part was added to give the possibility to access Chamilo directly on a course URL from a link diplayed in a portal.
* This is not a direct Shibboleth related functionnality, but this could be used in a shibbolethized
* Dokeos installation, mainly if you have a SSO system in your network.
* Please note that the file /claroline/inc/lib/main_api.lib.php must be adapted to your Shibboleth settings
* If any interest or question, please contact Nicolas.Rod_at_adm.unige.ch
*
*/
}
if ($url) {
//needed to log the user in his courses. Normally it is done by visiting /chamilo/index.php
// $include_path = api_get_path(INCLUDE_PATH);
// require("$include_path/local.inc.php");
//
// if (strpos($url, '?') === false) {
// $url = "$url?";
// }
//
// $rootWeb = api_get_path('WEB_PATH');
// $first_slash_pos = strpos($rootWeb, '/', 8);
// $rootWeb_wo_uri = substr($rootWeb, 0, $first_slash_pos);
// $url = $rootWeb_wo_uri . $course_url . '_stop';
Redirect::go($url);
}
Redirect::home();
}
/**
*
* @param ShibbolethUser $shibb_user
*/
public static function save($shibb_user)
{
$shibb_user->status = self::infer_user_status($shibb_user);
$shibb_user->status_request = self::infer_status_request($shibb_user);
$shibb_user->shibb_unique_id = $shibb_user->unique_id;
$shibb_user->shibb_persistent_id = $shibb_user->persistent_id;
$user = User::store()->get_by_shibboleth_id($shibb_user->unique_id);
if (empty($user)) {
$shibb_user->auth_source == self::NAME;
return User::create($shibb_user)->save();
}
$shibb_user->status_request = false;
$fields = self::config()->update_fields;
foreach ($fields as $key => $updatable) {
if ($updatable) {
$user->{$key} = $shibb_user->{$key};
}
}
$user->auth_source == self::NAME;
$user->shibb_unique_id = $shibb_user->shibb_unique_id;
$user->shibb_persistent_id = $shibb_user->shibb_persistent_id;
$user->save();
return $result;
}
/**
* Infer the rights/status the user can have in Chamilo based on his affiliation attribute
*
* @param ShibbolethUser $user
* @return The Chamilo user status, one of TEACHER, STUDENT or UNKNOWN
*/
public static function infer_user_status($user)
{
$affiliations = $user->affiliation;
$affiliations = is_array($affiliations) ? $affiliations : array($affiliations);
$map = self::config()->affiliation_status;
$rights = array();
foreach ($affiliations as $affiliation) {
$affiliation = strtolower($affiliation);
if (isset($map[$affiliation])) {
$right = $map[$affiliation];
$rights[$right] = $right;
}
}
$teacher_status = isset($rights[self::TEACHER_STATUS]);
$student_status = isset($rights[self::STUDENT_STATUS]);
//if the user has got teacher rights, we doesn't check anything else
if ($teacher_status) {
return self::TEACHER_STATUS;
}
if ($student_status) {
return self::STUDENT_STATUS;
}
$result = self::config()->default_status;
$result = (int) $result;
$result = ($result == Shibboleth::TEACHER_STATUS || $result == Shibboleth::STUDENT_STATUS) ? $result : Shibboleth::UNKNOWN_STATUS;
return $result;
}
/**
* Return true if the user can ask for a greater status than student.
* This happens for staff members.
*
* @param ShibbolethUser $user
* @return boolean
*/
public static function infer_status_request($user)
{
if ($user->status == self::TEACHER_STATUS) {
return false;
}
if ($user->status == self::UNKNOWN_STATUS) {
return true;
}
$config = Shibboleth::config();
$affiliations = $user->affiliation;
$affiliations = is_array($affiliations) ? $affiliations : array($affiliations);
foreach ($affiliations as $affiliation) {
$result = isset($config->affiliation_status_request[$affiliation]) ? $config->affiliation_status_request[$affiliation] : false;
if ($result) {
return true;
}
}
return false;
}
/**
* Sends an email to the Chamilo and Shibboleth administrators in the name
* of the logged-in user.
*
* @param string $subject
*/
public static function email_admin($subject, $message)
{
$user = Shibboleth::session()->user();
$firstname = $user['firstname'];
$lastname = $user['lastname'];
$email = $user['email'];
$status = $user['status'];
$status = self::format_status($status);
$signagure = <<<EOT
_________________________
$firstname $lastname
$email
$status
EOT;
$message .= $signagure;
$header = "From: $email \n";
$shibb_admin_email = Shibboleth::config()->admnistrator_email;
if ($shibb_admin_email) {
$header .= "Cc: $shibb_admin_email";
}
$administrator_email = api_get_setting('emailAdministrator');
$result = mail($administrator_email, $subject, $message);
return (bool) $result;
}
}

View File

@@ -0,0 +1,18 @@
<?php
namespace Shibboleth;
/**
* Administratrive login. Useful when the standard login is not available anymore
* which is usually the case.
*
* This page allow administrators to log into the application using the standard
* Chamilo method when Shibboleth is not available.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
$dir = __DIR__;
include_once "$dir/../../init.php";
ShibbolethController::instance()->admin_login();

View File

@@ -0,0 +1,20 @@
<?php
namespace Shibboleth;
/**
* Display the Request another status/additional rights. The request is emailed
* to the shibboleth and platform administrators for processing.
*
* Users such as staff that can be either student or teachers are presented with
* this page upon first login.
*
* Other users - teachers, students - are directly logged-in.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
$dir = __DIR__;
include_once "$dir/../../init.php";
ShibbolethController::instance()->request_status();

View File

@@ -0,0 +1,66 @@
<?php
namespace Shibboleth;
use \Display;
/**
* Utility display functions tailored for the Shibboleth pluging.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethDisplay
{
/**
*
* @return ShibbolethDisplay
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
/**
* @param string $message
*/
public function error_page($message)
{
$page_title = get_lang('ShibbolethLogin');
Display :: display_header($page_title);
echo Display::return_message($message, 'error');
Display :: display_footer();
die;
}
/**
* @param string $message
*/
public function message_page($message, $title = '')
{
$title = $title ? $title : get_lang('ShibbolethLogin');
Display::display_header($title);
echo Display::return_message($message, 'confirm');
Display::display_footer();
die;
}
public function page($content, $title = '')
{
$title = $title ? $title : get_lang('ShibbolethLogin');
Display :: display_header($title);
echo $content;
Display :: display_footer();
die;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Shibboleth;
/**
* Enter email form. When the email is mandatory and the Shibboleth email user field
* is empty the system display this form and ask the user to provide an email.
*
* @todo: add email validation
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethEmailForm
{
/**
*
* @return ShibbolethEmailForm
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
function display()
{
$email = get_lang('Email');
$submit = get_lang('Submit');
return <<<EOT
<form id="email_form" action="" method="post">
<label for="">$email</label>
<input type="text" value="" tabindex="1" name="email" id="email_email" class=""><br/>
<input type="submit" value="$submit" tabindex="2" name="submit" id="email_submit" class="submit">
</form>
EOT;
}
function get_email()
{
return isset($_POST['email']) ? $_POST['email'] : '';
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Shibboleth;
use Display;
/**
* Status request form. Display a form allowing the user to request additional
* rights/ another status.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethStatusRequestForm
{
/**
*
* @return ShibbolethStatusRequestForm
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
function display()
{
if ($this->is_submitted() && $this->get_reason() == '')
{
$reason_is_mandatory = get_lang('ReasonIsMandatory');
echo Display::return_message($reason_is_mandatory, 'error');
}
$status_request_message = get_lang('StatusRequestMessage');
$label_new_status = get_lang('NewStatus');
$label_reason = get_lang('Reason');
$label_ok = get_lang('Ok');
$label_cancel = get_lang('Cancel');
$user = Shibboleth::session()->user();
$items = array();
if ($user['status'] == Shibboleth::UNKNOWN_STATUS)
{
$items[Shibboleth::STUDENT_STATUS] = get_lang('Student');
}
$items[Shibboleth::TEACHER_STATUS] = get_lang('Teacher');
$status_options = '';
foreach ($items as $key => $value)
{
$status_options.= "<option value=\"$key\">$value</option>";
}
return <<<EOT
<div id="askAccountText">
<p>$status_request_message</p>
</div>
<form method="post" action="request.php" id="status_request_form">
<input type="hidden" name="formPosted" value="true"/>
<label for="status">$label_new_status:</label>
<select name="status">
$status_options
</select>
<label for="reason">$label_reason:</label>
<textarea name="reason" style="min-width:400px; min-height:100px;"></textarea>
<p><input name="submit" type="submit" value="$label_ok" style="margin-right:10px;"/><input name="cancel" type="submit" value="$label_cancel" /></p>
</form>
EOT;
}
public function is_submitted()
{
return isset($_POST['submit']) ? $_POST['submit'] : false;
}
public function cancelled()
{
return isset($_POST['cancel']) ? $_POST['cancel'] : false;
}
function get_reason()
{
return isset($_POST['reason']) ? $_POST['reason'] : '';
}
function get_status()
{
return isset($_POST['status']) ? $_POST['status'] : '';
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Shibboleth;
/**
* Example of a config.php file. Not used. Configuration must appear in
* config.php.
*
* By default set up the aai configuration.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
require_once __DIR__.'/config/aai.class.php';
Shibboleth::set_config(aai::config());

View File

@@ -0,0 +1,69 @@
<?php
namespace Shibboleth;
/**
* Shibboleth configuration for the AAI federation.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class aai
{
/**
*
* @return ShibbolethConfig
*/
public static function config()
{
$result = new ShibbolethConfig();
$result->unique_id = 'Shib-SwissEP-UniqueID';
$result->firstname = 'Shib-InetOrgPerson-givenName';
$result->lastname = 'Shib-Person-surname';
$result->email = 'Shib-InetOrgPerson-mail';
$result->language = 'Shib-InetOrgPerson-preferredLanguage';
$result->gender = 'Shib-SwissEP-Gender';
$result->address = 'Shib-OrgPerson-postalAddress';
$result->staff_category = 'Shib-SwissEP-StaffCategory';
$result->home_organization_type = 'Shib-SwissEP-HomeOrganizationType';
$result->home_organization = 'Shib-SwissEP-HomeOrganization';
$result->affiliation = 'Shib-EP-Affiliation';
$result->persistent_id = 'persistent-id';
$result->default_status = Shibboleth::STUDENT_STATUS;
$result->affiliation_status = array(
'faculty' => Shibboleth::TEACHER_STATUS,
'member' => Shibboleth::STUDENT_STATUS,
'staff' => Shibboleth::STUDENT_STATUS,
'student' => Shibboleth::STUDENT_STATUS,
);
$result->update_fields = array(
'firstname' => true,
'lastname' => true,
'email' => true,
'status' => false,
'persistent_id' => true,
);
/*
* Persistent id should never change but it was introduced after unique id.
* So we update persistent id on login for those users who are still missing it.
*/
$result->is_email_mandatory = true;
$result->affiliation_status_request = array(
'faculty' => false,
'member' => false,
'staff' => true,
'student' => false,
);
$result->admnistrator_email = '';
return $result;
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace Shibboleth;
use \Database;
/**
* Migrate the datatabase. Adds needed fields by Shibboleth to the User table.
* Upgrade is checked at each user login so there is no need to manually run
* an upgrade.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethUpgrade
{
/**
* Create additional fields required by the shibboleth plugin if those
* are missing.
*/
public static function update()
{
static $done = false;
if ($done)
{
return false;
}
$done = true;
self::create_shibb_unique_id_field_if_missing();
self::create_shibb_persistent_id_field_if_missing();
}
/**
* Creates the 'shibb_unique_id' field in the table 'user' of the main Chamilo database if it doesn't exist yet
*
* @author Nicolas Rod
* @return false|null
*/
public static function create_shibb_unique_id_field_if_missing()
{
$db_name = Database :: get_main_database();
$sql = "SELECT * FROM `$db_name`.`user` LIMIT 1";
$result = Database::query($sql);
$row = mysql_fetch_assoc($result);
$exists = array_key_exists('shibb_unique_id', $row);
if ($exists)
{
return false;
}
//create the 'shibb_unique_id' field
$sql = "ALTER TABLE `$db_name`.`user` ADD `shibb_unique_id` VARCHAR( 60 ) AFTER `auth_source`";
$result_alter = Database::query($sql);
/*
* Index cannot be a UNIQUE index as it may exist users which don't log in through Shibboleth
* and therefore don't have any value for 'shibb_unique_id'
*/
$sql = "ALTER TABLE `$db_name`.`user` ADD INDEX ( `shibb_unique_id` )";
$result_alter = Database::query($sql);
}
public static function create_shibb_persistent_id_field_if_missing()
{
$db_name = Database :: get_main_database();
$sql = "SELECT * FROM $db_name.user LIMIT 1";
$result = Database::query($sql);
$row = mysql_fetch_assoc($result);
$exists = array_key_exists('shibb_persistent_id', $row);
if ($exists)
{
return false;
}
$sql = "ALTER table $db_name.user ADD COLUMN shibb_persistent_id varchar(255) NULL DEFAULT NULL;";
$result = Database::query($sql);
return (bool) $result;
}
}

View File

@@ -0,0 +1,8 @@
<?php
/**
* Display nothing. This ensure Apache doesn't display the list of files and folders
* when it is not propertly configured.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/

View File

@@ -0,0 +1,23 @@
<?php
namespace Shibboleth;
/**
* Initialize the Shibboleth authentication system. All scripts that can be directly
* called must include this file
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
$__dir = __DIR__.'/';
$no_redirection = true; //no redirection in global.
include_once($__dir . '/../../inc/global.inc.php');
require_once $__dir . 'config.php';
if (api_get_setting('server_type') == 'test')
{
include_once $__dir . '/test/shibboleth_test_helper.class.php';
include_once $__dir . '/test/shibboleth_test.class.php';
}

View File

@@ -0,0 +1,14 @@
<?php
namespace Shibboleth;
/**
* Description of model
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class Model
{
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Shibboleth;
/**
* Scaffolder. Genereate code templates from the database layout.
* See /template/ for the code being generated
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class Scaffolder
{
/**
*
* @staticvar boolean $result
* @return Scaffolder
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
public function scaffold($table_name, $class_name = '', $prefix = '_')
{
$db_name = Database :: get_main_database();
$sql = "SELECT * FROM `$db_name`.`$table_name` LIMIT 1";
$fields = array();
$unique_fields = array();
$rs = Database::query($sql, null, __FILE__);
while ($field = mysql_fetch_field($rs))
{
$fields[] = $field;
if ($field->primary_key)
{
/**
* Could move that to an array to support multiple keys
*/
$id_name = $field->name;
}
if ($field->unique_key | $field->primary_key)
{
$keys[] = $field->name;
}
}
$name = $table_name;
$class_name = ucfirst($table_name);
ob_start();
include __DIR__.'/template/model.php';
$result = ob_get_clean();
return $result;
}
}

View File

@@ -0,0 +1,146 @@
<?php
/**
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
echo '<?php';
?>
namespace Shibboleth;
/**
* This file is autogenerated. Do not modifiy it.
*/
/**
*
* Model for table <?php echo $table_name ?>
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class <?php echo $prefix . $class_name ?>
{
/**
* Store for <?php echo $class_name ?> objects. Interact with the database.
*
* @return <?php echo $class_name ?>Store
*/
public static function store()
{
static $result = false;
if (empty($result))
{
$result = new <?php echo $class_name ?>Store();
}
return $result;
}
/**
*
* @return <?php echo $class_name ?>
*/
public static function create($data = null)
{
return self::store()->create_object($data);
}
<?php foreach($fields as $field){?>
public $<?php echo $field->name; ?> = <?php echo $field->def ? $field->def : 'null'; ?>;
<?php }?>
/**
*
* @return bool
*/
public function save()
{
return self::store()->save($this);
}
}
/**
* Store for <?php echo $class_name ?> objects. Interact with the database.
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class <?php echo $prefix . $class_name ?>Store extends Store
{
/**
*
* @return <?php echo $class_name ?>Store
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
public function __construct()
{
parent::__construct('<?php echo $table_name;?>', '<?php echo $class_name;?>', '<?php echo $id_name;?>');
}
/**
*
* @return <?php echo $class_name ?>
*/
public function get($w)
{
$args = func_get_args();
$f = array('parent', 'get');
return call_user_func_array($f, $args);
}
/**
*
* @return <?php echo $class_name ?>
*/
public function create_object($data)
{
return parent::create_object($data);
}
<?php foreach($keys as $key){?>
/**
*
* @return <?php echo $class_name ?>
*/
public function get_by_<?php echo $key ?>($value)
{
return $this->get(array('<?php echo $key; ?>' => $value));
}
/**
*
* @return bool
*/
public function <?php echo $key ?>_exists($value)
{
return $this->exist(array('<?php echo $key; ?>' => $value));
}
/**
*
* @return bool
*/
public function delete_by_<?php echo $key ?>($value)
{
return $this->delete(array('<?php echo $key; ?>' => $value));
}
<?php }?>
}

View File

@@ -0,0 +1,146 @@
<?php
/**
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
echo '<?php';
?>
namespace Shibboleth;
/**
* This file is autogenerated. Do not modifiy it.
*/
/**
*
* Model for table <?php echo $table_name ?>
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class <?php echo $prefix . $class_name ?>
{
/**
* Store for <?php echo $class_name ?> objects. Interact with the database.
*
* @return <?php echo $class_name ?>Store
*/
public static function store()
{
static $result = false;
if (empty($result))
{
$result = new <?php echo $class_name ?>Store();
}
return $result;
}
/**
*
* @return <?php echo $class_name ?>
*/
public static function create($data = null)
{
return self::store()->create_object($data);
}
<?php foreach($fields as $field){?>
public $<?php echo $field->name; ?> = <?php echo $field->def ? $field->def : 'null'; ?>;
<?php }?>
/**
*
* @return bool
*/
public function save()
{
return self::store()->save($this);
}
}
/**
* Store for <?php echo $class_name ?> objects. Interact with the database.
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class <?php echo $prefix . $class_name ?>Store extends Store
{
/**
*
* @return <?php echo $class_name ?>Store
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
public function __construct()
{
parent::__construct('<?php echo $table_name;?>', '<?php echo $class_name;?>', '<?php echo $id_name;?>');
}
/**
*
* @return <?php echo $class_name ?>
*/
public function get($w)
{
$args = func_get_args();
$f = array('parent', 'get');
return call_user_func_array($f, $args);
}
/**
*
* @return <?php echo $class_name ?>
*/
public function create_object($data)
{
return parent::create_object($data);
}
<?php foreach($keys as $key){?>
/**
*
* @return <?php echo $class_name ?>
*/
public function get_by_<?php echo $key ?>($value)
{
return $this->get(array('<?php echo $key; ?>' => $value));
}
/**
*
* @return bool
*/
public function <?php echo $key ?>_exists($value)
{
return $this->exist(array('<?php echo $key; ?>' => $value));
}
/**
*
* @return bool
*/
public function delete_by_<?php echo $key ?>($value)
{
return $this->delete(array('<?php echo $key; ?>' => $value));
}
<?php }?>
}

View File

@@ -0,0 +1,39 @@
<?php
/**
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
echo '<?php';
?>
namespace Shibboleth;
/**
*
* Model for table <?php echo $table_name ?>
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class <?php echo $class_name ?>
{
}
/**
* Store for <?php echo $class_name ?> objects. Interact with the database.
*
* @copyright (c) 2012 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author Laurent Opprecht <laurent@opprecht.info>
*/
class <?php echo $class_name ?>Store extends Store
{
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Shibboleth;
/**
* Shibboleth configuration. All configuration for the Shibboleth authentication
* plugin: field names mapping, etc.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethConfig
{
public $unique_id = '';
public $firstname = '';
public $lastname = '';
public $email = '';
public $language = '';
public $gender = '';
public $address = '';
public $staff_category = '';
public $home_organization_type = '';
public $home_organization = '';
public $affiliation = '';
public $persistent_id = '';
public $default_status = Shibboleth::UNKNOWN_STATUS;
/**
* Mapping of affiliation => right
* @var array
*/
public $affiliation_status = array();
/**
* Mapping of affiliation => bool. Display the request status form.
* @var array
*/
public $affiliation_status_request = array();
/**
* List of fields to update when the user already exists field_name => boolean.
* @var array
*/
public $update_fields = array();
/*
* True if email is mandatory. False otherwise.
*/
public $is_email_mandatory = true;
/**
* The email of the shibboleth administrator.
*
* @var string
*/
public $admnistrator_email = '';
}

View File

@@ -0,0 +1,100 @@
<?php
namespace Shibboleth;
use ChamiloSession as Session;
use Database;
use Event;
/**
* A Chamilo user session. Used as there is no session object so far provided by the core API.
* Should be moved to the core library.Prefixed by Shibboleth to avoid name clashes.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethSession
{
/**
* @return ShibbolethSession
*/
public static function instance()
{
static $result = false;
if (empty($result)) {
$result = new self();
}
return $result;
}
function is_logged_in()
{
return isset($_SESSION['_user']['user_id']);
}
function user()
{
return $_SESSION['_user'];
}
function logout()
{
$_SESSION['_user'] = array();
online_logout(null, false);
global $logoutInfo;
Event::courseLogout($logoutInfo);
}
/**
* Create a Shibboleth session for the user ID
*
* @param string $uid The user ID
* @return array $_user The user infos array created when the user logs in
*/
function login($uid)
{
/* This must be set for local.inc.php to register correctly the global variables in session
* This is BAD. Logic should be migrated into a function and stop relying on global variables.
*/
global $_uid, $is_allowedCreateCourse, $is_platformAdmin, $_real_cid, $is_courseAdmin;
global $is_courseMember, $is_courseTutor, $is_session_general_coach, $is_allowed_in_course, $is_sessionAdmin, $_gid;
$_uid = $uid;
//is_allowedCreateCourse
$user = User::store()->get_by_user_id($uid);
if (empty($user)) {
return;
}
$this->logout();
Session::instance();
Session::write('_uid', $_uid);
global $_user;
$_user = (array) $user;
$_SESSION['_user'] = $_user;
$_SESSION['_user']['user_id'] = $_uid;
$_SESSION['noredirection'] = true;
//must be called before 'init_local.inc.php'
Event::eventLogin($_uid);
//used in 'init_local.inc.php' this is BAD but and should be changed
$loginFailed = false;
$uidReset = true;
$gidReset = true;
$cidReset = false; //FALSE !!
$mainDbName = Database :: get_main_database();
$includePath = api_get_path(SYS_INC_PATH);
$no_redirection = true;
require("$includePath/local.inc.php");
return $_user;
}
}

View File

@@ -0,0 +1,357 @@
<?php
namespace Shibboleth;
use \Database;
/**
* A database store. Used interact with the database - save objects, run queries.
*
* One store = one table.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class Store
{
/**
*
* @return Store
*/
public static function create($table_name, $class_name = '', $id_name = 'id', $db_name = '')
{
return new self($table_name, $class_name, $id_name, $db_name);
}
protected $db_name = '';
protected $table_name = '';
protected $id_name = '';
protected $class_name = '';
function __construct($table_name, $class_name = '', $id_name = 'id', $db_name = '')
{
$this->db_name = $db_name ? $db_name : Database::get_main_database();
$this->table_name = $table_name;
$this->class_name = $class_name;
$this->id_name = $id_name;
}
function get_db_name($object = '')
{
if ($this->db_name)
{
return $this->db_name;
}
if ($object)
{
$result = isset($object->{db_name}) ? $object->{db_name} : '';
$result = $result ? $result : Database :: get_main_database();
return $result;
}
return Database::get_main_database();
}
function get($w)
{
$args = func_get_args();
$f = array($this, 'get_where');
$db_name = $this->get_db_name();
$where = call_user_func_array($f, $args);
$sql = "SELECT *
FROM `{$db_name}`.`{$this->table_name}`
WHERE $where";
$items = $this->query($sql);
return (count($items) == 1) ? reset($items) : null;
}
function select($w)
{
$args = func_get_args();
$f = array($this, 'get_where');
$db_name = $this->get_db_name();
$where = call_user_func_array($f, $args);
$sql = "SELECT *
FROM `{$db_name}`.`{$this->table_name}`
WHERE $where";
$result = $this->query($sql);
return $result;
}
function exist($w)
{
$args = func_get_args();
$f = array($this, 'get');
$object = call_user_func_array($f, $args);
return !empty($object);
}
function is_new($object)
{
$id_name = $this->id_name;
$id = isset($object->{$id_name}) ? $object->{$id_name} : false;
return empty($id);
}
function save($object)
{
if (empty($object))
{
return false;
}
$object = is_array($object) ? $this->create_object($object) : $object;
$this->before_save($object);
if ($this->is_new($object))
{
$result = $this->insert($object);
}
else
{
$result = $this->update($object);
}
return $result;
}
function delete($object)
{
$args = func_get_args();
$f = array($this, 'get_where');
$db_name = $this->get_db_name();
$where = call_user_func_array($f, $args);
$sql = "DELETE
FROM `{$db_name
}
`.`{$this->table_name
}
`
WHERE $where";
$result = $this->query($sql);
return $result;
}
/**
*
* @param array|object $data
* @return object
*/
public function create_object($data = array())
{
$data = $data ? $data : array();
$data = (object) $data;
$class = $this->class_name;
if (empty($class))
{
return clone $data;
}
$result = new $class();
foreach ($result as $key => $value)
{
$result->{$key} = property_exists($data, $key) ? $data->{$key} : null;
}
return $result;
}
public function fields($object)
{
static $result = array();
if (!empty($result))
{
return $result;
}
$db_name = $this->get_db_name($object);
$sql = "SELECT *
FROM `{$db_name}`.`{$this->table_name}`
LIMIT 1";
$rs = Database::query($sql, null, __FILE__);
while ($field = mysql_fetch_field($rs))
{
$result[] = $field;
}
return $result;
}
protected function before_save($object)
{
//hook
}
protected function update($object)
{
$id = isset($object->{$this->id_name}) ? $object->{$this->id_name} : false;
if (empty($id))
{
return false;
}
$items = array();
$fields = $this->fields($object);
foreach ($fields as $field)
{
$name = $field->name;
if ($name != $this->id_name)
{
if (property_exists($object, $name))
{
$value = $object->{$name};
$value = $this->format_value($value);
$items[] = "$name=$value";
}
}
}
$db_name = $this->get_db_name($object);
$sql = "UPDATE `{$db_name}`.`{$this->table_name}` SET ";
$sql .= join(', ', $items);
$sql .= " WHERE {$this->id_name}=$id";
$result = $this->execute($sql);
if ($result)
{
$object->{db_name} = $db_name;
}
return (bool) $result;
}
protected function insert($object)
{
$id = isset($object->{$this->id_name}) ? $object->{$this->id_name} : false;
if (empty($object))
{
return false;
}
$values = array();
$keys = array();
$fields = $this->fields($object);
foreach ($fields as $field)
{
$name = $field->name;
if ($name != $this->id_name)
{
if (property_exists($object, $name))
{
$value = $object->{$name};
$value = is_null($value) ? 'DEFAULT' : $this->format_value($value);
$values[] = $value;
$keys[] = $name;
}
}
}
$db_name = $this->get_db_name($object);
$sql = "INSERT INTO `{$db_name}`.`{$this->table_name}` ";
$sql .= ' (' . join(', ', $keys) . ') ';
$sql .= 'VALUES';
$sql .= ' (' . join(', ', $values) . ') ';
$result = $this->execute($sql);
if ($result)
{
$id = mysql_insert_id();
$object->{$this->id_name} = $id;
$object->{db_name} = $db_name;
return $id;
}
else
{
return false;
}
}
protected function get_where($_)
{
$args = func_get_args();
if (count($args) == 1)
{
$arg = reset($args);
if (is_numeric($arg))
{
$id = (int) $arg;
if (empty($id))
{
return '';
}
$args = array($this->pk_name, $arg);
}
else if (is_string($arg))
{
return $arg;
}
else if (is_array($arg))
{
$args = $arg;
}
else
{
return $arg;
}
}
$items = array();
foreach ($args as $key => $val)
{
$items[] = $key . ' = ' . $this->format_value($val);
}
return implode(' AND ', $items);
}
protected function format_value($value)
{
if (is_null($value))
{
return 'NULL';
}
if (is_bool($var))
{
return $value ? '1' : '0';
}
else if (is_numeric($value))
{
return empty($value) ? '0' : $value;
}
else if (is_string($value))
{
$value = mysql_escape_string($value);
return "'$value'";
}
else
{
return $value;
}
}
/**
*
* @param string $sql
* @return array
*/
protected function query($sql)
{
$resource = Database::query($sql, null, __FILE__);
if ($resource == false)
{
return array();
}
$result = array();
while ($data = mysql_fetch_assoc($resource))
{
$result[] = $this->create_object($data);
}
return $result;
}
/**
* @param string $sql
*/
protected function execute($sql)
{
return Database::query($sql, null, __FILE__);
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace Shibboleth;
/**
* Shibboleth login page.
*
* Actual authentication is provided by the Shibboleth Apache security module.
* Shibboleth must be properly installed and configured. Then this page must
* be secured through an Apache security directive.
*
* When Shibboleth is properly set up this page will only be available for
* authenticated users. The plugin ensure those people are created and logged in.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
include_once __DIR__.'/init.php';
/*
==============================================================================
TEST SECTION
==============================================================================
*
* @todo: Only for testing. Comment that out for production
*
*/
//Shibboleth::session()->logout();
//ShibbolethTest::helper()->setup_new_student_no_email();
//ShibbolethTest::helper()->setup_staff();
//ShibbolethTest::helper()->setup_new_teacher();
//ShibbolethTest::helper()->setup_new_student();
//ShibbolethTest::helper()->setup_new_minimal_data();
ShibbolethController::instance()->login();

View File

@@ -0,0 +1,36 @@
<?php
namespace Shibboleth;
/**
* Scaffold script. Generates the required database models for the Shibboleth
* plugin.
*
* Will only run when the server is a test server.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
$dir = __DIR__;
include_once $dir.'/../init.php';
include_once $dir.'/../app/lib/scaffolder/scaffolder.class.php';
if (!ShibbolethTest::is_enabled())
{
echo 'This is not a test server';
die;
}
if (!Shibboleth::session()->is_logged_in())
{
echo 'Not authorized';
die;
}
$name = 'user';
$result = Scaffolder::instance()->scaffold($name);
file_put_contents("$dir/output/$name.class.php", $result);
header('content-type: text/plain');
echo $result;

View File

@@ -0,0 +1,218 @@
<?php
namespace Shibboleth;
/**
* Various Unit Tests. Note that those tests create users in the database but
* don't delete them.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethTest
{
static function is_enabled()
{
return api_get_setting('server_type') == 'test';
}
/**
* @return ShibbolethTestHelper
*/
static function helper()
{
return ShibbolethTestHelper::instance();
}
static function init()
{
if (!self::is_enabled())
{
die;
}
}
static function test_new_teacher()
{
self::init();
self::helper()->setup_new_teacher();
$shib_user = Shibboleth::store()->get_user();
self::assert(!User::store()->shibboleth_id_exists($shib_user->unique_id));
Shibboleth::save($shib_user);
$user = User::store()->get_by_shibboleth_id($shib_user->unique_id);
self::assert($user->email == $shib_user->email);
self::assert($user->firstname == $shib_user->firstname);
self::assert($user->lastname == $shib_user->lastname);
self::assert($user->persistent_id == $shib_user->persistent_id);
self::assert($user->status == Shibboleth::TEACHER_STATUS);
self::assert(!empty($user->password));
self::assert(!empty($user->username));
}
static function test_new_student()
{
self::init();
self::helper()->setup_new_student();
$shib_user = Shibboleth::store()->get_user();
self::assert(!User::store()->shibboleth_id_exists($shib_user->unique_id));
Shibboleth::save($shib_user);
$user = User::store()->get_by_shibboleth_id($shib_user->unique_id);
self::assert($user->email == $shib_user->email);
self::assert($user->firstname == $shib_user->firstname);
self::assert($user->lastname == $shib_user->lastname);
self::assert($user->persistent_id == $shib_user->persistent_id);
self::assert($user->status == Shibboleth::STUDENT_STATUS);
self::assert(!empty($user->password));
self::assert(!empty($user->username));
}
static function test_new_staff()
{
self::init();
self::helper()->setup_new_staff();
$shib_user = Shibboleth::store()->get_user();
self::assert(!User::store()->shibboleth_id_exists($shib_user->unique_id));
Shibboleth::save($shib_user);
$user = User::store()->get_by_shibboleth_id($shib_user->unique_id);
self::assert($user->email == $shib_user->email);
self::assert($user->firstname == $shib_user->firstname);
self::assert($user->lastname == $shib_user->lastname);
self::assert($user->persistent_id == $shib_user->persistent_id);
self::assert($user->status == Shibboleth::STUDENT_STATUS);
self::assert(!empty($user->password));
self::assert(!empty($user->username));
}
static function test_new_infer_status_request()
{
self::init();
self::helper()->setup_new_staff();
$shib_user = Shibboleth::store()->get_user();
Shibboleth::save($shib_user);
self::assert($shib_user->status_request);
self::helper()->setup_new_teacher();
$shib_user = Shibboleth::store()->get_user();
Shibboleth::save($shib_user);
self::assert(!$shib_user->status_request);
self::helper()->setup_new_student();
$shib_user = Shibboleth::store()->get_user();
Shibboleth::save($shib_user);
self::assert(!$shib_user->status_request);
}
static function test_update_teacher()
{
self::init();
$fields = Shibboleth::config()->update_fields;
self::assert($fields['email']);
self::assert($fields['persistent_id']);
self::assert($fields['firstname']);
self::assert($fields['lastname']);
self::assert(!$fields['status']);
self::helper()->setup_teacher();
$shib_user = Shibboleth::store()->get_user();
Shibboleth::save($shib_user);
$new_shib_user = clone($shib_user);
$new_shib_user->firstname = 'frs';
$new_shib_user->lastname = 'ls';
$new_shib_user->email = 'em';
$new_shib_user->status = 10;
$new_shib_user->persistent_id = 'per';
Shibboleth::save($new_shib_user);
$user = User::store()->get_by_shibboleth_id($shib_user->unique_id);
self::assert($user->email == $new_shib_user->email);
self::assert($value = ($user->shibb_persistent_id == $new_shib_user->persistent_id));
self::assert($user->firstname == $new_shib_user->firstname);
self::assert($user->lastname == $new_shib_user->lastname);
self::assert($user->status == $shib_user->status);
self::assert(!empty($user->password));
self::assert(!empty($user->username));
}
static function test_new_student_multiple_givenname()
{
self::init();
self::helper()->setup_new_student_multiple_givenname();
$shib_user = Shibboleth::store()->get_user();
self::assert(!User::store()->shibboleth_id_exists($shib_user->unique_id));
Shibboleth::save($shib_user);
$user = User::store()->get_by_shibboleth_id($shib_user->unique_id);
self::assert($user->email == $shib_user->email);
self::assert($user->firstname == 'John');
self::assert($user->lastname == $shib_user->lastname);
self::assert($user->persistent_id == $shib_user->persistent_id);
self::assert($user->status == Shibboleth::STUDENT_STATUS);
self::assert(!empty($user->password));
self::assert(!empty($user->username));
}
static function test_new_no_affiliation_default()
{
self::init();
self::helper()->setup_new_no_affiliation();
$shib_user = Shibboleth::store()->get_user();
self::assert($config = Shibboleth::config()->default_status == Shibboleth::STUDENT_STATUS);
self::assert(!User::store()->shibboleth_id_exists($shib_user->unique_id));
self::assert($shib_user->affiliation == '');
Shibboleth::save($shib_user);
$user = User::store()->get_by_shibboleth_id($shib_user->unique_id);
self::assert($user->email == $shib_user->email);
self::assert($user->firstname == 'John');
self::assert($user->lastname == $shib_user->lastname);
self::assert($user->persistent_id == $shib_user->persistent_id);
self::assert($user->status == Shibboleth::STUDENT_STATUS);
self::assert(!empty($user->password));
self::assert(!empty($user->username));
}
static function assert($assertion, $message = '')
{
if (!$assertion)
{
$message = "Assert failed $message <br/>";
echo $message;
// Dump variable for debug
error_log(print_r(debug_backtrace(), 1));
die;
}
else
{
$message = "Assert successful $message <br/>";
echo $message;
}
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace Shibboleth;
/**
* Helper functions for the tests. Set up various dummy user types: teacher, student, etc.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
class ShibbolethTestHelper
{
/**
*
* @return ShibbolethTestHelper
*/
public static function instance()
{
static $result = false;
if (empty($result))
{
$result = new self();
}
return $result;
}
public function setup_teacher()
{
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_1';
$_SERVER['Shib-EP-Affiliation'] = 'member;staff;faculty';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe';
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!drea34çcv3d';
}
public function setup_student()
{
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_1';
$_SERVER['Shib-EP-Affiliation'] = 'member';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe';
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!drea34çcv3d';
}
public function setup_staff()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_123456';
$_SERVER['Shib-EP-Affiliation'] = 'member;staff';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John Staff';
$_SERVER['Shib-Person-surname'] = 'Doe';
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.staff.doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!usr_123456';
}
public function setup_new_student()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_' . $id;
$_SERVER['Shib-EP-Affiliation'] = 'member';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe' . $id;
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.' . $id . 'Doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!' . md5($id);
}
public function setup_new_student_no_email()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_' . $id;
$_SERVER['Shib-EP-Affiliation'] = 'member';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe' . $id;
$_SERVER['Shib-InetOrgPerson-mail'] = '';
$_SERVER['persistent-id'] = 'idp!viewer!' . md5($id);
}
public function setup_new_student_multiple_givenname()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_' . $id;
$_SERVER['Shib-EP-Affiliation'] = 'member';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John;Alex;John Alex';
$_SERVER['Shib-Person-surname'] = 'Doe' . $id;
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.' . $id . 'Doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!' . md5($id);
}
public function setup_new_teacher()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_' . $id;
$_SERVER['Shib-EP-Affiliation'] = 'member;staff;faculty';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe' . $id;
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.' . $id . 'Doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!' . md5($id);
}
public function setup_new_staff()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_' . $id;
$_SERVER['Shib-EP-Affiliation'] = 'member;staff';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe' . $id;
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.' . $id . 'Doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!' . md5($id);
}
public function setup_new_no_affiliation()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_' . $id;
$_SERVER['Shib-EP-Affiliation'] = '';
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe' . $id;
$_SERVER['Shib-InetOrgPerson-mail'] = 'john.' . $id . 'Doe@localhost.org';
$_SERVER['persistent-id'] = 'idp!viewer!' . md5($id);
}
public function setup_new_minimal_data()
{
$id = uniqid();
$_SERVER['Shib-SwissEP-UniqueID'] = 'usr_' . $id;
$_SERVER['Shib-InetOrgPerson-givenName'] = 'John';
$_SERVER['Shib-Person-surname'] = 'Doe' . $id;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Shibboleth;
/**
* Run unit tests. Server needs to be a test server to run those.
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
include_once __DIR__.'/../init.php';
if (!ShibbolethTest::is_enabled())
{
echo 'This is not a test server';
die;
}
echo 'Test started<br/>-------------------<br/>';
ShibbolethTest::test_new_teacher();
ShibbolethTest::test_new_student();
ShibbolethTest::test_update_teacher();
ShibbolethTest::test_new_student_multiple_givenname();
ShibbolethTest::test_new_no_affiliation_default();
ShibbolethTest::test_new_staff();
ShibbolethTest::test_new_infer_status_request();
echo '-------------------<br/>Done!';

View File

@@ -0,0 +1,20 @@
<?php
namespace Shibboleth;
/**
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info>, Nicolas Rod for the University of Geneva
*/
include_once __DIR__.'/../init.php';
if (!ShibbolethTest::is_enabled())
{
echo 'This is not a test server';
die;
}
Shibboleth::session()->logout();
ShibbolethTest::helper()->setup_new_student_no_email();
require_once __DIR__.'/../login.php';

View File

@@ -0,0 +1,478 @@
<?php
/* For licensing terms, see /license.txt */
$cidReset = true; // Flag forcing the 'current course' reset
require_once __DIR__.'/../inc/global.inc.php';
api_block_anonymous_users();
$auth = new Auth();
$user_course_categories = CourseManager::get_user_course_categories(api_get_user_id());
$courses_in_category = $auth->getCoursesInCategory(false);
// Only authorized actions
$authorizedActions = [
'edit_category',
'edit_course_category',
'deletecoursecategory',
'createcoursecategory',
'set_collapsable',
'unsubscribe',
];
if (in_array(trim($_REQUEST['action']), $authorizedActions)) {
$action = trim($_REQUEST['action']);
}
$currentUrl = api_get_self();
$interbreadcrumb[] = [
'url' => api_get_self(),
'name' => get_lang('SortMyCourses'),
];
// We are moving the course of the user to a different user defined course category (=Sort My Courses).
if (isset($_POST['submit_change_course_category'])) {
$course2EditCategory = Security::remove_XSS($_POST['course_2_edit_category']);
$courseCategories = Security::remove_XSS($_POST['course_categories']);
$result = $auth->updateCourseCategory($course2EditCategory, $courseCategories);
if ($result) {
Display::addFlash(
Display::return_message(get_lang('EditCourseCategorySucces'))
);
}
header('Location: '.api_get_self());
exit;
}
// We edit course category
if (isset($_POST['submit_edit_course_category']) &&
isset($_POST['title_course_category'])
) {
$titleCourseCategory = Security::remove_XSS($_POST['title_course_category']);
$categoryId = Security::remove_XSS($_POST['category_id']);
$result = $auth->store_edit_course_category($titleCourseCategory, $categoryId);
if ($result) {
Display::addFlash(
Display::return_message(get_lang('CourseCategoryEditStored'))
);
}
header('Location: '.api_get_self());
exit;
}
// We are creating a new user defined course category (= Create Course Category).
if (isset($_POST['create_course_category']) &&
isset($_POST['title_course_category']) &&
strlen(trim($_POST['title_course_category'])) > 0
) {
$titleCourseCategory = Security::remove_XSS($_POST['title_course_category']);
$result = $auth->store_course_category($titleCourseCategory);
if ($result) {
Display::addFlash(
Display::return_message(get_lang('CourseCategoryStored'))
);
} else {
Display::addFlash(
Display::return_message(
get_lang('ACourseCategoryWithThisNameAlreadyExists'),
'error'
)
);
}
header('Location: '.api_get_self());
exit;
}
// We are moving a course or category of the user up/down the list (=Sort My Courses).
if (isset($_GET['move'])) {
$getCourse = isset($_GET['course']) ? Security::remove_XSS($_GET['course']) : '';
$getMove = Security::remove_XSS($_GET['move']);
$getCategory = isset($_GET['category']) ? Security::remove_XSS($_GET['category']) : '';
if (!empty($getCourse)) {
$result = $auth->move_course($getMove, $getCourse, $getCategory);
if ($result) {
Display::addFlash(
Display::return_message(get_lang('CourseSortingDone'))
);
}
}
if (!empty($getCategory) && empty($getCourse)) {
$result = $auth->move_category($getMove, $getCategory);
if ($result) {
Display::addFlash(
Display::return_message(get_lang('CategorySortingDone'))
);
}
}
header('Location: '.api_get_self());
exit;
}
switch ($action) {
case 'edit_category':
$categoryId = isset($_GET['category_id']) ? (int) $_GET['category_id'] : 0;
$categoryInfo = $auth->getUserCourseCategory($categoryId);
if ($categoryInfo) {
$categoryName = $categoryInfo['title'];
$form = new FormValidator(
'edit_course_category',
'post',
$currentUrl.'?action=edit_category'
);
$form->addText('title_course_category', get_lang('Name'));
$form->addHidden('category_id', $categoryId);
$form->addButtonSave(get_lang('Edit'), 'submit_edit_course_category');
$form->setDefaults(['title_course_category' => $categoryName]);
$form->display();
}
exit;
break;
case 'edit_course_category':
$edit_course = (int) $_GET['course_id'];
$defaultCategoryId = isset($_GET['category_id']) ? (int) $_GET['category_id'] : 0;
$courseInfo = api_get_course_info_by_id($edit_course);
if (empty($courseInfo)) {
exit;
}
$form = new FormValidator(
'edit_course_category',
'post',
$currentUrl.'?action=edit_course_category'
);
$form->addHeader($courseInfo['title']);
$options = [];
foreach ($user_course_categories as $row) {
$options[$row['id']] = $row['title'];
}
asort($options);
$form->addSelect(
'course_categories',
get_lang('Categories'),
$options,
['disable_js' => true, 'placeholder' => get_lang('SelectAnOption')]
);
$form->addHidden('course_2_edit_category', $edit_course);
if (!empty($defaultCategoryId)) {
$form->setDefaults(['course_categories' => $defaultCategoryId]);
}
$form->addButtonSave(get_lang('Save'), 'submit_change_course_category');
$form->display();
exit;
break;
case 'deletecoursecategory':
// we are deleting a course category
if (isset($_GET['id'])) {
if (Security::check_token('get')) {
$getId = Security::remove_XSS($_GET['id']);
$result = $auth->delete_course_category($getId);
if ($result) {
Display::addFlash(
Display::return_message(get_lang('CourseCategoryDeleted'))
);
}
}
}
header('Location: '.api_get_self());
exit;
break;
case 'createcoursecategory':
$form = new FormValidator(
'create_course_category',
'post',
$currentUrl.'?action=createcoursecategory'
);
$form->addText('title_course_category', get_lang('Name'));
$form->addButtonSave(get_lang('AddCategory'), 'create_course_category');
$form->display();
exit;
break;
case 'set_collapsable':
if (!api_get_configuration_value('allow_user_course_category_collapsable')) {
api_not_allowed(true);
}
$userId = api_get_user_id();
$categoryId = isset($_REQUEST['categoryid']) ? (int) $_REQUEST['categoryid'] : 0;
$option = isset($_REQUEST['option']) ? (int) $_REQUEST['option'] : 0;
$redirect = isset($_REQUEST['redirect']) ? Security::remove_XSS($_REQUEST['redirect']) : 0;
if (empty($userId) || empty($categoryId)) {
api_not_allowed(true);
}
$table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
$sql = "UPDATE $table
SET collapsed = $option
WHERE user_id = $userId AND id = $categoryId";
Database::query($sql);
Display::addFlash(Display::return_message(get_lang('Updated')));
if ($redirect === 'home') {
$url = api_get_path(WEB_PATH).'user_portal.php';
header('Location: '.$url);
exit;
}
$url = api_get_self();
header('Location: '.$url);
exit;
break;
}
function generateUnsubscribeForm(string $courseCode, string $secToken): string
{
$alertMessage = api_htmlentities(get_lang("ConfirmUnsubscribeFromCourse"), ENT_QUOTES);
$form = new FormValidator(
'frm_unsubscribe',
'get',
api_get_path(WEB_CODE_PATH).'auth/courses.php',
'',
[
'onsubmit' => 'javascript: if (!confirm(\''.addslashes($alertMessage).'\')) return false;',
],
FormValidator::LAYOUT_INLINE
);
$form->addHidden('action', 'unsubscribe');
$form->addHidden('sec_token', $secToken);
$form->addHidden('course_code', $courseCode);
$form->addButton('unsub', get_lang('Unsubscribe'));
return $form->returnForm();
}
Display::display_header();
$stok = Security::get_token();
$courses_without_category = isset($courses_in_category[0]) ? $courses_in_category[0] : null;
echo '<div id="actions" class="actions">';
if ($action != 'createcoursecategory') {
echo '<a class="ajax" href="'.$currentUrl.'?action=createcoursecategory">';
echo Display::return_icon('new_folder.png', get_lang('CreateCourseCategory'), '', '32');
echo '</a>';
}
echo '</div>';
if (!empty($message)) {
echo Display::return_message($message, 'confirm', false);
}
$allowCollapsable = api_get_configuration_value('allow_user_course_category_collapsable');
$teachersIcon = Display::return_icon('teacher.png', get_lang('Teachers'), null, ICON_SIZE_TINY);
// COURSES WITH CATEGORIES
if (!empty($user_course_categories)) {
$counter = 0;
$last = end($user_course_categories);
foreach ($user_course_categories as $row) {
echo Display::page_subheader($row['title']);
echo '<a name="category'.$row['id'].'"></a>';
$url = $currentUrl.'?categoryid='.$row['id'].'&sec_token='.$stok;
if ($allowCollapsable) {
if (isset($row['collapsed']) && $row['collapsed'] == 0) {
echo Display::url(
'<i class="fa fa-folder-open"></i>',
$url.'&action=set_collapsable&option=1'
);
} else {
echo Display::url(
'<i class="fa fa-folder"></i>',
$url.'&action=set_collapsable&option=0'
);
}
}
echo Display::url(
Display::return_icon('edit.png', get_lang('Edit'), '', 22),
$currentUrl.'?action=edit_category&category_id='.$row['id'].'&sec_token='.$stok,
['class' => 'ajax']
);
if (0 != $counter) {
echo Display::url(
Display::return_icon('up.png', get_lang('Up'), '', 22),
$currentUrl.'?move=up&category='.$row['id'].'&sec_token='.$stok
);
} else {
echo Display::return_icon('up_na.png', get_lang('Up'), '', 22);
}
if ($row['id'] != $last['id']) {
echo Display::url(
Display::return_icon('down.png', get_lang('Down'), '', 22),
$currentUrl.'?move=down&category='.$row['id'].'&sec_token='.$stok
);
} else {
echo Display::return_icon('down_na.png', get_lang('Down'), '', 22);
}
echo Display::url(
Display::return_icon(
'delete.png',
get_lang('Delete'),
[
'onclick' => "javascript: if (!confirm('".addslashes(
api_htmlentities(
get_lang('CourseCategoryAbout2bedeleted'),
ENT_QUOTES,
api_get_system_encoding()
)
)."')) return false;",
],
22
),
$currentUrl.'?action=deletecoursecategory&id='.$row['id'].'&sec_token='.$stok
);
$counter++;
echo '<br /><br />';
// Show the courses inside this category
echo '<table class="table table-hover table-striped data_table">';
$number_of_courses = isset($courses_in_category[$row['id']]) ? count($courses_in_category[$row['id']]) : 0;
$key = 0;
if (!empty($courses_in_category[$row['id']])) {
foreach ($courses_in_category[$row['id']] as $course) {
echo '<tr><td>';
echo '<a name="course'.$course['code'].'"></a>';
echo '<strong>'.$course['title'].'</strong>';
echo ' ('.$course['visual_code'].')';
echo '<br />';
echo $teachersIcon;
echo '&nbsp;';
echo CourseManager::getTeacherListFromCourseCodeToString($course['code']);
echo '<br />';
if (api_get_setting('display_teacher_in_courselist') === 'true') {
echo $course['tutor'];
}
echo '</td><td class="text-right">';
if (api_get_setting('show_courses_descriptions_in_catalog') === 'true') {
$icon_title = get_lang('CourseDetails').' - '.$course['title'];
$url = api_get_path(
WEB_CODE_PATH
).'inc/ajax/course_home.ajax.php?a=show_course_information&code='.$course['code'];
echo Security::remove_XSS(
Display::url(
Display::return_icon('info.png', $icon_title, '', '22'),
$url,
['class' => 'ajax', 'data-title' => $icon_title, 'title' => $icon_title]
)
);
echo Display::url(
Display::return_icon('edit.png', get_lang('Edit'), '', 22),
$currentUrl.'?action=edit_course_category&category_id='.$row['id'].'&course_id='.$course['real_id'].'&sec_token='.$stok,
['class' => 'ajax']
);
}
if ($key > 0) {
?>
<a href="<?php echo $currentUrl; ?>?action=<?php echo $action; ?>&amp;move=up&amp;course=<?php echo $course['code']; ?>&amp;category=<?php echo $course['user_course_cat']; ?>&amp;sec_token=<?php echo $stok; ?>">
<?php echo Display::display_icon('up.png', get_lang('Up'), '', 22); ?>
</a>
<?php
} else {
echo Display::display_icon('up_na.png', get_lang('Up'), '', 22);
}
if ($key < $number_of_courses - 1) {
?>
<a href="<?php echo $currentUrl; ?>?action=<?php echo $action; ?>&amp;move=down&amp;course=<?php echo $course['code']; ?>&amp;category=<?php echo $course['user_course_cat']; ?>&amp;sec_token=<?php echo $stok; ?>">
<?php echo Display::return_icon('down.png', get_lang('Down'), '', 22); ?>
</a>
<?php
} else {
echo Display::return_icon('down_na.png', get_lang('Down'), '', 22);
}
if ($course['status'] != 1 && $course['unsubscr'] == 1) {
echo generateUnsubscribeForm($course['code'], $stok);
}
$key++;
echo '</td></tr>';
}
echo '</table>';
}
}
}
echo Display::page_subheader(get_lang('NoCourseCategory'));
echo '<table class="table table-hover table-striped data_table">';
// COURSES WITHOUT CATEGORY
if (!empty($courses_without_category)) {
$number_of_courses = count($courses_without_category);
$key = 0;
foreach ($courses_without_category as $course) {
echo '<tr><td>';
echo '<a name="course'.$course['code'].'"></a>';
echo '<strong>'.$course['title'].'</strong>';
echo ' ('.$course['visual_code'].')';
echo '<br />';
echo $teachersIcon;
echo '&nbsp;';
echo CourseManager::getTeacherListFromCourseCodeToString($course['code']);
echo '<br />';
if (api_get_setting('display_teacher_in_courselist') === 'true') {
echo $course['tutor'];
}
echo '</td><td class="text-right">';
if (api_get_setting('show_courses_descriptions_in_catalog') === 'true') {
$icon_title = get_lang('CourseDetails').' - '.$course['title'];
$url = api_get_path(WEB_CODE_PATH).'inc/ajax/course_home.ajax.php?a=show_course_information&code='.$course['code'];
echo Security::remove_XSS(
Display::url(
Display::return_icon('info.png', $icon_title, '', '22'),
$url,
['class' => 'ajax', 'data-title' => $icon_title, 'title' => $icon_title]
)
);
}
echo '';
if (isset($_GET['edit']) && $course['code'] == $_GET['edit']) {
echo Display::return_icon('edit_na.png', get_lang('Edit'), '', 22);
} else {
echo Display::url(
Display::return_icon('edit.png', get_lang('Edit'), '', 22),
$currentUrl.'?action=edit_course_category&course_id='.$course['real_id'].'&'.$stok,
['class' => 'ajax']
);
}
if ($key > 0) {
?>
<a
href="<?php echo $currentUrl; ?>?action=<?php echo $action; ?>&amp;move=up&amp;course=<?php echo $course['code']; ?>&amp;category=<?php echo $course['user_course_cat']; ?>&amp;sec_token=<?php echo $stok; ?>">
<?php echo Display::display_icon('up.png', get_lang('Up'), '', 22); ?>
</a>
<?php
} else {
echo Display::return_icon('up_na.png', get_lang('Up'), '', 22);
}
if ($key < $number_of_courses - 1) {
?>
<a
href="<?php echo $currentUrl; ?>?action=<?php echo $action; ?>&amp;move=down&amp;course=<?php echo $course['code']; ?>&amp;category=<?php echo $course['user_course_cat']; ?>&amp;sec_token=<?php echo $stok; ?>">
<?php echo Display::display_icon('down.png', get_lang('Down'), '', 22); ?>
</a>
<?php
} else {
echo Display::return_icon('down_na.png', get_lang('Down'), '', 22);
}
if ($course['status'] != 1) {
if ($course['unsubscr'] == 1) {
echo generateUnsubscribeForm($course['code'], $stok);
}
}
echo '</td></tr>';
$key++;
}
}
?>
</table>
<?php
Display::display_footer();

View File

@@ -0,0 +1,60 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This file contains the necessary elements to implement a Single Sign On
* using chamilo as a SSO server.
*
* @package chamilo.auth.sso
*/
class SsoServer
{
/**
* This is used to get the url with the SSO params.
*
* @param string $refererSso
* @param array $additionalParams
*
* @return string
*/
public function getUrl($refererSso, $additionalParams = [])
{
if (empty($refererSso)) {
return null;
}
$getParams = parse_url($refererSso, PHP_URL_QUERY);
$userInfo = api_get_user_info(api_get_user_id(), false, true);
$chamiloUrl = api_get_path(WEB_PATH);
$sso = [
'username' => $userInfo['username'],
'secret' => sha1($userInfo['password']),
'master_domain' => $chamiloUrl,
'master_auth_uri' => $chamiloUrl.'?submitAuth=true',
'lifetime' => time() + 3600,
'target' => $refererSso,
];
if (!empty($additionalParams)) {
foreach ($additionalParams as $key => $value) {
if (!empty($key)) {
$sso[$key] = $value;
continue;
}
$sso[] = $value;
}
}
$cookie = base64_encode(serialize($sso));
return $refererSso
.($getParams ? '&' : '?')
.http_build_query([
'loginFailed' => 0,
'sso_referer' => $refererSso,
'sso_cookie' => $cookie,
]);
}
}

View File

@@ -0,0 +1,301 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* This file contains the necessary elements to implement a Single Sign On
* mechanism with an external Drupal application (on which the Chamilo module
* 7.x-1.0-alpha3 or above must be implemented).
*
* To use this class, set variable "sso_authentication_subclass" to "Drupal"
* in Chamilo settings. If not yet available in the "Security" tab, execute the
* following on the Chamilo database:
* INSERT INTO `settings_current` (`variable`, `type`, `category`, `selected_value`, `title`, `comment`, `access_url`)
* VALUES ('sso_authentication_subclass', 'textfield', 'Security', 'Drupal', 'SSOSubclass', 'SSOSubclassComment', 1);
*
* @package chamilo.auth.sso
*/
/**
* The SSO class allows for management of remote Single Sign On resources.
*/
class ssoDrupal
{
public $protocol; // 'http://',
public $domain; // 'localhost/project/drupal',
public $auth_uri; // '/?q=user',
public $deauth_uri; // '/?q=logout',
public $referer; // http://my.chamilo.com/main/auth/profile.php
/**
* Instanciates the object, initializing all relevant URL strings.
*/
public function __construct()
{
$this->protocol = api_get_setting('sso_authentication_protocol');
// There can be multiple domains, so make sure to take only the first
// This might be later extended with a decision process
$domains = preg_split('/,/', api_get_setting('sso_authentication_domain'));
$this->domain = trim($domains[0]);
$this->auth_uri = api_get_setting('sso_authentication_auth_uri');
$this->deauth_uri = api_get_setting('sso_authentication_unauth_uri');
//cut the string to avoid recursive URL construction in case of failure
$this->referer = $this->protocol.$_SERVER['HTTP_HOST'].substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], 'sso'));
$this->deauth_url = $this->protocol.$this->domain.$this->deauth_uri;
$this->master_url = $this->protocol.$this->domain.$this->auth_uri;
$this->referrer_uri = base64_encode($_SERVER['REQUEST_URI']);
$this->target = api_get_path(WEB_PATH);
}
/**
* Unlogs the user from the remote server.
*/
public function logout()
{
// no_redirect means Drupal sent the signal to logout. When redirecting to Drupal, the $_GET['stop'] param is
// set to 1, to allow Drupal to know that this is it, the logout is already done in Chamilo and there's no
// need to do it again
if (empty($_GET['no_redirect'])) {
header('Location: '.$this->deauth_url.'&stop=1');
} else {
header('Location: '.$this->protocol.$this->domain);
}
exit;
}
/**
* Sends the user to the master URL for a check of active connection.
*/
public function ask_master()
{
// Generate a single usage token that must be encoded by the master
$_SESSION['sso_challenge'] = api_generate_password(48);
// Redirect browser to the master URL
$params = '';
if (empty($_GET['no_redirect'])) {
$params = 'sso_referer='.urlencode($this->referer).
'&sso_target='.urlencode($this->target).
'&sso_challenge='.urlencode($_SESSION['sso_challenge']).
'&sso_ruri='.urlencode($this->referrer_uri);
if (strpos($this->master_url, "?") === false) {
$params = "?{$params}";
} else {
$params = "&{$params}";
}
}
header('Location: '.$this->master_url.$params);
exit;
}
/**
* Validates the received active connection data with the database.
*
* @return false|null Return the loginFailed variable value to local.inc.php
*/
public function check_user()
{
global $_user;
$loginFailed = false;
//change the way we recover the cookie depending on how it is formed
$sso = $this->decode_cookie($_GET['sso_cookie']);
//get token that should have been used and delete it
//from session since it can only be used once
$sso_challenge = '';
if (isset($_SESSION['sso_challenge'])) {
$sso_challenge = $_SESSION['sso_challenge'];
unset($_SESSION['sso_challenge']);
}
//lookup the user in the main database
$user_table = Database::get_main_table(TABLE_MAIN_USER);
$sql = "SELECT id, username, password, auth_source, active, expiration_date, status
FROM $user_table
WHERE username = '".trim(Database::escape_string($sso['username']))."'";
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
$uData = Database::fetch_array($result);
//Check the user's password
if ($uData['auth_source'] == PLATFORM_AUTH_SOURCE) {
if ($sso['secret'] === sha1($uData['username'].$sso_challenge.api_get_security_key())
&& ($sso['username'] == $uData['username'])) {
//Check if the account is active (not locked)
if ($uData['active'] == '1') {
// check if the expiration date has not been reached
if (empty($uData['expiration_date']) or $uData['expiration_date'] > date('Y-m-d H:i:s') or $uData['expiration_date'] == '0000-00-00 00:00:00') {
//If Multiple URL is enabled
if (api_get_multiple_access_url()) {
//Check the access_url configuration setting if the user is registered in the access_url_rel_user table
//Getting the current access_url_id of the platform
$current_access_url_id = api_get_current_access_url_id();
// my user is subscribed in these
//sites: $my_url_list
$my_url_list = api_get_access_url_from_user($uData['id']);
} else {
$current_access_url_id = 1;
$my_url_list = [1];
}
$my_user_is_admin = UserManager::is_admin($uData['id']);
if ($my_user_is_admin === false) {
if (is_array($my_url_list) && count($my_url_list) > 0) {
if (in_array($current_access_url_id, $my_url_list)) {
// the user has permission to enter at this site
$_user['user_id'] = $uData['id'];
$_user = api_get_user_info($_user['user_id']);
$_user['uidReset'] = true;
Session::write('_user', $_user);
Event::eventLogin($_user['user_id']);
// Redirect to homepage
$sso_target = '';
if (!empty($sso['ruri'])) {
//The referrer URI is *only* used if
// the user credentials are OK, which
// should be protection enough
// against evil URL spoofing...
$sso_target = api_get_path(WEB_PATH).base64_decode($sso['ruri']);
} else {
$sso_target = isset($sso['target']) ? $sso['target'] : api_get_path(WEB_PATH).'index.php';
}
header('Location: '.$sso_target);
exit;
} else {
// user does not have permission for this site
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
exit;
}
} else {
// there is no URL in the multiple
// urls list for this user
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
exit;
}
} else {
//Only admins of the "main" (first) Chamilo
// portal can login wherever they want
if (in_array(1, $my_url_list)) {
//Check if this admin is admin on the
// principal portal
$_user['user_id'] = $uData['id'];
$_user = api_get_user_info($_user['user_id']);
$is_platformAdmin = $uData['status'] == COURSEMANAGER;
Session::write('is_platformAdmin', $is_platformAdmin);
Session::write('_user', $_user);
Event::eventLogin($_user['user_id']);
} else {
//Secondary URL admin wants to login
// so we check as a normal user
if (in_array($current_access_url_id, $my_url_list)) {
$_user['user_id'] = $uData['user_id'];
$_user = api_get_user_info($_user['user_id']);
Session::write('_user', $_user);
Event::eventLogin($_user['user_id']);
} else {
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
exit;
}
}
}
} else {
// user account expired
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_expired');
exit;
}
} else {
//User not active
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_inactive');
exit;
}
} else {
//SHA1 of password is wrong
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_password');
exit;
}
} else {
//Auth_source is wrong
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_authentication_source');
exit;
}
} else {
//No user by that login
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_not_found');
exit;
}
return $loginFailed;
}
/**
* Generate the URL for profile editing for a any user or the current user.
*
* @param int $userId Optional. The user id
* @param bool $asAdmin Optional. Whether get the URL for the platform admin
*
* @return string If the URL is obtained return the drupal_user_id. Otherwise return false
*/
public function generateProfileEditingURL($userId = 0, $asAdmin = false)
{
$userId = intval($userId);
if (empty($userId)) {
$userId = api_get_user_id();
}
$userExtraFieldValue = new ExtraFieldValue('user');
$drupalUserIdData = $userExtraFieldValue->get_values_by_handler_and_field_variable(
$userId,
'drupal_user_id'
);
// If this is an administrator, allow him to make some changes in
// the Chamilo profile
if ($asAdmin && api_is_platform_admin(true)) {
return api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=$userId";
}
// If the user doesn't match a Drupal user, give the normal profile
// link
if ($drupalUserIdData === false) {
return api_get_path(WEB_CODE_PATH).'auth/profile.php';
}
// In all other cases, generate a link to the Drupal profile edition
$drupalUserId = $drupalUserIdData['value'];
$url = "{$this->protocol}{$this->domain}/user/{$drupalUserId}/edit";
return $url;
}
/**
* Decode the cookie (this function may vary depending on the
* Single Sign On implementation.
*
* @param string Encoded cookie
*
* @return array Parsed and unencoded cookie
*/
private function decode_cookie($cookie)
{
return UnserializeApi::unserialize(
'not_allowed_classes',
base64_decode($cookie)
);
}
}

304
main/auth/sso/sso.class.php Normal file
View File

@@ -0,0 +1,304 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* This file contains the necessary elements to implement a Single Sign On
* mechanism with an arbitrary external web application (given some light
* development there) and is based on the Drupal-Chamilo module implementation.
* To develop a new authentication mechanism, please extend this class and
* overwrite its method, then modify the corresponding calling code in
* main/inc/local.inc.php.
*
* @package chamilo.auth.sso
*/
/**
* The SSO class allows for management or remote Single Sign On resources.
*/
class sso
{
public $protocol; // 'http://',
public $domain; // 'localhost/project/drupal5',
public $auth_uri; // '/?q=user',
public $deauth_uri; // '/?q=logout',
public $referer; // http://my.chamilo.com/main/auth/profile.php
/*
* referrer_uri: [some/path/inside/Chamilo], might be used by module to
* redirect the user to where he wanted to go initially in Chamilo
*/
public $referrer_uri;
/**
* Instanciates the object, initializing all relevant URL strings.
*/
public function __construct()
{
$this->protocol = api_get_setting('sso_authentication_protocol');
// There can be multiple domains, so make sure to take only the first
// This might be later extended with a decision process
$domains = explode(',', api_get_setting('sso_authentication_domain'));
$this->domain = trim($domains[0]);
$this->auth_uri = api_get_setting('sso_authentication_auth_uri');
$this->deauth_uri = api_get_setting('sso_authentication_unauth_uri');
//cut the string to avoid recursive URL construction in case of failure
$this->referer = $this->protocol.$_SERVER['HTTP_HOST'].substr($_SERVER['REQUEST_URI'], 0, strpos($_SERVER['REQUEST_URI'], 'sso'));
$this->deauth_url = $this->protocol.$this->domain.$this->deauth_uri;
$this->master_url = $this->protocol.$this->domain.$this->auth_uri;
$this->referrer_uri = base64_encode($_SERVER['REQUEST_URI']);
$this->target = api_get_path(WEB_PATH);
}
/**
* Unlogs the user from the remote server.
*/
public function logout()
{
header('Location: '.$this->deauth_url);
exit;
}
/**
* Sends the user to the master URL for a check of active connection.
*/
public function ask_master()
{
$tempKey = api_generate_password(32);
$params = 'sso_referer='.urlencode($this->referer).
'&sso_target='.urlencode($this->target).
'&sso_challenge='.$tempKey.
'&sso_ruri='.urlencode($this->referrer_uri);
Session::write('tempkey', $tempKey);
if (strpos($this->master_url, "?") === false) {
$params = "?$params";
} else {
$params = "&$params";
}
header('Location: '.$this->master_url.$params);
exit;
}
/**
* Validates the received active connection data with the database.
*
* @return bool Return the loginFailed variable value to local.inc.php
*/
public function check_user()
{
global $_user;
$loginFailed = false;
//change the way we recover the cookie depending on how it is formed
$sso = $this->decode_cookie($_GET['sso_cookie']);
//error_log('check_user');
//error_log('sso decode cookie: '.print_r($sso,1));
//lookup the user in the main database
$user_table = Database::get_main_table(TABLE_MAIN_USER);
$sql = "SELECT user_id, username, password, auth_source, active, expiration_date, status
FROM $user_table
WHERE username = '".trim(Database::escape_string($sso['username']))."'";
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
//error_log('user exists');
$uData = Database::fetch_array($result);
//Check the user's password
if ($uData['auth_source'] == PLATFORM_AUTH_SOURCE) {
//This user's authentification is managed by Chamilo itself
// check the user's password
// password hash comes already parsed in sha1, md5 or none
/*
error_log($sso['secret']);
error_log($uData['password']);
error_log($sso['username']);
error_log($uData['username']);
*/
global $_configuration;
// Two possible authentication methods here: legacy using password
// and new using a temporary, session-fixed, tempkey
if ((
$sso['username'] == $uData['username']
&& $sso['secret'] === sha1(
$uData['username'].
Session::read('tempkey').
$_configuration['security_key']
)
)
or (
($sso['secret'] === sha1($uData['password']))
&& ($sso['username'] == $uData['username'])
)
) {
//error_log('user n password are ok');
//Check if the account is active (not locked)
if ($uData['active'] == '1') {
// check if the expiration date has not been reached
if (empty($uData['expiration_date'])
or $uData['expiration_date'] > date('Y-m-d H:i:s')
or $uData['expiration_date'] == '0000-00-00 00:00:00') {
//If Multiple URL is enabled
if (api_get_multiple_access_url()) {
//Check the access_url configuration setting if
// the user is registered in the access_url_rel_user table
//Getting the current access_url_id of the platform
$current_access_url_id = api_get_current_access_url_id();
// my user is subscribed in these
//sites: $my_url_list
$my_url_list = api_get_access_url_from_user($uData['user_id']);
} else {
$current_access_url_id = 1;
$my_url_list = [1];
}
$my_user_is_admin = UserManager::is_admin($uData['user_id']);
if ($my_user_is_admin === false) {
if (is_array($my_url_list) && count($my_url_list) > 0) {
if (in_array($current_access_url_id, $my_url_list)) {
// the user has permission to enter at this site
$_user['user_id'] = $uData['user_id'];
$_user = api_get_user_info($_user['user_id']);
$_user['uidReset'] = true;
Session::write('_user', $_user);
Event::eventLogin($_user['user_id']);
// Redirect to homepage
$sso_target = '';
if (!empty($sso['ruri'])) {
//The referrer URI is *only* used if
// the user credentials are OK, which
// should be protection enough
// against evil URL spoofing...
$sso_target = api_get_path(WEB_PATH).base64_decode($sso['ruri']);
} else {
$sso_target = isset($sso['target']) ? $sso['target'] : api_get_path(WEB_PATH).'index.php';
}
header('Location: '.$sso_target);
exit;
} else {
// user does not have permission for this site
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
exit;
}
} else {
// there is no URL in the multiple
// urls list for this user
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
exit;
}
} else {
//Only admins of the "main" (first) Chamilo
// portal can login wherever they want
if (in_array(1, $my_url_list)) {
//Check if this admin is admin on the
// principal portal
$_user['user_id'] = $uData['user_id'];
$_user = api_get_user_info($_user['user_id']);
$is_platformAdmin = $uData['status'] == COURSEMANAGER;
Session::write('is_platformAdmin', $is_platformAdmin);
Session::write('_user', $_user);
Event::eventLogin($_user['user_id']);
} else {
//Secondary URL admin wants to login
// so we check as a normal user
if (in_array($current_access_url_id, $my_url_list)) {
$_user['user_id'] = $uData['user_id'];
$_user = api_get_user_info($_user['user_id']);
Session::write('_user', $_user);
Event::eventLogin($_user['user_id']);
} else {
$loginFailed = true;
Session::erase('_uid');
header(
'Location: '.api_get_path(WEB_PATH)
.'index.php?loginFailed=1&error=access_url_inactive'
);
exit;
}
}
}
} else {
// user account expired
$loginFailed = true;
Session::erase('_uid');
header(
'Location: '.api_get_path(WEB_PATH)
.'index.php?loginFailed=1&error=account_expired'
);
exit;
}
} else {
//User not active
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_inactive');
exit;
}
} else {
//SHA1 of password is wrong
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_password');
exit;
}
} else {
//Auth_source is wrong
$loginFailed = true;
Session::erase('_uid');
header(
'Location: '.api_get_path(WEB_PATH)
.'index.php?loginFailed=1&error=wrong_authentication_source'
);
exit;
}
} else {
//No user by that login
$loginFailed = true;
Session::erase('_uid');
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_not_found');
exit;
}
return $loginFailed;
}
/**
* Generate the URL for profile editing for a any user or the current user.
*
* @param int $userId Optional. The user id
* @param bool $asAdmin Optional. Whether get the URL for the platform admin
*
* @return string The SSO URL
*/
public function generateProfileEditingURL($userId = 0, $asAdmin = false)
{
$userId = intval($userId);
if ($asAdmin && api_is_platform_admin(true)) {
return api_get_path(WEB_CODE_PATH)."admin/user_edit.php?user_id=$userId";
}
return api_get_path(WEB_CODE_PATH).'auth/profile.php';
}
/**
* Decode the cookie (this function may vary depending on the
* Single Sign On implementation.
*
* @param string Encoded cookie
*
* @return array Parsed and unencoded cookie
*/
private function decode_cookie($cookie)
{
return UnserializeApi::unserialize(
'not_allowed_classes',
base64_decode($cookie)
);
}
}

View File

@@ -0,0 +1,105 @@
<?php
/*
SSO sample
This is the "server" of my institution/university authentification "code"
1. Active all the SSO option in your Chamilo installation: main/admin/settings.php?category=Security
2. Copy the main/auth/sso/sso.class.php file to something else representing your remote system, like
sso.Remote.class.php and modify the class name in soo.Remote.class.php to "ssoRemote"
3. Insert the following setting manually in your database (change the selected_value from 'Remote'
to the name of your system (used in the filename and classname above).
INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext)
VALUES ('sso_authentication_subclass',NULL,'textfield','Security','Remote','SSOAuthSubClassTitle','SSOAuthSubClassComment',NULL,NULL);
4. Make sure this script is located in the index page of the server you fill in the "Domain of the Single Sign On server" Chamilo setting
For example this script must be located in example.com/index.php if you set the "Domain of the Single Sign On server" = example.com
5. Create a user in chamilo and in your external system with login = "joe" and password = "doe"
6. Remember this is just a sample! Check the chamilo drupal extension for more information:
http://drupal.org/node/817682
7. When activating the settings in step 1, the principal Chamilo file main/inc/local.inc.php will load the class main/auth/sso.[class.php library
* that will redirect to this field with some parameters.
*
*/
exit; //Uncomment this to execute the page
//After you located this file in you new domain and you set the settings in step 2,
//this page will be loaded when entering to the Chamilo site if the SSO option was set in step 1.
//Getting the chamilo server
$my_chamilo_server = filter_xss($_SERVER['HTTP_HOST']);
$account = [];
if (isset($_SESSION['my_server_user_session'])) {
//validate if the user is already logged in my external system in order to redirect to chamilo
}
//Login process
if (isset($_POST['user']) && isset($_POST['password'])) {
//1. Your Server validations
$validate = validate_user($_POST['user'], $_POST['password']);
if ($validate) {
/* 2.Get the chamilo username and password from your system or from webservices */
$account['username'] = 'jbrion525'; //username in Chamilo
$account['password'] = sha1(sha1('jbrion525')); //encrypted password with assuming that the first encrypted method is sha1 in chamilo
$master_auth_uri = $my_chamilo_server.'/?q=user';
// Creating an array cookie that will be sent to Chamilo
$sso = [
'username' => $account['username'],
'secret' => $account['password'],
'master_domain' => $my_chamilo_server,
'master_auth_uri' => $master_auth_uri,
'lifetime' => time() + 3600,
'target' => filter_xss($_GET['sso_target']),
];
$cookie = base64_encode(serialize($sso));
$url = chamilo_sso_protocol().$master_auth_uri;
$params = 'sso_referer='.urlencode($url).'&sso_cookie='.urlencode($cookie);
$final_url = filter_xss($_GET['sso_referer']).'?'.$params;
//If your user exists redirect to chamilo and set the account in a session to check it later
$_SESSION['my_server_user_session'] = $account;
//3. After validating the user in the server and getting and setting the user data of chamilo in the sso_cookie variable:
// Redirect to this URL
header('Location: '.$final_url);
exit;
} else {
echo '<h2>Wrong parameters</h2>';
}
}
if (isset($_POST['logout'])) {
//echo do something to logout
}
function validate_user($user, $pass)
{
return true;
}
function filter_xss($val)
{
//do some cleaning
return $val;
}
function chamilo_sso_protocol()
{
//get the sso_protocol from chamilo using webservices
return 'http://';
}
?>
<html>
<form method="post">
User <input name="user"/>
Pass <input name="password" />
<input type="submit" value="Login">
</form>
</html>

View File

@@ -0,0 +1,44 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../inc/global.inc.php';
if (api_get_setting('platform_unsubscribe_allowed') != 'true') {
api_not_allowed();
}
$tool_name = get_lang('Unsubscribe');
$message = Display::return_message(get_lang('UnsubscribeFromPlatform'), 'warning');
$form = new FormValidator('user_add');
$form->addElement(
'button',
'submit',
get_lang('Unsubscribe'),
[
'onclick' => "javascript:if(!confirm('".addslashes(api_htmlentities(get_lang("UnsubscribeFromPlatformConfirm")))."')) return false;",
]
);
$content = $form->returnForm();
if ($form->validate()) {
$user_info = api_get_user_info();
$result = UserManager::delete_user($user_info['user_id']);
if ($result) {
$message = Display::return_message(
sprintf(
get_lang('UnsubscribeFromPlatformSuccess'),
$user_info['username']
)
);
$content = null;
online_logout($user_info['user_id'], false);
api_not_allowed(true, $message);
}
}
$tpl = new Template($tool_name);
$tpl->assign('message', $message);
$tpl->assign('content', $content);
$tpl->display_one_col_template();

View File

@@ -0,0 +1,56 @@
<?php
/* For license terms, see /license.txt */
require_once __DIR__.'/../inc/global.inc.php';
$token = isset($_GET['token']) ? $_GET['token'] : '';
if (!ctype_alnum($token)) {
$token = '';
}
/** @var \Chamilo\UserBundle\Entity\User $user */
$user = UserManager::getManager()->findUserByConfirmationToken($token);
if ($user) {
$user->setActive(1); // Set to 1 to activate the user
$user->setConfirmationToken(null);
Database::getManager()->persist($user);
Database::getManager()->flush();
// See where to redirect the user to, if any redirection has been set
$url = api_get_path(WEB_PATH);
if (!empty($_GET['c'])) {
$courseCode = Security::remove_XSS($_GET['c']);
}
if (!empty($_GET['s'])) {
$sessionId = (int) $_GET['s'];
}
// Get URL to a course, to a session, or an empty string
$courseUrl = api_get_course_url($courseCode, $sessionId);
if (!empty($courseUrl)) {
$url = $courseUrl;
}
Event::addEvent(
LOG_USER_CONFIRMED_EMAIL,
LOG_USER_OBJECT,
api_get_user_info($user->getId()),
api_get_utc_datetime()
);
Display::addFlash(
Display::return_message(get_lang('UserConfirmedNowYouCanLogInThePlatform'), 'success')
);
header('Location: '.$url);
exit;
} else {
Display::addFlash(
Display::return_message(get_lang('LinkExpired'))
);
header('Location: '.api_get_path(WEB_PATH));
exit;
}