blob: 3848ebe62ae735dfbb12589845d61ed5942bd35a [file] [log] [blame]
<?php
/*
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* \file
*/
$authFuncs['ldap'] = array('test' => function() {return 0;},
'auth' => function() {return NULL;},
'unauth' => 'unauthLDAP');
////////////////////////////////////////////////////////////////////////////////
///
/// \fn unauthLDAP($mode)
///
/// \param $mode - headers or content
///
/// \brief for headers, simply returns; for content, prints information that
/// user has been logged out; VCLAUTH cookie is cleared elsewhere
///
////////////////////////////////////////////////////////////////////////////////
function unauthLDAP($mode) {
if($mode == 'headers')
return;
print "<h2>" . _('Logout') . "</h2>\n";
print _("You are now logged out of VCL.") . "<br><br>\n";
print "<a href=\"" . BASEURL . SCRIPT . "?mode=selectauth\">" . _("Return to Login");
print "</a><br><br><br>\n";
}
////////////////////////////////////////////////////////////////////////////////
///
/// \fn ldapLogin($authtype, $userid, $passwd)
///
/// \param $authtype - index from $authMechs array
/// \param $userid - userid without affiliation
/// \param $passwd - submitted password
///
/// \brief tries to authenticate user via ldap; calls printLoginPageWithSkin if
/// authentication fails
///
////////////////////////////////////////////////////////////////////////////////
function ldapLogin($authtype, $userid, $passwd) {
global $HTMLheader, $printedHTMLheader, $authMechs, $phpVer;
$esc_userid = vcl_mysql_escape_string($userid);
if(! $fh = fsockopen($authMechs[$authtype]['server'], 636, $errno, $errstr, 5)) {
printLoginPageWithSkin($authtype, 1);
return;
}
fclose($fh);
$ds = ldap_connect("ldaps://{$authMechs[$authtype]['server']}/");
if(! $ds) {
addLoginLog($userid, $authtype, $authMechs[$authtype]['affiliationid'], 0);
print $HTMLheader;
$printedHTMLheader = 1;
selectAuth();
return;
}
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
if(array_key_exists('lookupuserbeforeauth', $authMechs[$authtype]) &&
$authMechs[$authtype]['lookupuserbeforeauth'] &&
array_key_exists('lookupuserfield', $authMechs[$authtype])) {
# in this case, we have to look up what part of the tree the user is in
# before we can actually look up the user
$auth = $authMechs[$authtype];
if(array_key_exists('masterlogin', $auth) && strlen($auth['masterlogin']))
$res = ldap_bind($ds, $auth['masterlogin'], $auth['masterpwd']);
else
$res = ldap_bind($ds);
if(! $res) {
addLoginLog($userid, $authtype, $auth['affiliationid'], 0);
printLoginPageWithSkin($authtype);
return;
}
$search = ldap_search($ds,
$auth['binddn'],
"{$auth['lookupuserfield']}=$userid",
array('dn'), 0, 3, 15);
if($search) {
$tmpdata = ldap_get_entries($ds, $search);
if(! $tmpdata['count'] || ! array_key_exists('dn', $tmpdata[0])) {
addLoginLog($userid, $authtype, $auth['affiliationid'], 0);
printLoginPageWithSkin($authtype);
return;
}
$ldapuser = $tmpdata[0]['dn'];
}
else {
addLoginLog($userid, $authtype, $auth['affiliationid'], 0);
printLoginPageWithSkin($authtype);
return;
}
}
else
$ldapuser = sprintf($authMechs[$authtype]['userid'], $userid);
$res = ldap_bind($ds, $ldapuser, $passwd);
if(! $res) {
// login failed
$err = ldap_error($ds);
if($err == 'Invalid credentials')
addLoginLog($userid, $authtype, $authMechs[$authtype]['affiliationid'], 0, $err);
else
addLoginLog($userid, $authtype, $authMechs[$authtype]['affiliationid'], 0);
printLoginPageWithSkin($authtype);
return;
}
else {
addLoginLog($userid, $authtype, $authMechs[$authtype]['affiliationid'], 1);
# used to rely on later code to update user info if update timestamp was expired
// see if user in our db
/*$query = "SELECT id "
. "FROM user "
. "WHERE unityid = '$esc_userid' AND "
. "affiliationid = {$authMechs[$authtype]['affiliationid']}";
$qh = doQuery($query, 101);
if(! mysqli_num_rows($qh)) {
// if not, add user
$newid = updateLDAPUser($authtype, $userid);
if(is_null($newid))
abort(8);
}*/
# now, we always update the user info
$newid = updateLDAPUser($authtype, $userid);
if(is_null($newid))
abort(8);
// get cookie data
$cookie = getAuthCookieData("$userid@" . getAffiliationName($authMechs[$authtype]['affiliationid']), 'ldap');
// set cookie
if(version_compare(PHP_VERSION, "5.2", ">=") == true)
setcookie("VCLAUTH", "{$cookie['data']}", 0, "/", COOKIEDOMAIN, 0, 1);
else
setcookie("VCLAUTH", "{$cookie['data']}", 0, "/", COOKIEDOMAIN, 0);
# set skin cookie based on affiliation
$skin = getAffiliationTheme($authMechs[$authtype]['affiliationid']);
$ucskin = strtoupper($skin);
setcookie("VCLSKIN", "$ucskin", (time() + (SECINDAY * 31)), "/", COOKIEDOMAIN);
// redirect to main page
header("Location: " . BASEURL . SCRIPT);
dbDisconnect();
exit;
}
}
////////////////////////////////////////////////////////////////////////////////
///
/// \fn addLDAPUser($authtype, $userid)
///
/// \param $authtype - index from the $authMechs array
/// \param $userid - a userid without the affiliation part
///
/// \return id from the user table or NULL on failure
///
/// \brief looks up $userid in LDAP according to info in $authMechs array, adds
/// the user to the user table, and returns the new id from the table
///
////////////////////////////////////////////////////////////////////////////////
function addLDAPUser($authtype, $userid) {
global $authMechs, $mysqli_link_vcl;
$data = getLDAPUserData($authtype, $userid);
if(is_null($data))
return NULL;
$loweruserid = strtolower($userid);
$loweruserid = vcl_mysql_escape_string($loweruserid);
# check for existance of an expired user if a numericid exists
if(array_key_exists('numericid', $data)) {
$query = "SELECT id, "
. "unityid, "
. "affiliationid "
. "FROM user "
. "WHERE lastupdated < DATE_SUB(NOW(), INTERVAL 1 YEAR) AND "
. "uid = {$data['numericid']} AND "
. "unityid != '$loweruserid'";
#. "affiliationid = {$authMechs[$authtype]['affiliationid']}";
$qh = doQuery($query, 101);
if($row = mysqli_fetch_assoc($qh)) {
# find the authtype for this user
foreach($authMechs as $index => $auth) {
if($auth['affiliationid'] == $row['affiliationid'] &&
$auth['type'] == 'ldap') {
$checktype = $index;
break;
}
}
# see if user is still in ldap
if(! empty($checktype)) {
$testdata = getLDAPUserData($checktype, $row['unityid']);
if(! is_null($testdata))
abort(52);
# if not, null the uid for the user
$query = "UPDATE user SET uid = NULL WHERE id = {$row['id']}";
doQuery($query, 101);
}
}
}
$query = "INSERT INTO user (";
if(array_key_exists('numericid', $data))
$query .= "uid, ";
$query .= "unityid, "
. "affiliationid, "
. "firstname, "
. "lastname, "
. "email, "
. "emailnotices, "
. "lastupdated) "
. "VALUES (";
if(array_key_exists('numericid', $data))
$query .= "{$data['numericid']}, ";
$query .= "'$loweruserid', "
. "{$authMechs[$authtype]['affiliationid']}, "
. "'{$data['first']}', "
. "'{$data['last']}', "
. "'{$data['email']}', "
. "'{$data['emailnotices']}', "
. "NOW())";
doQuery($query, 101, 'vcl', 1);
if(mysqli_affected_rows($mysqli_link_vcl)) {
$qh = doQuery("SELECT LAST_INSERT_ID() FROM user", 101);
if(! $row = mysqli_fetch_row($qh)) {
abort(101);
}
return $row[0];
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
///
/// \fn validateLDAPUser($type, $loginid)
///
/// \param $type - an array from the $authMechs table
/// \param $loginid - a userid without the affiliation part
///
/// \return 1 if user was found in ldap, 0 if not, -1 on ldap error
///
/// \brief checks to see if a user is in ldap
///
////////////////////////////////////////////////////////////////////////////////
function validateLDAPUser($type, $loginid) {
global $authMechs;
$auth = $authMechs[$type];
$savehdlr = set_error_handler(function() {});
if(! ($fh = fsockopen($auth['server'], 636, $errno, $errstr, 5)))
return -1;
set_error_handler($savehdlr);
fclose($fh);
$ds = ldap_connect("ldaps://{$auth['server']}/");
if(! $ds)
return -1;
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
if(array_key_exists('masterlogin', $auth) && strlen($auth['masterlogin']))
$res = ldap_bind($ds, $auth['masterlogin'], $auth['masterpwd']);
else
$res = ldap_bind($ds);
if(! $res)
return -1;
$return = array('dn');
$search = ldap_search($ds,
$auth['binddn'],
"{$auth['unityid']}=$loginid",
$return, 0, 3, 15);
if(! $search)
return -1;
$data = ldap_get_entries($ds, $search);
if($data['count'])
return 1;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
///
/// \fn updateLDAPUser($authtype, $userid)
///
/// \param $authtype - an array from the $authMechs table
/// \param $userid - a userid without the affiliation part
///
/// \return an array of user information or NULL on error
///
/// \brief pulls the user's information from ldap, updates it in the db, and
/// returns an array of the information
///
////////////////////////////////////////////////////////////////////////////////
function updateLDAPUser($authtype, $userid) {
global $authMechs;
$esc_userid = vcl_mysql_escape_string($userid);
$userData = getLDAPUserData($authtype, $userid);
if(is_null($userData))
return NULL;
$affilid = $authMechs[$authtype]['affiliationid'];
$now = unixToDatetime(time());
// select desired data from db
$qbase = "SELECT i.name AS IMtype, "
. "u.IMid AS IMid, "
. "u.affiliationid, "
. "af.name AS affiliation, "
. "af.shibonly, "
. "u.emailnotices, "
. "u.preferredname AS preferredname, "
. "u.uid AS uid, "
. "u.id AS id, "
. "u.width AS width, "
. "u.height AS height, "
. "u.bpp AS bpp, "
. "u.audiomode AS audiomode, "
. "u.mapdrives AS mapdrives, "
. "u.mapprinters AS mapprinters, "
. "u.mapserial AS mapserial, "
. "COALESCE(u.rdpport, 3389) AS rdpport, "
. "u.showallgroups "
. "FROM affiliation af, "
. "user u "
. "LEFT JOIN IMtype i ON (u.IMtypeid = i.id) "
. "WHERE af.id = $affilid AND ";
if(array_key_exists('numericid', $userData) &&
is_numeric($userData['numericid']))
$query = $qbase . "u.uid = {$userData['numericid']}";
else {
$query = $qbase . "u.unityid = '$esc_userid' AND "
. "u.affiliationid = $affilid";
}
$qh = doQuery($query, 255);
$updateuid = 0;
# check to see if there is a matching entry where uid is NULL but unityid and affiliationid match
if(array_key_exists('numericid', $userData) &&
is_numeric($userData['numericid']) &&
! mysqli_num_rows($qh)) {
$updateuid = 1;
$query = $qbase . "u.unityid = '$esc_userid' AND "
. "u.affiliationid = $affilid";
$qh = doQuery($query, 255);
}
// if get a row
// update db
// update results from select
if($user = mysqli_fetch_assoc($qh)) {
$user["unityid"] = $userid;
$user["firstname"] = $userData['first'];
$user["lastname"] = $userData["last"];
$user["email"] = $userData["email"];
$user["lastupdated"] = $now;
$query = "UPDATE user "
. "SET unityid = '$esc_userid', "
. "firstname = '{$userData['first']}', "
. "lastname = '{$userData['last']}', "
. "email = '{$userData['email']}', ";
if($updateuid)
$query .= "uid = {$userData['numericid']}, ";
$query .= "lastupdated = '$now' ";
if(array_key_exists('numericid', $userData) &&
is_numeric($userData['numericid']) &&
! $updateuid)
$query .= "WHERE uid = {$userData['numericid']}";
else
$query .= "WHERE unityid = '$esc_userid' AND "
. "affiliationid = $affilid";
doQuery($query, 256, 'vcl', 1);
}
else {
// call addLDAPUser
$id = addLDAPUser($authtype, $userid);
$query = "SELECT u.unityid AS unityid, "
. "u.affiliationid, "
. "af.name AS affiliation, "
. "u.firstname AS firstname, "
. "u.lastname AS lastname, "
. "u.preferredname AS preferredname, "
. "u.email AS email, "
. "i.name AS IMtype, "
. "u.IMid AS IMid, "
. "u.uid AS uid, "
. "u.id AS id, "
. "u.width AS width, "
. "u.height AS height, "
. "u.bpp AS bpp, "
. "u.audiomode AS audiomode, "
. "u.mapdrives AS mapdrives, "
. "u.mapprinters AS mapprinters, "
. "u.mapserial AS mapserial, "
. "COALESCE(u.rdpport, 3389) AS rdpport, "
. "u.showallgroups, "
. "u.usepublickeys, "
. "u.sshpublickeys, "
. "u.lastupdated AS lastupdated "
. "FROM affiliation af, "
. "user u "
. "LEFT JOIN IMtype i ON (u.IMtypeid = i.id) "
. "WHERE u.affiliationid = af.id AND "
. "u.id = $id";
$qh = doQuery($query, 101);
if(! $user = mysqli_fetch_assoc($qh))
return NULL;
$user['sshpublickeys'] = htmlspecialchars($user['sshpublickeys']);
}
// TODO handle generic updating of groups
switch(getAffiliationName($affilid)) {
case 'EXAMPLE1':
updateEXAMPLE1Groups($user);
break;
default:
//TODO possibly add to a default group
}
$user["groups"] = getUsersGroups($user["id"], 1);
$user["groupperms"] = getUsersGroupPerms(array_keys($user['groups']));
$user["privileges"] = getOverallUserPrivs($user["id"]);
$user['login'] = $user['unityid'];
return $user;
}
////////////////////////////////////////////////////////////////////////////////
///
/// \fn getLDAPUserData($authtype, $userid)
///
/// \param $authtype - an array from the $authMechs table
/// \param $userid - a userid without the affiliation part
///
/// \return an array of user information with the following keys:\n
/// \b first - first name of user (escaped with vcl_mysql_escape_string)\n
/// \b last - last name of user (escaped with vcl_mysql_escape_string)\n
/// \b email - email address of user (escaped with vcl_mysql_escape_string)\n
/// \b emailnotices - 0 or 1, whether or not emails should be sent to user\n
/// \b numericid - numeric id of user if $authtype is configured to include it
///
/// \brief gets user information from ldap
///
////////////////////////////////////////////////////////////////////////////////
function getLDAPUserData($authtype, $userid) {
global $authMechs, $mysqli_link_vcl;
$auth = $authMechs[$authtype];
$donumericid = 0;
if(array_key_exists('numericid', $auth))
$donumericid = 1;
$ds = ldap_connect("ldaps://{$auth['server']}/");
// FIXME
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
if(array_key_exists('masterlogin', $auth) && strlen($auth['masterlogin']))
$res = ldap_bind($ds, $auth['masterlogin'], $auth['masterpwd']);
else
$res = ldap_bind($ds);
// FIXME
$ldapsearch = array($auth['firstname'],
$auth['lastname'],
$auth['email']);
if($donumericid)
array_push($ldapsearch, $auth['numericid']);
# FIXME hack
array_push($ldapsearch, 'gecos');
$searchuser = stripslashes($userid);
$search = ldap_search($ds,
$auth['binddn'],
"{$auth['unityid']}=$searchuser",
$ldapsearch, 0, 3, 15);
$return = array();
if($search) {
$tmpdata = ldap_get_entries($ds, $search);
if(! $tmpdata['count'])
return NULL;
$data = array();
for($i = 0; $i < $tmpdata['count']; $i++) {
for($j = 0; $j < $tmpdata[$i]['count']; $j++) {
if(is_array($tmpdata[$i][$tmpdata[$i][$j]]))
$data[strtolower($tmpdata[$i][$j])] = $tmpdata[$i][$tmpdata[$i][$j]][0];
else
$data[strtolower($tmpdata[$i][$j])] = $tmpdata[$i][$tmpdata[$i][$j]];
}
}
// FIXME hack to take care of users that don't have full info in ldap
if(! array_key_exists($auth['firstname'], $data) &&
! array_key_exists(strtolower($auth['firstname']), $data)) {
if(array_key_exists('gecos', $data)) {
$tmpArr = explode(' ', $data['gecos']);
if(count($tmpArr) == 3) {
$data[strtolower($auth['firstname'])] = $tmpArr[0];
$data[strtolower($auth['lastname'])] = $tmpArr[2];
}
elseif(count($tmpArr) == 2) {
$data[strtolower($auth['firstname'])] = $tmpArr[0];
$data[strtolower($auth['lastname'])] = $tmpArr[1];
}
elseif(count($tmpArr) == 1) {
$data[strtolower($auth['firstname'])] = '';
$data[strtolower($auth['lastname'])] = $tmpArr[0];
}
}
else {
$data[strtolower($auth['firstname'])] = '';
$data[strtolower($auth['lastname'])] = '';
}
}
$return['emailnotices'] = 1;
if(! array_key_exists($auth['email'], $data)) {
$data[strtolower($auth['email'])] = $userid . $auth['defaultemail'];
$return['emailnotices'] = 0;
}
if(array_key_exists(strtolower($auth['firstname']), $data))
$return['first'] = vcl_mysql_escape_string($data[strtolower($auth['firstname'])]);
else
$return['first'] = '';
if(array_key_exists(strtolower($auth['lastname']), $data))
$return['last'] = vcl_mysql_escape_string($data[strtolower($auth['lastname'])]);
else
$return['last'] = '';
if($donumericid && is_numeric($data[strtolower($auth['numericid'])]))
$return['numericid'] = $data[strtolower($auth['numericid'])];
$return['email'] = vcl_mysql_escape_string($data[strtolower($auth['email'])]);
return $return;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
///
/// \fn updateEXAMPLE1Groups($user)
///
/// \param $user - an array of user data
///
/// \brief builds an array of memberof groups user is a member of and calls
/// updateGroups
///
////////////////////////////////////////////////////////////////////////////////
function updateEXAMPLE1Groups($user) {
global $authMechs;
$auth = $authMechs['EXAMPLE1 LDAP'];
$ds = ldap_connect("ldaps://{$auth['server']}/");
if(! $ds)
return 0;
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
$res = ldap_bind($ds, $auth['masterlogin'],
$auth['masterpwd']);
if(! $res)
return 0;
$search = ldap_search($ds,
$auth['binddn'],
"{$auth['unityid']}={$user['unityid']}",
array('memberof'), 0, 10, 15);
if(! $search)
return 0;
$data = ldap_get_entries($ds, $search);
$newusergroups = array();
if(! array_key_exists('memberof', $data[0]))
return;
for($i = 0; $i < $data[0]['memberof']['count']; $i++) {
if(preg_match('/^CN=(.+),OU=CourseRolls,DC=example1,DC=com/', $data[0]['memberof'][$i], $match) ||
preg_match('/^CN=(Students_Enrolled),OU=Students,DC=example1,DC=com$/', $data[0]['memberof'][$i], $match) ||
preg_match('/^CN=(Staff),OU=IT,DC=example1,DC=com$/', $data[0]['memberof'][$i], $match))
array_push($newusergroups, getUserGroupID($match[1], $user['affiliationid']));
}
$newusergroups = array_unique($newusergroups);
updateGroups($newusergroups, $user["id"]);
}
?>