Enrol/LDAP - initial commit, a bit rough around the edges.

This code is in production in a high-volume environment, so it has already proven to be reliable and scalable. Configuration is still tricky, and some corner cases may or may not be handled elegantly.

Please test!
This commit is contained in:
martinlanghoff 2004-11-22 02:41:41 +00:00
parent 6e8ca9837e
commit 64031bbb0f
4 changed files with 1017 additions and 0 deletions

295
enrol/ldap/config.html Executable file
View File

@ -0,0 +1,295 @@
<?php // initialize variables
global $THEME;
// general
optional_variable($frm->enrol_ldap_host_url, '');
optional_variable($frm->enrol_ldap_version, '');
optional_variable($frm->enrol_ldap_bind_dn, '');
optional_variable($frm->ldap_bind_pw, '');
// student & teacher enrol
optional_variable($frm->enrol_ldap_student_contexts, '');
optional_variable($frm->enrol_ldap_student_memberattribute, '');
optional_variable($frm->enrol_ldap_teacher_contexts, '');
optional_variable($frm->enrol_ldap_teacher_memberattribute, '');
optional_variable($frm->enrol_ldap_objectclass, '');
optional_variable($frm->enrol_ldap_course_idnumber_updatelocal, '');
// fields
optional_variable($frm->enrol_ldap_course_idnumber, '');
optional_variable($frm->enrol_ldap_course_idnumber_updatelocal, false);
optional_variable($frm->enrol_ldap_course_idnumber_editlock, false);
optional_variable($frm->enrol_ldap_course_fullname, '');
optional_variable($frm->enrol_ldap_course_fullname_updatelocal, false);
optional_variable($frm->enrol_ldap_course_fullname_editlock, false);
optional_variable($frm->enrol_ldap_course_shortname, '');
optional_variable($frm->enrol_ldap_course_shortname_updatelocal, false);
optional_variable($frm->enrol_ldap_course_shortname_editlock, false);
optional_variable($frm->enrol_ldap_course_summary, '');
optional_variable($frm->enrol_ldap_course_summary_updatelocal, false);
optional_variable($frm->enrol_ldap_course_summary_editlock, false);
// autocreate
optional_variable($frm->enrol_ldap_autocreate, false);
optional_variable($frm->enrol_ldap_category, 1);
optional_variable($frm->enrol_ldap_template, '');
?>
<table cellspacing="0" cellpadding="5" border="0" align="center">
<tr>
<td colspan="2">
<h4><?php print_string("enrol_ldap_server_settings", "enrol_ldap") ?> </h4>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_host_url:</td>
<td>
<input name="enrol_ldap_host_url" TYPE="text" SIZE="30" value="<?php echo $frm->enrol_ldap_host_url?>">
<?php if (isset($err["enrol_ldap_host_url"])) formerr($err["enrol_ldap_host_url"]); ?>
</td>
<td>
<?php print_string("enrol_ldap_host_url","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_version:</td>
<td>
<?php
$versions[2] = "2";
$versions[3] = "3";
choose_from_menu($versions, "enrol_ldap_version", $frm->enrol_ldap_version, "");
if (isset($err["enrol_ldap_version"])) formerr($err["enrol_ldap_version"]);
?>
</td>
<td>
<?php print_string("enrol_ldap_version","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_bind_dn:</td>
<td>
<input name="enrol_ldap_bind_dn" type="text" size="30" value="<?php echo $frm->enrol_ldap_bind_dn?>">
<?php if (isset($err["enrol_ldap_bind_dn"])) formerr($err["ldap_bind_dn"]); ?>
</td><td>
<?php print_string("enrol_ldap_bind_dn","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>ldap_bind_pw:</td>
<td>
<input name="enrol_ldap_bind_pw" type="text" size="30" value="<?php echo $frm->enrol_ldap_bind_pw?>">
<?php if (isset($err["enrol_ldap_bind_pw"])) formerr($err["enrol_ldap_bind_pw"]); ?>
</td><td>
<?php print_string("enrol_ldap_bind_pw","enrol_ldap") ?>
</td>
</tr>
<tr>
<td colspan="2">
<h4><?php print_string("enrol_ldap_student_settings", "enrol_ldap") ?> </h4>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_student_contexts:</td>
<td>
<input name="enrol_ldap_student_contexts" TYPE="text" SIZE="30 "value="<?php echo $frm->enrol_ldap_student_contexts?>">
<?php if (isset($err["enrol_ldap_student_contexts"])) formerr($err["enrol_ldap_student_contexts"]); ?>
</td>
<td>
<?php print_string("enrol_ldap_student_contexts","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_student_memberattribute:</td>
<td>
<input name="enrol_ldap_student_memberattribute" type="text" size="30" value="<?php echo $frm->enrol_ldap_student_memberattribute?>">
<?php if (isset($err["enrol_ldap_student_memberattribute"])) formerr($err["enrol_ldap_student_memberattribute"]); ?>
</td><td>
<?php print_string("enrol_ldap_student_memberattribute","enrol_ldap") ?>
</td>
</tr>
<tr>
<td colspan="2">
<h4><?php print_string("enrol_ldap_teacher_settings", "enrol_ldap") ?> </h4>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_teacher_contexts:</td>
<td>
<input name="enrol_ldap_teacher_contexts" TYPE="text" SIZE="30 "value="<?php echo $frm->enrol_ldap_teacher_contexts?>">
<?php if (isset($err["enrol_ldap_teacher_contexts"])) formerr($err["enrol_ldap_teacher_contexts"]); ?>
</td>
<td>
<?php print_string("enrol_ldap_teacher_contexts","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_teacher_memberattribute:</td>
<td>
<input name="enrol_ldap_teacher_memberattribute" type="text" size="30" value="<?php echo $frm->enrol_ldap_teacher_memberattribute?>">
<?php if (isset($err["enrol_ldap_teacher_memberattribute"])) formerr($err["enrol_ldap_teacher_memberattribute"]); ?>
</td><td>
<?php print_string("enrol_ldap_teacher_memberattribute","enrol_ldap") ?>
</td>
</tr>
<tr>
<td colspan="2">
<h4><?php print_string("enrol_ldap_course_settings", "enrol_ldap") ?> </h4>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_objectclass:</td>
<td>
<input name=enrol_ldap_objectclass type="text" size="30" value="<?php echo $frm->enrol_ldap_objectclass?>">
<?php if (isset($err["enrol_ldap_objectclass"])) formerr($err["enrol_ldap_objectclass"]); ?>
</td>
<td>
<?php print_string("enrol_ldap_objectclass","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_course_idnumber:</td>
<td>
<input name="enrol_ldap_course_idnumber" type="text" size="30" value="<?php echo $frm->enrol_ldap_course_idnumber?>">
<?php if (isset($err["enrol_ldap_course_idnumber"])) formerr($err["enrol_ldap_course_idnumber"]); ?>
<div align="right">
<?php print_string("enrol_ldap_updatelocal", "enrol_ldap") ?>
<select name="enrol_ldap_course_idnumber_updatelocal">
<option value="0" <?php echo ($frm->enrol_ldap_course_idnumber_updatelocal ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_idnumber_updatelocal ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select><BR>
<?php print_string("enrol_ldap_editlock", "enrol_ldap") ?>
<select name="enrol_ldap_course_idnumber_editlock">
<option value="0" <?php echo ($frm->enrol_ldap_course_idnumber_editlock ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_idnumber_editlock ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select></div>
</td><td>
<?php print_string("enrol_ldap_course_idnumber","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_course_shortname:</td>
<td>
<input name="enrol_ldap_course_shortname" type="text" size="30" value="<?php echo $frm->enrol_ldap_course_shortname?>">
<?php if (isset($err["enrol_ldap_course_shortname"])) formerr($err["enrol_ldap_course_shortname"]); ?>
<div align="right">
<?php print_string("enrol_ldap_updatelocal", "enrol_ldap") ?>
<select name="enrol_ldap_course_shortname_updatelocal">
<option value="0" <?php echo ($frm->enrol_ldap_course_shortname_updatelocal ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_shortname_updatelocal ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select><BR>
<?php print_string("enrol_ldap_editlock", "enrol_ldap") ?>
<select name="enrol_ldap_course_shortname_editlock">
<option value="0" <?php echo ($frm->enrol_ldap_course_shortname_editlock ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_shortname_editlock ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select></div>
</td><td>
<?php print_string("enrol_ldap_course_shortname","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_course_fullname:</td>
<td>
<input name="enrol_ldap_course_fullname" type="text" size="30" value="<?php echo $frm->enrol_ldap_course_fullname ?>">
<?php if (isset($err["enrol_ldap_course_fullname"])) formerr($err["enrol_ldap_course_fullname"]); ?>
<div align="right">
<?php print_string("enrol_ldap_updatelocal", "enrol_ldap") ?>
<select name="enrol_ldap_course_fullname_updatelocal">
<option value="0" <?php echo ($frm->enrol_ldap_course_fullname_updatelocal ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_fullname_updatelocal ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select><BR>
<?php print_string("enrol_ldap_editlock", "enrol_ldap") ?>
<select name="enrol_ldap_course_idnumber_editlock">
<option value="0" <?php echo ($frm->enrol_ldap_course_fullname_editlock ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_fullname_editlock ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select></div>
</td><td>
<?php print_string("enrol_ldap_course_fullname","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_course_summary:</td>
<td>
<input name="enrol_ldap_course_summary" type="text" size="30" value="<?php echo $frm->enrol_ldap_course_summary?>">
<?php if (isset($err["enrol_ldap_course_summary"])) formerr($err["enrol_ldap_course_summary"]); ?>
<div align="right">
<?php print_string("enrol_ldap_updatelocal", "enrol_ldap") ?>
<select name="enrol_ldap_course_summary_updatelocal">
<option value="0" <?php echo ($frm->enrol_ldap_course_summary_updatelocal ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_summary_updatelocal ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select><BR>
<?php print_string("enrol_ldap_editlock", "enrol_ldap") ?>
<select name="enrol_ldap_course_summary_editlock">
<option value="0" <?php echo ($frm->enrol_ldap_course_summary_editlock ? '' : 'selected="yes"') ?> >
<?php print_string("no") ?></option>
<option value="1" <?php echo ($frm->enrol_ldap_course_summary_editlock ? 'selected="yes"' : '') ?> >
<?php print_string("yes") ?></option>
</select></div>
</td><td>
<?php print_string("enrol_ldap_course_summary","enrol_ldap") ?>
</td>
</tr>
<tr>
<td colspan="2">
<h4><?php print_string("enrol_ldap_autocreation_settings", "enrol_ldap") ?> </h4>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_autocreate:</td>
<td>
<?php
choose_from_menu(array('0'=>'no','1'=>'yes'), "enrol_ldap_autocreate", $frm->enrol_ldap_autocreate, "");
if (isset($err["enrol_ldap_autocreate"])) formerr($err["enrol_ldap_autocreate"]);
?>
</td>
<td>
<?php print_string("enrol_ldap_autocreate","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_category:</td>
<td>
<?php
$displaylist = array();
$parentlist = array();
make_categories_list($displaylist, $parentlist);
choose_from_menu($displaylist, "enrol_ldap_category", $frm->enrol_ldap_category, "");
?>
<?php if (isset($err["enrol_ldap_category"])) formerr($err["enrol_ldap_category"]); ?>
</td><td>
<?php print_string("enrol_ldap_category","enrol_ldap") ?>
</td>
</tr>
<tr valign="top" bgcolor="<?php echo $THEME->cellheading2 ?>">
<td align="right"><P>enrol_ldap_template:</td>
<td>
<input name=enrol_ldap_template type="text" size="30" value="<?php echo $frm->enrol_ldap_template?>">
<?php if (isset($err["enrol_ldap_template"])) formerr($err["enrol_ldap_template"]); ?>
</td><td>
<?php print_string("enrol_ldap_template","enrol_ldap") ?>
</td>
</tr>
</table>

624
enrol/ldap/enrol.php Executable file
View File

@ -0,0 +1,624 @@
<?php // $Id$
require_once("$CFG->dirroot/enrol/enrol.class.php");
require_once("$CFG->dirroot/course/lib.php");
require_once("$CFG->dirroot/lib/blocklib.php");
$CFG->enrol_localcoursefield = 'idnumber';
class enrolment_plugin extends enrolment_base {
var $log;
/// Leave get_teacher_courses() function unchanged for the time being
/// Leave cron() function unchanged
/// Overide the base get_student_courses() function
function get_student_courses(&$user) {
global $CFG;
parent::get_student_courses($user); // populate known enrolments
$this->get_user_courses($user, 'student');
return parent::get_student_courses($user);
}
/// Overide the base get_teacher_courses() function
function get_teacher_courses(&$user) {
global $CFG;
parent::get_teacher_courses($user); // populate known enrolments
$this->get_user_courses($user, 'teacher');
return parent::get_teacher_courses($user);
}
/// Overide the base get_student_courses() function
function get_user_courses(&$user, $type) {
global $CFG;
if(!isset($type) || !($type =='student' || $type =='teacher' )){
error("Bad call to get_user_courses()!");
}
// Connect to the external database
$ldap_connection = $this->enrol_ldap_connect();
if (!$ldap_connection) {
@ldap_close($ldap_connection);
error("LDAP-module cannot connect to server: $CFG->ldap_host_url");
return false;
}
// we are connected OK, continue...
/// Find a record in the external database that matches the local course field and local user field
/// to the respective remote fields
$enrolments = $this->find_ext_enrolments( $ldap_connection,
$user->idnumber ,
$type);
foreach ($enrolments as $enrol){
$course_ext_id = $enrol[$CFG->enrol_ldap_course_idnumber][0];
if(empty($course_ext_id)){
error_log("The course external id is invalid!\n");
continue; // next; skip this one!
}
// create the course ir required
$course_obj = get_record( 'course',
$CFG->enrol_localcoursefield,
$course_ext_id );
if (empty($course_obj)){ // course doesn't exist
if($CFG->enrol_ldap_autocreate){ // autocreate
error_log("CREATE User $user->username enrolled to a nonexistant course $course_ext_id \n");
$newcourseid = $this->create_course($enrol);
$course_obj = get_record( 'course',
$CFG->enrol_localcoursefield,
$newcourseid);
} else {
error_log("User $user->username enrolled to a nonexistant course $course_ext_id \n");
}
} else { // the course object exists before we call...
if ($courseobj->visible==0) {
// non-visible courses don't show up in the enrolled
// array, so we should skip them --
unset($user->{$type}[$course_obj->id]);
continue;
}
}
// deal with enrolment in the moodle db
if (!empty($course_obj)) { // does course exist now?
if(isset($user->{$type}[$course_obj->id])){
unset($user->{$type}[$course_obj->id]); // remove from old list
} else {
$CFG->debug=10;
if ($type === 'student') { // enrol
error_log("Enrolling student $user->id ($user->username) in course $course_obj->id ($course_obj->shortname) ");
if (! enrol_student($user->id, $course_obj->id, NULL,NULL, 'ldap')){
error_log("Failed to enrol student $user->id ($user->username) into course $course_obj->id ($course_obj->shortname)");
}
} else if ($type === 'teacher') {
error_log("Enrolling teacher $user->id ($user->username) in course $course_obj->id ($course_obj->shortname)");
add_teacher($user->id, $course_obj->id, NULL,NULL,NULL, NULL,'ldap');
}
$CFG->debug=0;
}
}
}
// ok, if there's any thing still left in the $user->student or $user->teacher
// array, those are old enrolments that we want to remove (or are they?)
if(!empty($user->{$type})){
$courses = array_keys($user->{$type});
foreach ($courses as $courseid){
// get the record if it's ldap
$rec = get_record('user_' . $type . 's', 'userid', $user->id, 'course', $courseid, 'enrol', 'ldap');
if(!empty($rec)){ // this was a legacy
if ($type === 'student') { // enrol
unenrol_student($user->id, $courseid);
} else if ($type === 'teacher') {
remove_teacher($user->id, $courseid);
}
unset($user->{$type}[$course_obj->id]);
}
}
}
@ldap_close($ldap_connection);
return true;
}
/// sync enrolments with ldap, create courses if required.
function sync_enrolments($type, $enrol) {
global $CFG;
if(!isset($type) || !($type =='student' || $type =='teacher' )){
error("Bad call to get_all_courses()!");
}
$table = array( 'teacher' => 'user_teachers',
'student' => 'user_students');
if(!isset($enrol)){
$enrol=false;
}
// Connect to the external database
$ldap_connection = $this->enrol_ldap_connect();
if (!$ldap_connection) {
@ldap_close($ldap_connection);
error("LDAP-module cannot connect to server: $CFG->ldap_host_url");
return false;
}
// we are connected OK, continue...
$this->enrol_ldap_bind($ldap_connection);
//get all contexts and look for first matching user
$ldap_contexts = explode(";",$CFG->{'enrol_ldap_'.$type.'_contexts'});
// get all the fields we will want for the potential course creation
// as they are light. don't get membership -- potentially a lot of data.
$ldap_fields_wanted = array( 'dn', $CFG->enrol_ldap_course_idnumber);
if (!empty($CFG->enrol_ldap_course_fullname)){
array_push($ldap_fields_wanted, $CFG->enrol_ldap_course_fullname);
}
if (!empty($CFG->enrol_ldap_course_shortname)){
array_push($ldap_fields_wanted, $CFG->enrol_ldap_course_shortname);
}
if (!empty($CFG->enrol_ldap_course_summary)){
array_push($ldap_fields_wanted, $CFG->enrol_ldap_course_summary);
}
if($enrol){
array_push($ldap_fields_wanted, $CFG->{'enrol_ldap_'.$type.'_memberattribute'});
}
// define the search pattern
if (!empty($CFG->enrol_ldap_objectclass)){
$ldap_search_pattern='(objectclass='.$CFG->enrol_ldap_objectclass.')';
} else {
$ldap_search_pattern="(objectclass='*')";
}
// first, pack the sortorder...
fix_course_sortorder();
foreach ($ldap_contexts as $context) {
$context == trim($context);
if ($CFG->enrol_ldap_search_sub){
//use ldap_search to find first user from subtree
$ldap_result = ldap_search($ldap_connection,
$context,
$ldap_search_pattern,
$ldap_fields_wanted);
} else {
//search only in this context
$ldap_result = ldap_list($ldap_connection,
$context,
$ldap_search_pattern,
$ldap_fields_wanted,0,0);
}
// check and push results
$records = ldap_get_entries($ldap_connection,$ldap_result);
// ldap libraries return an odd array, really. fix it:
$flat_records=array();
for ($c=0;$c<$records['count'];$c++) {
array_push($flat_records, $records["$c"]);
}
// free mem -- is there a leak?
$records=0; $ldap_result=0;
if (count($flat_records)) {
foreach($flat_records as $course){
$idnumber = $course{$CFG->enrol_ldap_course_idnumber}[0];
print "== Synching $idnumber\n";
// does the course exist in moodle already?
$course_obj = false;
$course_obj = get_record( 'course',
$CFG->enrol_localcoursefield,
$idnumber );
if (!is_object($course_obj)) {
// ok, now then let's create it!
print "Creating Course $idnumber...";
$newcourseid = $this->create_course($course, true); // we are skipping fix_course_sortorder()
$course_obj = get_record( 'course', 'id', $newcourseid);
if (is_object($course_obj)) {
print "OK!\n";
} else {
print "failed\n";
}
}
// enrol&unenrol if required
if($enrol && is_object($course_obj)){
// pull the ldap membership into a nice array
// this is an odd array -- mix of hash and array --
$ldapmembers=array();
if(!empty($course{ strtolower($CFG->{'enrol_ldap_'.$type.'_memberattribute'} ) })){ // may have no membership!
$ldapmembers = $course{ strtolower($CFG->{'enrol_ldap_'.$type.'_memberattribute'} ) };
unset($ldapmembers{'count'}); // remove oddity ;)
}
// prune old ldap enrolments
// hopefully they'll fit in the max buffer size for the RDBMS
$sql = ' SELECT enr.userid AS user, 1
FROM ' . $CFG->prefix . $table{$type} . ' enr ' .
' JOIN ' . $CFG->prefix . 'user usr ON usr.id=enr.userid ' .
" WHERE course=$course_obj->id
AND enrol='ldap' ";
if (!empty($ldapmembers)) {
$sql .= 'AND usr.idnumber NOT IN (\''. join('\',\'', $ldapmembers).'\')';
} else {
print ("Empty enrolment for $course_obj->shortname \n");
}
$todelete = get_records_sql($sql);
if(!empty($todelete)){
foreach ($todelete as $member) {
$member = $member->{'user'};
if($type==='student'){
if (unenrol_student($member, $course_obj->id)){
print "Unenrolled $type $member into course $course_obj->id ($course_obj->shortname)\n";
} else {
print "Failed to unenrol $type $member into course $course_obj->id ($course_obj->shortname)\n";
}
} else {
if (remove_teacher($member, $course_obj->id)){
print "Unenrolled $type $member into course $course_obj->id ($course_obj->shortname)\n";
} else {
print "Failed to unenrol $type $member into course $course_obj->id ($course_obj->shortname)\n";
}
}
}
}
// insert current enrolments
// bad we can't do INSERT IGNORE with postgres...
foreach ($ldapmembers as $ldapmember) {
$sql = 'SELECT id,1 FROM '.$CFG->prefix.'user '
." WHERE idnumber='$ldapmember'";
$member = get_record_sql($sql);
// print "sql: $sql \nidnumber = $ldapmember \n" . var_dump($member);
$member = $member->id;
if(empty($member)){
print "Could not find user $ldapmember, skipping\n";
continue;
}
if (!get_record($table{$type}, 'course', $course_obj->id,
'userid', $member, 'enrol', 'ldap')){
if($type === 'student'){
if (enrol_student($member, $course_obj->id, NULL,NULL, 'ldap')){
print "Enrolled $type $member ($ldapmember) into course $course_obj->id ($course_obj->shortname)\n";
} else {
print "Failed to enrol $type $member ($ldapmember) into course $course_obj->id ($course_obj->shortname)\n";
}
} else { // teacher
if (add_teacher($member, $course_obj->id, NULL,NULL,NULL, NULL,'ldap')){
print "Enrolled $type $member ($ldapmember) into course $course_obj->id ($course_obj->shortname)\n";
} else {
print "Failed to enrol $type $member ($ldapmember) into course $course_obj->id ($course_obj->shortname)\n";
}
}
}
}
}
}
}
}
// we are done now, a bit of housekeeping
fix_course_sortorder();
@ldap_close($ldap_connection);
return true;
}
/// Override the base print_entry() function
function print_entry($course) {
global $CFG;
if (! empty($CFG->enrol_allowinternal) ) {
parent::print_entry($course);
} else {
print_header();
notice(get_string("enrolmentnointernal"), $CFG->wwwroot);
}
}
/// Override the base check_entry() function
function check_entry($form, $course) {
global $CFG;
if (! empty($CFG->enrol_allowinternal) ) {
parent::check_entry($form, $course);
}
}
/// Overide the get_access_icons() function
function get_access_icons($course) {
}
/// Overrise the base config_form() function
function config_form($frm) {
global $CFG;
include("$CFG->dirroot/enrol/ldap/config.html");
}
/// Override the base process_config() function
function process_config($config) {
optional_variable($config->enrol_ldap_host_url, '');
set_config('enrol_ldap_host_url', $config->enrol_ldap_host_url);
optional_variable($config->enrol_ldap_version, '');
set_config('enrol_ldap_version', $config->enrol_ldap_version);
optional_variable($config->enrol_ldap_bind_dn, '');
set_config('enrol_ldap_bind_dn', $config->enrol_ldap_bind_dn);
optional_variable($config->enrol_ldap_bind_pw, '');
set_config('enrol_ldap_bind_pw', $config->enrol_ldap_bind_pw);
optional_variable($config->enrol_ldap_student_contexts, '');
set_config('enrol_ldap_student_contexts', $config->enrol_ldap_student_contexts);
optional_variable($config->enrol_ldap_student_memberattribute, '');
set_config('enrol_ldap_student_memberattribute', $config->enrol_ldap_student_memberattribute);
optional_variable($config->enrol_ldap_teacher_contexts, '');
set_config('enrol_ldap_teacher_contexts', $config->enrol_ldap_teacher_contexts);
optional_variable($config->enrol_ldap_teacher_memberattribute, '');
set_config('enrol_ldap_teacher_memberattribute', $config->enrol_ldap_teacher_memberattribute);
optional_variable($config->enrol_ldap_objectclass, '');
set_config('enrol_ldap_objectclass', $config->enrol_ldap_objectclass);
optional_variable($config->enrol_ldap_category, '');
set_config('enrol_ldap_category', $config->enrol_ldap_category);
optional_variable($config->enrol_ldap_template, '');
set_config('enrol_ldap_template', $config->enrol_ldap_template);
optional_variable($config->enrol_ldap_course_fullname, '');
set_config('enrol_ldap_course_fullname', $config->enrol_ldap_course_fullname);
optional_variable($config->enrol_ldap_course_shortname, '');
set_config('enrol_ldap_course_shortname', $config->enrol_ldap_course_shortname);
optional_variable($config->enrol_ldap_course_summary, '');
set_config('enrol_ldap_course_summary', $config->enrol_ldap_course_summary);
optional_variable($config->enrol_ldap_course_idnumber, '');
set_config('enrol_ldap_course_idnumber', $config->enrol_ldap_course_idnumber);
optional_variable($config->enrol_localcoursefield, '');
set_config('enrol_localcoursefield', $config->enrol_localcoursefield);
optional_variable($config->enrol_ldap_user_memberfield, '');
set_config('enrol_ldap_user_memberfield', $config->enrol_ldap_user_memberfield);
optional_variable($config->enrol_ldap_search_sub, '0');
set_config('enrol_ldap_search_sub', $config->enrol_ldap_search_sub);
optional_variable($config->enrol_ldap_autocreate, '0');
set_config('enrol_ldap_autocreate', $config->enrol_ldap_autocreate);
if (!isset($config->enrol_allowinternal)) {
$config->enrol_allowinternal = '';
}
set_config('enrol_allowinternal', $config->enrol_allowinternal);
return true;
}
function enrol_ldap_connect(){
/// connects to ldap-server
global $CFG;
$result = ldap_connect($CFG->enrol_ldap_host_url);
if ($result) {
if (!empty($CFG->enrol_ldap_version)) {
ldap_set_option($result, LDAP_OPT_PROTOCOL_VERSION, $CFG->enrol_ldap_version);
}
return $result;
} else {
error("LDAP-module cannot connect to server: $CFG->enrol_ldap_host_url");
return false;
}
}
function enrol_ldap_bind($ldap_connection){
/// makes bind to ldap for searching users
/// uses ldap_bind_dn or anonymous bind
global $CFG;
if ( ! empty($CFG->enrol_ldap_bind_dn) ){
//bind with search-user
if (!ldap_bind($ldap_connection, $CFG->enrol_ldap_bind_dn,$CFG->enrol_ldap_bind_dn)){
error("Error: could not bind ldap with ldap_bind_dn/pw");
return false;
}
} else {
//bind anonymously
if ( !ldap_bind($ldap_connection)){
error("Error: could not bind ldap anonymously");
return false;
}
}
return true;
}
function find_ext_enrolments ($ldap_connection, $memberuid, $type){
/// type is teacher or student
/// return multidimentional array array with of courses (at least dn and idnumber)
///
global $CFG;
//default return value
$courses = array();
$this->enrol_ldap_bind($ldap_connection);
//get all contexts and look for first matching user
$ldap_contexts = explode(";",$CFG->{'enrol_ldap_'.$type.'_contexts'});
// get all the fields we will want for the potential course creation
// as they are light. don't get membership -- potentially a lot of data.
$ldap_fields_wanted = array( 'dn', $CFG->enrol_ldap_course_idnumber);
if (!empty($CFG->enrol_ldap_course_fullname)){
array_push($ldap_fields_wanted, $CFG->enrol_ldap_course_fullname);
}
if (!empty($CFG->enrol_ldap_course_shortname)){
array_push($ldap_fields_wanted, $CFG->enrol_ldap_course_shortname);
}
if (!empty($CFG->enrol_ldap_course_summary)){
array_push($ldap_fields_wanted, $CFG->enrol_ldap_course_summary);
}
// define the search pattern
$ldap_search_pattern = "(".$CFG->{'enrol_ldap_'.$type.'_memberattribute'}."=".$memberuid.")";
if (!empty($CFG->enrol_ldap_objectclass)){
$ldap_search_pattern='(&(objectclass='.$CFG->enrol_ldap_objectclass.')'.$ldap_search_pattern.')';
}
foreach ($ldap_contexts as $context) {
$context == trim($context);
if ($CFG->enrol_ldap_search_sub){
//use ldap_search to find first user from subtree
$ldap_result = ldap_search($ldap_connection,
$context,
$ldap_search_pattern,
$ldap_fields_wanted);
} else {
//search only in this context
$ldap_result = ldap_list($ldap_connection,
$context,
$ldap_search_pattern,
$ldap_fields_wanted);
}
// check and push results
$records = ldap_get_entries($ldap_connection,$ldap_result);
// ldap libraries return an odd array, really. fix it:
$flat_records=array();
for ($c=0;$c<$records['count'];$c++) {
array_push($flat_records, $records["$c"]);
}
if (count($flat_records)) {
$courses = array_merge($courses, $flat_records);
}
}
return $courses;
}
// will create the moodle course from the template
// course_ext is an array as obtained from ldap -- flattened somewhat
// NOTE: if you pass true for $skip_fix_course_sortorder
// you will want to call fix_course_sortorder() after your are done
// with course creation
function create_course ($course_ext,$skip_fix_course_sortorder=0){
global $CFG;
// set defaults
$course = NULL;
$course->student = 'Student';
$course->students = 'Students';
$course->teacher = 'Teacher';
$course->teachers = 'Teachers';
$course->format = 'topics';
$course->blockinfo = blocks_get_default_blocks();
// override defaults with template course
if(!empty($CFG->enrol_ldap_template)){
$course = get_record("course", 'shortname', $CFG->enrol_ldap_template);
unset($course->id); // so we are clear to reinsert the record
unset($course->sortorder);
}
// override with required ext data
$course->idnumber = $course_ext[$CFG->enrol_ldap_course_idnumber][0];
$course->fullname = $course_ext[$CFG->enrol_ldap_course_fullname][0];
$course->shortname = $course_ext[$CFG->enrol_ldap_course_shortname][0];
if ( empty($course->idnumber)
|| empty($course->fullname)
|| empty($course->shortname) ) {
// we are in trouble!
error_log("Cannot create course: missing required data from the LDAP record!");
error_log(var_export($course, true));
return false;
}
if(!empty($CFG->enrol_ldap_course_summary)){ // optional
$course->summary = $course_ext->{$CFG->enrol_ldap_course_summary}[0];
}
if(!empty($CFG->enrol_ldap_category)){ // optional ... but ensure it is set!
$course->category = $CFG->enrol_ldap_category;
}
if ($course->category == 0){ // must be avoided as it'll break moodle
$course->category = 1; // the misc 'catch-all' category
}
// define the sortorder (yuck)
$sort = get_record_sql('SELECT MAX(sortorder) AS max, 1 FROM ' . $CFG->prefix . 'course WHERE category=' . $course->category);
$sort = $sort->max;
$sort++;
$course->sortorder = $sort;
// override with local data
$course->startdate = time();
$course->timecreated = time();
$course->visible = 1;
// store it and log
if ($newcourseid = insert_record("course", $course)) { // Set up new course
$section = NULL;
$section->course = $newcourseid; // Create a default section.
$section->section = 0;
$section->id = insert_record("course_sections", $section);
if(!$skip_fix_course_sortorder){
fix_course_sortorder();
}
add_to_log($newcourseid, "course", "new", "view.php?id=$newcourseid", "enrol/ldap auto-creation");
} else {
error_log("Could not create new course from LDAP from DN:" . $course_ext['dn']);
error("Serious Error! Could not create the new course!");
return false;
}
return $newcourseid;
}
} // end of class
?>

29
enrol/ldap/enrol_ldap_sync.php Executable file
View File

@ -0,0 +1,29 @@
<?php
if(!empty($_SERVER['GATEWAY_INTERFACE'])){
error_log("should not be called from apache!");
exit;
}
error_reporting(E_ALL);
require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); // global moodle config file.
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/lib/moodlelib.php');
require_once($CFG->dirroot . '/lib/datalib.php');
require_once($CFG->dirroot . "/enrol/" . $CFG->enrol . "/enrol.php");
// ensure errors are well explained
$CFG->debug=10;
// update enrolments -- these handlers should autocreate courses if required
$enrol = new enrolment_plugin();
$enrol->enrol_ldap_connect();
$enrol->sync_enrolments('student', true);
$enrol->sync_enrolments('teacher', true);
// sync metacourses
if (function_exists('sync_metacourses')) {
sync_metacourses();
}
?>

69
lang/en/enrol_ldap.php Executable file
View File

@ -0,0 +1,69 @@
<?php // $Id$
$string['enrolname'] = 'LDAP';
$string['description'] = '<p>You can use an LDAP server to control your enrolments.
It is assumed your LDAP tree contains groups that map to
the courses, and that each of thouse groups/courses will
have membership entries to map to students.</p>
<p>It is assumed that courses are defined as groups in
LDAP, with each group having multiple membership fields
(<em>member</em> or <em>memberUid</em>) that contain a unique
identification of the user.</p>
<p>To use LDAP enrolment, your users <strong>must</strong>
to have a valid idnumber field. The LDAP groups must have
that idnumber in the member fields for a user to be enrolled
in the course.
This will usually work well if you are already using LDAP
Authentication.</p>
<p>Enrolments will be updated when the user logs in. You
can also run a script to keep enrolments in synch. Look in
<em>enrol/ldap/enrol_ldap_sync.php</em>.<p>
<p>This plugin can also be set to automatically create new
courses when new groups appear in LDAP.</p>';
$string['enrol_ldap_server_settings'] = 'LDAP Server Settings';
$string['enrol_ldap_host_url'] = 'Specify LDAP host in URL-form like
\'ldap://ldap.myorg.com/\'
or \'ldaps://ldap.myorg.com/\'';
$string['enrol_ldap_version'] = 'The version of the LDAP protocol your server is using.';
$string['enrol_ldap_bind_dn'] = 'If you want to use bind-user to search users,
specify it here. Someting like
\'cn=ldapuser,ou=public,o=org\'';
$string['enrol_ldap_bind_pw'] = 'Password for bind-user.';
$string['enrol_ldap_student_settings'] = 'Student enrolment settings';
$string['enrol_ldap_teacher_settings'] = 'Teacher enrolment settings';
$string['enrol_ldap_course_settings'] = 'Course enrolment settings';
$string['enrol_ldap_student_contexts'] = 'List of contexts where groups with student
enrolments are located. Separate different
contexts with \';\'. For example:
\'ou=courses,o=org; ou=others,o=org\'';
$string['enrol_ldap_student_memberattribute'] = 'Member attribute, when users belongs
(is enrolled) to a group. Usually \'member\'
or \'memberUid\'.';
$string['enrol_ldap_teacher_contexts'] = 'List of contexts where groups with teacher
enrolments are located. Separate different
contexts with \';\'. For example:
\'ou=courses,o=org; ou=others,o=org\'';
$string['enrol_ldap_teacher_memberattribute'] = 'Member attribute, when users belongs
(is enrolled) to a group. Usually \'member\'
or \'memberUid\'.';
$string['enrol_ldap_autocreation_settings'] = 'Automatic course creation settings';
$string['enrol_ldap_autocreate'] = 'Courses can be created automatically if there are
enrolments to a course that doesn\'t yet exist
in Moodle.';
$string['enrol_ldap_objectclass'] = 'objectClass used to search courses. Usually
\'posixGroup\'.';
$string['enrol_ldap_category'] = 'The category for auto-created courses.';
$string['enrol_ldap_template'] = 'Optional: auto-created courses can copy
their settings from a template course.';
$string['enrol_ldap_updatelocal'] = 'Update local data';
$string['enrol_ldap_editlock'] = 'Lock value';
$string['enrol_ldap_course_idnumber'] = 'Map to the unique identifier in LDAP, usually
<em>cn</em> or <em>uid</em>. It is
ecommended to lock the value if you are using
automatic course creation.';
$string['enrol_ldap_course_shortname'] = 'Optional: LDAP field to get the shortname from.';
$string['enrol_ldap_course_fullname'] = 'Optional: LDAP field to get the full name from.';
$string['enrol_ldap_course_summary'] = 'Optional: LDAP field to get the summary from.';
?>