Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F1376
LdapAuthentication.php
Public
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Authored By
•
bzimport
Nov 21 2014, 7:04 PM
2014-11-21 19:04:00 (UTC+0)
Size
28 KB
Referenced Files
None
Subscribers
None
LdapAuthentication.php
View Options
<?php
# Copyright (C) 2004 Brion Vibber <brion@pobox.com>
# http://www.mediawiki.org/
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# http://www.gnu.org/copyleft/gpl.html
/**
* Authentication plugin interface. Instantiate a subclass of AuthPlugin
* and set $wgAuth to it to authenticate against some external tool.
*
* The default behavior is not to do anything, and use the local user
* database for all authentication. A subclass can require that all
* accounts authenticate externally, or use it only as a fallback; also
* you can transparently create internal wiki accounts the first time
* someone logs in who can be authenticated externally.
*
* This interface is new, and might change a bit before 1.4.0 final is
* done...
*
* @package MediaWiki
*/
#
# LdapAuthentication.php
#
# Info availible at http://bugzilla.wikipedia.org/show_bug.cgi?id=814
# and at http://meta.wikimedia.org/wiki/LDAP_Authentication
# and at http://meta.wikimedia.org/wiki/LDAP_Authentication_Configuration_Examples
#
# Version 1.0h / 11/13/2006
#
require_once
(
'AuthPlugin.php'
);
class
LdapAuthenticationPlugin
extends
AuthPlugin
{
var
$email
,
$lang
,
$realname
,
$nickname
,
$SearchType
;
/**
* Check whether there exists a user account with the given name.
* The name will be normalized to MediaWiki's requirements, so
* you might need to munge it (for instance, for lowercase initial
* letters).
*
* @param string $username
* @return bool
* @access public
*/
function
userExists
(
$username
)
{
global
$wgLDAPAddLDAPUsers
;
$this
->
printDebug
(
"Entering userExists"
,
1
);
//If we can't add LDAP users, we don't really need to check
//if the user exists, the authenticate method will do this for
//us. This will decrease hits to the LDAP server.
if
(!
$wgLDAPAddLDAPUsers
)
{
return
true
;
}
$ldapconn
=
$this
->
connect
();
if
(
$ldapconn
)
{
$this
->
printDebug
(
"Successfully connected"
,
1
);
$searchstring
=
$this
->
getSearchString
(
$ldapconn
,
$username
);
//Search for the entry.
$entry
=
@
ldap_read
(
$ldapconn
,
$searchstring
,
"objectclass=*"
);
//getSearchString is going to bind, but will not unbind
//Let's clean up
@
ldap_unbind
();
if
(!
$entry
)
{
$this
->
printDebug
(
"Did not find a matching user in LDAP"
,
1
);
//user wasn't found
return
false
;
}
else
{
$this
->
printDebug
(
"Found a matching user in LDAP"
,
1
);
return
true
;
}
}
else
{
$this
->
printDebug
(
"Failed to connect"
,
1
);
return
false
;
}
}
/**
* Connect to the external database.
*
* @return resource
* @access private
*/
function
connect
()
{
global
$wgLDAPServerNames
;
global
$wgLDAPUseSSL
,
$wgLDAPUseTLS
;
$this
->
printDebug
(
"Entering Connect"
,
1
);
if
(
$wgLDAPUseSSL
)
{
$this
->
printDebug
(
"Using SSL"
,
3
);
$serverpre
=
"ldaps://"
;
}
else
{
$this
->
printDebug
(
"Not Using SSL"
,
3
);
$serverpre
=
"ldap://"
;
}
$servers
=
""
;
$tmpservers
=
$wgLDAPServerNames
[
$_SESSION
[
'wsDomain'
]];
$tok
=
strtok
(
$tmpservers
,
" "
);
while
(
$tok
)
{
$servers
=
$servers
.
" "
.
$serverpre
.
$tok
;
$tok
=
strtok
(
" "
);
}
$servers
=
rtrim
(
$servers
);
$this
->
printDebug
(
"Using servers: $servers"
,
2
);
$ldapconn
=
@
ldap_connect
(
$servers
);
ldap_set_option
(
$ldapconn
,
LDAP_OPT_PROTOCOL_VERSION
,
3
);
ldap_set_option
(
$ldapconn
,
LDAP_OPT_REFERRALS
,
0
);
if
(
$wgLDAPUseTLS
)
{
$this
->
printDebug
(
"Using TLS"
,
3
);
ldap_start_tls
(
$ldapconn
);
}
return
$ldapconn
;
}
/**
* Check if a username+password pair is a valid login.
* The name will be normalized to MediaWiki's requirements, so
* you might need to munge it (for instance, for lowercase initial
* letters).
*
* @param string $username
* @param string $password
* @return bool
* @access public
*/
function
authenticate
(
$username
,
$password
)
{
global
$wgLDAPRetrievePrefs
;
global
$wgLDAPGroupDN
,
$wgLDAPRequiredGroups
;
global
$wgLDAPGroupUseFullDN
;
global
$wgLDAPRequireAuthAttribute
,
$wgLDAPAuthAttribute
;
$this
->
printDebug
(
"Entering Connect"
,
1
);
//We don't handle local authentication
if
(
'local'
==
$_SESSION
[
'wsDomain'
]
)
{
$this
->
printDebug
(
"User is using a local domain"
,
2
);
return
false
;
}
if
(
''
==
$password
)
{
$this
->
printDebug
(
"User used a blank password"
,
1
);
return
false
;
}
$ldapconn
=
$this
->
connect
();
if
(
$ldapconn
)
{
$this
->
printDebug
(
"Connected successfully"
,
1
);
$userdn
=
$this
->
getSearchString
(
$ldapconn
,
$username
);
//It is possible that getSearchString will return an
//empty string; if this happens, the bind will ALWAYS
//return true, and will let anyone in!
if
(
''
==
$userdn
)
{
$this
->
printDebug
(
"User DN is blank"
,
1
);
return
false
;
}
$this
->
printDebug
(
"Binding as the user"
,
1
);
//Let's see if the user can authenticate.
$bind
=
$this
->
bindAs
(
$ldapconn
,
$userdn
,
$password
);
if
(!
$bind
)
{
return
false
;
}
$this
->
printDebug
(
"Binded successfully"
,
1
);
if
(
$wgLDAPRequireAuthAttribute
)
{
$this
->
printDebug
(
"Checking for auth attributes"
,
1
);
$filter
=
"("
.
$wgLDAPAuthAttribute
[
$_SESSION
[
'wsDomain'
]]
.
")"
;
$attributes
=
array
(
"dn"
);
$entry
=
ldap_read
(
$ldapconn
,
$userdn
,
$filter
,
$attributes
);
$info
=
ldap_get_entries
(
$ldapconn
,
$entry
);
if
(
$info
[
"count"
]
<
1
)
{
$this
->
printDebug
(
"Failed auth attribute check"
,
1
);
return
false
;
}
}
//Old style groups, non-nestable and fairly limited on group type (full DN
//versus username). DEPRECATED
if
(
$wgLDAPGroupDN
)
{
$this
->
printDebug
(
"Checking for (old style) group membership"
,
1
);
if
(!
$this
->
isMemberOfLdapGroup
(
$ldapconn
,
$userdn
,
$wgLDAPGroupDN
))
{
$this
->
printDebug
(
"Failed (old style) group membership check"
,
1
);
//No point in going on if the user isn't in the required group
return
false
;
}
}
if
(
$wgLDAPRequiredGroups
[
$_SESSION
[
'wsDomain'
]])
{
$this
->
printDebug
(
"Checking for (new style) group membership"
,
1
);
if
(
$wgLDAPGroupUseFullDN
[
$_SESSION
[
'wsDomain'
]])
{
$inGroup
=
$this
->
isMemberOfRequiredLdapGroup
(
$ldapconn
,
$userdn
);
}
else
{
$inGroup
=
$this
->
isMemberOfRequiredLdapGroup
(
$ldapconn
,
$username
);
}
if
(!
$inGroup
)
{
return
false
;
}
}
if
(
$wgLDAPRetrievePrefs
)
{
$this
->
printDebug
(
"Retrieving preferences"
,
1
);
$entry
=
@
ldap_read
(
$ldapconn
,
$userdn
,
"objectclass=*"
);
$info
=
@
ldap_get_entries
(
$ldapconn
,
$entry
);
$this
->
email
=
$info
[
0
][
"mail"
][
0
];
$this
->
lang
=
$info
[
0
][
"preferredlanguage"
][
0
];
$this
->
nickname
=
$info
[
0
][
"displayname"
][
0
];
$this
->
realname
=
$info
[
0
][
"cn"
][
0
];
$this
->
printDebug
(
"Retrieved: $this->email, $this->lang, $this->nickname, $this->realname"
,
2
);
}
// Lets clean up.
@
ldap_unbind
();
}
else
{
$this
->
printDebug
(
"Failed to connect"
,
1
);
return
false
;
}
$this
->
printDebug
(
"Authentication passed"
,
1
);
//We made it this far; the user authenticated and didn't fail any checks, so he/she gets in.
return
true
;
}
/**
* Modify options in the login template.
*
* @param UserLoginTemplate $template
* @access public
*/
function
modifyUITemplate
(
&
$template
)
{
global
$wgLDAPDomainNames
,
$wgLDAPUseLocal
;
global
$wgLDAPAddLDAPUsers
;
if
(
!
$wgLDAPAddLDAPUsers
)
{
$template
->
set
(
'create'
,
false
);
}
$template
->
set
(
'usedomain'
,
true
);
$template
->
set
(
'useemail'
,
false
);
$tempDomArr
=
$wgLDAPDomainNames
;
if
(
$wgLDAPUseLocal
)
{
array_push
(
$tempDomArr
,
'local'
);
}
$template
->
set
(
'domainnames'
,
$tempDomArr
);
}
/**
* Return true if the wiki should create a new local account automatically
* when asked to login a user who doesn't exist locally but does in the
* external auth database.
*
* This is just a question, and shouldn't perform any actions.
*
* @return bool
* @access public
*/
function
autoCreate
()
{
return
true
;
}
/**
* Set the given password in the authentication database.
* Return true if successful.
*
* @param string $password
* @return bool
* @access public
*/
function
setPassword
(
$user
,
&
$password
)
{
global
$wgLDAPUpdateLDAP
,
$wgLDAPWriterDN
,
$wgLDAPWriterPassword
;
$this
->
printDebug
(
"Entering setPassword"
,
1
);
if
(
$_SESSION
[
'wsDomain'
]
==
'local'
)
{
$this
->
printDebug
(
"User is using a local domain"
,
1
);
//We don't set local passwords, but we don't want the wiki
//to send the user a failure.
return
true
;
}
else
if
(!
$wgLDAPUpdateLDAP
)
{
$this
->
printDebug
(
"Wiki is set to not allow updates"
,
1
);
//We aren't allowing the user to change his/her own password
return
false
;
}
if
(!
isset
(
$wgLDAPWriterDN
))
{
$this
->
printDebug
(
"Wiki doesn't have wgLDAPWriterDN set"
,
1
);
//We can't change a user's password without an account that is
//allowed to do it.
return
false
;
}
$pass
=
$this
->
getPasswordHash
(
$password
);
$ldapconn
=
$this
->
connect
();
if
(
$ldapconn
)
{
$this
->
printDebug
(
"Connected successfully"
,
1
);
$userdn
=
$this
->
getSearchString
(
$ldapconn
,
$user
->
getName
());
$this
->
printDebug
(
"Binding as the writerDN"
,
1
);
$bind
=
$this
->
bindAs
(
$ldapconn
,
$wgLDAPWriterDN
,
$wgLDAPWriterPassword
);
if
(!
$bind
)
{
return
false
;
}
$values
[
"userpassword"
]
=
$pass
;
//Blank out the password in the database. We don't want to save
//domain credentials for security reasons.
$password
=
''
;
$success
=
ldap_modify
(
$ldapconn
,
$userdn
,
$values
);
@
ldap_unbind
();
if
(
$success
)
{
$this
->
printDebug
(
"Successfully modified the user's password"
,
1
);
return
true
;
}
else
{
$this
->
printDebug
(
"Failed to modify the user's password"
,
1
);
return
false
;
}
}
else
{
return
false
;
}
}
/**
* Update user information in the external authentication database.
* Return true if successful.
*
* @param User $user
* @return bool
* @access public
*/
function
updateExternalDB
(
$user
)
{
global
$wgLDAPUpdateLDAP
;
global
$wgLDAPWriterDN
,
$wgLDAPWriterPassword
;
$this
->
printDebug
(
"Entering updateExternalDB"
,
1
);
if
(!
$wgLDAPUpdateLDAP
||
$_SESSION
[
'wsDomain'
]
==
'local'
)
{
$this
->
printDebug
(
"Either the user is using a local domain, or the wiki isn't allowing updates"
,
1
);
//We don't handle local preferences, but we don't want the
//wiki to return an error.
return
true
;
}
if
(!
isset
(
$wgLDAPWriterDN
))
{
$this
->
printDebug
(
"The wiki doesn't have wgLDAPWriterDN set"
,
1
);
//We can't modify LDAP preferences if we don't have a user
//capable of editing LDAP attributes.
return
false
;
}
$this
->
email
=
$user
->
getEmail
();
$this
->
realname
=
$user
->
getRealName
();
$this
->
nickname
=
$user
->
getOption
(
'nickname'
);
$this
->
language
=
$user
->
getOption
(
'language'
);
$ldapconn
=
$this
->
connect
();
if
(
$ldapconn
)
{
$this
->
printDebug
(
"Connected successfully"
,
1
);
$userdn
=
$this
->
getSearchString
(
$ldapconn
,
$user
->
getName
());
$this
->
printDebug
(
"Binding as the writerDN"
,
1
);
$bind
=
$this
->
bindAs
(
$ldapconn
,
$wgLDAPWriterDN
,
$wgLDAPWriterPassword
);
if
(!
$bind
)
{
return
false
;
}
if
(
''
!=
$this
->
email
)
{
$values
[
"mail"
]
=
$this
->
email
;
}
if
(
''
!=
$this
->
nickname
)
{
$values
[
"displayname"
]
=
$this
->
nickname
;
}
if
(
''
!=
$this
->
realname
)
{
$values
[
"cn"
]
=
$this
->
realname
;
}
if
(
''
!=
$this
->
language
)
{
$values
[
"preferredlanguage"
]
=
$this
->
language
;
}
if
(
0
!=
sizeof
(
$values
)
&&
ldap_modify
(
$ldapconn
,
$userdn
,
$values
))
{
$this
->
printDebug
(
"Successfully modified the user's attributes"
,
1
);
@
ldap_unbind
();
return
true
;
}
else
{
$this
->
printDebug
(
"Failed to modify the user's attributes"
,
1
);
@
ldap_unbind
();
return
false
;
}
}
else
{
$this
->
printDebug
(
"Failed to Connect"
,
1
);
return
false
;
}
}
function
canCreateAccounts
()
{
global
$wgLDAPAddLDAPUsers
;
if
(
$wgLDAPAddLDAPUsers
)
{
return
true
;
}
}
/**
* Add a user to the external authentication database.
* Return true if successful.
*
* @param User $user
* @param string $password
* @return bool
* @access public
*/
function
addUser
(
$user
,
$password
)
{
global
$wgLDAPAddLDAPUsers
,
$wgLDAPWriterDN
,
$wgLDAPWriterPassword
;
global
$wgLDAPSearchAttributes
;
global
$wgLDAPWriteLocation
;
global
$wgLDAPRequiredGroups
,
$wgLDAPGroupDN
;
global
$wgLDAPRequireAuthAttribute
,
$wgLDAPAuthAttribute
;
$this
->
printDebug
(
"Entering addUser"
,
1
);
if
(
$wgLDAPRequiredGroups
||
$wgLDAPGroupDN
)
{
$this
->
printDebug
(
"The wiki is requiring users to be in specific groups, cannot add users as this would be a security hole."
,
1
);
//It is possible that later we can add users into
//groups, but since we don't support it, we don't want
//to open holes!
return
false
;
}
if
(!
$wgLDAPAddLDAPUsers
||
'local'
==
$_SESSION
[
'wsDomain'
])
{
$this
->
printDebug
(
"Either the user is using a local domain, or the wiki isn't allowing users to be added to LDAP"
,
1
);
//Tell the wiki not to return an error.
return
true
;
}
if
(!
isset
(
$wgLDAPWriterDN
))
{
$this
->
printDebug
(
"The wiki doesn't have wgLDAPWriterDN set"
,
1
);
//We can't add users without an LDAP account capable of doing so.
return
false
;
}
$this
->
email
=
$user
->
getEmail
();
$this
->
realname
=
$user
->
getRealName
();
$username
=
$user
->
getName
();
$pass
=
$this
->
getPasswordHash
(
$password
);
$ldapconn
=
$this
->
connect
();
if
(
$ldapconn
)
{
$this
->
printDebug
(
"Successfully connected"
,
1
);
$userdn
=
$this
->
getSearchString
(
$ldapconn
,
$username
);
if
(
''
==
$userdn
)
{
$this
->
printDebug
(
"userdn is blank, attempting to use wgLDAPWriteLocation"
,
1
);
if
(
isset
(
$wgLDAPWriteLocation
[
$_SESSION
[
'wsDomain'
]]))
{
$this
->
printDebug
(
"wgLDAPWriteLocation is set, using that"
,
1
);
$userdn
=
$wgLDAPSearchAttributes
[
$_SESSION
[
'wsDomain'
]]
.
"="
.
$username
.
$wgLDAPWriteLocation
[
$_SESSION
[
'wsDomain'
]];
}
else
{
$this
->
printDebug
(
"wgLDAPWriteLocation is not set, failing"
,
1
);
return
false
;
}
}
$this
->
printDebug
(
"Binding as the writerDN"
,
1
);
$bind
=
$this
->
bindAs
(
$ldapconn
,
$wgLDAPWriterDN
,
$wgLDAPWriterPassword
);
if
(!
$bind
)
{
return
false
;
}
//Set up LDAP attributes
$values
[
"uid"
]
=
$username
;
$values
[
"sn"
]
=
$username
;
if
(
''
!=
$this
->
email
)
{
$values
[
"mail"
]
=
$this
->
email
;
}
if
(
''
!=
$this
->
realname
)
{
$values
[
"cn"
]
=
$this
->
realname
;
}
else
{
$values
[
"cn"
]
=
$username
;
}
$values
[
"userpassword"
]
=
$pass
;
$values
[
"objectclass"
]
=
"inetorgperson"
;
if
(
$wgLDAPRequireAuthAttribute
)
{
$values
[
$wgLDAPAuthAttribute
[
$_SESSION
[
'wsDomain'
]]]
=
"true"
;
}
if
(@
ldap_add
(
$ldapconn
,
$userdn
,
$values
))
{
$this
->
printDebug
(
"Successfully added user"
,
1
);
@
ldap_unbind
();
return
true
;
}
else
{
$this
->
printDebug
(
"Failed to add user"
,
1
);
@
ldap_unbind
();
return
false
;
}
}
else
{
return
false
;
}
}
/**
* Set the domain this plugin is supposed to use when authenticating.
*
* @param string $domain
* @access public
*/
function
setDomain
(
$domain
)
{
$_SESSION
[
'wsDomain'
]
=
$domain
;
}
/**
* Check to see if the specific domain is a valid domain.
*
* @param string $domain
* @return bool
* @access public
*/
function
validDomain
(
$domain
)
{
global
$wgLDAPDomainNames
,
$wgLDAPUseLocal
;
$this
->
printDebug
(
"Entering validDomain"
,
1
);
if
(
in_array
(
$domain
,
$wgLDAPDomainNames
)
||
(
$wgLDAPUseLocal
&&
'local'
==
$domain
))
{
$this
->
printDebug
(
"User is using a valid domain"
,
1
);
return
true
;
}
else
{
$this
->
printDebug
(
"User is not using a valid domain, failing"
,
1
);
return
false
;
}
}
/**
* When a user logs in, optionally fill in preferences and such.
* For instance, you might pull the email address or real name from the
* external user database.
*
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
* @param User $user
* @access public
*/
function
updateUser
(
&
$user
)
{
$this
->
printDebug
(
"Entering updateUser"
,
1
);
if
(
''
!=
$this
->
lang
)
{
$user
->
setOption
(
'language'
,
$this
->
lang
);
}
if
(
''
!=
$this
->
nickname
)
{
$user
->
setOption
(
'nickname'
,
$this
->
nickname
);
}
if
(
''
!=
$this
->
realname
)
{
$user
->
setRealName
(
$this
->
realname
);
}
if
(
''
!=
$this
->
email
)
{
$user
->
setEmail
(
$this
->
email
);
}
}
/**
* Return true to prevent logins that don't authenticate here from being
* checked against the local database's password fields.
*
* This is just a question, and shouldn't perform any actions.
*
* @return bool
* @access public
*/
function
strict
()
{
global
$wgLDAPUseLocal
,
$wgLDAPMailPassword
;
if
(
$wgLDAPUseLocal
||
$wgLDAPMailPassword
)
{
return
false
;
}
else
{
return
true
;
}
}
/**
* When creating a user account, optionally fill in preferences and such.
* For instance, you might pull the email address or real name from the
* external user database.
*
* The User object is passed by reference so it can be modified; don't
* forget the & on your function declaration.
*
* @param User $user
* @access public
*/
function
initUser
(
&
$user
)
{
$this
->
printDebug
(
"Entering updateUser"
,
1
);
if
(
'local'
==
$_SESSION
[
'wsDomain'
])
{
$this
->
printDebug
(
"User is using a local domain"
,
1
);
return
;
}
//We are creating an LDAP user, it is very important that we do
//NOT set a local password because it could compromise the
//security of our domain.
$user
->
setPassword
(
''
);
if
(
''
!=
$this
->
lang
)
{
$user
->
setOption
(
'language'
,
$this
->
lang
);
}
if
(
''
!=
$this
->
nickname
)
{
$user
->
setOption
(
'nickname'
,
$this
->
nickname
);
}
if
(
''
!=
$this
->
realname
)
{
$user
->
setRealName
(
$this
->
realname
);
}
if
(
''
!=
$this
->
email
)
{
$user
->
setEmail
(
$this
->
email
);
}
}
function
getCanonicalName
(
$username
)
{
$this
->
printDebug
(
"Entering getCanonicalName"
,
1
);
//Change username to lowercase so that multiple user accounts
//won't be created for the same user.
$username
=
strtolower
(
$username
);
//The wiki considers an all lowercase name to be invalid; need to
//uppercase the first letter
$username
[
0
]
=
strtoupper
(
$username
[
0
]);
$this
->
printDebug
(
"Munged username: $username"
,
1
);
return
$username
;
}
function
getSearchString
(
$ldapconn
,
$username
)
{
global
$wgLDAPSearchStrings
;
$this
->
printDebug
(
"Entering getSearchString"
,
1
);
if
(
isset
(
$wgLDAPSearchStrings
[
$_SESSION
[
'wsDomain'
]]))
{
//This is a straight bind
$this
->
printDebug
(
"Doing a straight bind"
,
1
);
$tmpuserdn
=
$wgLDAPSearchStrings
[
$_SESSION
[
'wsDomain'
]];
$userdn
=
str_replace
(
"USER-NAME"
,
$username
,
$tmpuserdn
);
}
else
{
//This is a proxy bind, or an anonymous bind with a search
$this
->
printDebug
(
"Doing a proxy or anonymous bind"
,
1
);
$userdn
=
$this
->
getUserDN
(
$ldapconn
,
$username
);
}
$this
->
printDebug
(
"userdn is: $userdn"
,
2
);
return
$userdn
;
}
function
getUserDN
(
$ldapconn
,
$username
)
{
global
$wgLDAPProxyAgent
,
$wgLDAPProxyAgentPassword
;
global
$wgLDAPSearchAttributes
;
global
$wgLDAPRequireAuthAttribute
,
$wgLDAPAuthAttribute
;
global
$wgLDAPBaseDNs
;
$this
->
printDebug
(
"Entering getUserDN"
,
1
);
if
(
isset
(
$wgLDAPProxyAgent
))
{
//This is a proxy bind
$this
->
printDebug
(
"Doing a proxy bind"
,
1
);
$bind
=
$this
->
bindAs
(
$ldapconn
,
$wgLDAPProxyAgent
,
$wgLDAPProxyAgentPassword
);
}
else
{
//This is an anonymous bind
$this
->
printDebug
(
"Doing an anonymous bind"
,
1
);
$bind
=
$this
->
bindAs
(
$ldapconn
);
}
if
(!
$bind
)
{
$this
->
printDebug
(
"Failed to bind"
,
1
);
return
''
;
}
//we need to do a subbase search for the entry
//Why waste a bind later, if a user is missing an auth attribute
//let's catch it here.
if
(
$wgLDAPRequireAuthAttribute
)
{
/**
* Disabled because it is done later and it causes problems with
* people who need the binded user to search for the auth attribute.
* Re-enable for better performance if you search with a proxyagent
* or if you search anonymously.
$auth_filter = "(" . $wgLDAPAuthAttribute[$_SESSION['wsDomain']] . ")";
$srch_filter = "(" . $wgLDAPSearchAttributes[$_SESSION['wsDomain']] . "=$username)";
$filter = "(&" . $srch_filter . $auth_filter . ")";
$this->printDebug("Created an auth attribute filter: $filter",2);
*/
}
else
{
$filter
=
"("
.
$wgLDAPSearchAttributes
[
$_SESSION
[
'wsDomain'
]]
.
"=$username)"
;
$this
->
printDebug
(
"Created a regular filter: $filter"
,
2
);
}
$attributes
=
array
(
"dn"
);
$base
=
$wgLDAPBaseDNs
[
$_SESSION
[
'wsDomain'
]];
$this
->
printDebug
(
"Using base: $base"
,
2
);
$entry
=
@
ldap_search
(
$ldapconn
,
$base
,
$filter
,
$attributes
);
if
(!
$entry
)
{
$this
->
printDebug
(
"Couldn't find an entry"
,
1
);
return
''
;
}
$info
=
@
ldap_get_entries
(
$ldapconn
,
$entry
);
$userdn
=
$info
[
0
][
"dn"
];
return
$userdn
;
}
//DEPRECATED
function
isMemberOfLdapGroup
(
$ldapconn
,
$userDN
,
$groupDN
)
{
$this
->
printDebug
(
"Entering isMemberOfLdapGroup (DEPRECATED)"
,
1
);
//we need to do a subbase search for the entry
$filter
=
"(member="
.
$userDN
.
")"
;
$info
=
ldap_get_entries
(
$ldapconn
,@
ldap_search
(
$ldapconn
,
$groupDN
,
$filter
));
return
(
$info
[
"count"
]>=
1
);
}
function
isMemberOfRequiredLdapGroup
(
$ldapconn
,
$userDN
)
{
global
$wgLDAPRequiredGroups
;
global
$wgLDAPGroupSearchNestedGroups
;
global
$wgLDAPGroupLowerCaseUsername
;
if
(
$wgLDAPGroupLowerCaseUsername
)
{
$userDN
=
strtolower
(
$userDN
);
}
$this
->
printDebug
(
"Entering isMemberOfRequiredLdapGroup"
,
1
);
$reqgroups
=
$wgLDAPRequiredGroups
[
$_SESSION
[
'wsDomain'
]];
$searchnested
=
$wgLDAPGroupSearchNestedGroups
[
$_SESSION
[
'wsDomain'
]];
$this
->
printDebug
(
"Required groups:"
.
implode
(
","
,
$reqgroups
)
.
""
,
1
);
$groups
=
$this
->
getGroups
(
$ldapconn
,
$userDN
);
if
(
count
(
$groups
)
==
0
)
{
//User isn't in any groups, so he/she obviously can't be in
//a required one
$this
->
printDebug
(
"Couldn't find the user in any groups (1)."
,
1
);
return
false
;
}
else
{
//User is in groups, let's see if a required group is one of them
foreach
(
$groups
as
$group
)
{
if
(
in_array
(
$group
,
$reqgroups
)
)
{
$this
->
printDebug
(
"Found user in a group."
,
1
);
return
true
;
}
}
//We didn't find the user in the group, lets check nested groups
if
(
$searchnested
)
{
//No reason to go on if we aren't allowing nested group
//searches
if
(
$this
->
searchNestedGroups
(
$ldapconn
,
$groups
)
)
{
return
true
;
}
}
$this
->
printDebug
(
"Couldn't find the user in any groups (2)."
,
1
);
return
false
;
}
}
function
searchNestedGroups
(
$ldapconn
,
$groups
,
$checkedgroups
=
array
()
)
{
global
$wgLDAPRequiredGroups
;
$this
->
printDebug
(
"Entering searchNestedGroups"
,
1
);
//base case, no more groups left to check
if
(!
$groups
)
{
$this
->
printDebug
(
"Couldn't find user in any nested groups."
,
1
);
return
false
;
}
$this
->
printDebug
(
"Checking groups:"
.
implode
(
","
,
$groups
)
.
""
,
2
);
$reqgroups
=
$wgLDAPRequiredGroups
[
$_SESSION
[
'wsDomain'
]];
$groupstocheck
=
array
();
foreach
(
$groups
as
$group
)
{
$returnedgroups
=
$this
->
getGroups
(
$ldapconn
,
$group
);
foreach
(
$returnedgroups
as
$checkme
)
{
$checkme
=
strtolower
(
$checkme
);
$this
->
printDebug
(
"Checking membership for: $checkme"
,
2
);
if
(
in_array
(
$checkme
,
$checkedgroups
))
{
//We already checked this, move on
continue
;
}
else
if
(
in_array
(
$checkme
,
$reqgroups
))
{
$this
->
printDebug
(
"Found user in a nested group."
,
1
);
//Woohoo
return
true
;
}
else
{
//We'll need to check this group's members now
array_push
(
$groupstocheck
,
$checkme
);
}
}
}
$checkedgroups
=
array_unique
(
array_merge
(
$groups
,
$checkedgroups
));
//Mmmmmm. Tail recursion. Tasty.
if
(
$this
->
searchNestedGroups
(
$ldapconn
,
$groupstocheck
,
$checkedgroups
)
)
{
return
true
;
}
else
{
return
false
;
}
}
function
getGroups
(
$ldapconn
,
$dn
)
{
global
$wgLDAPBaseDNs
;
global
$wgLDAPGroupObjectclass
,
$wgLDAPGroupAttribute
;
global
$wgLDAPProxyAgent
,
$wgLDAPProxyAgentPassword
;
$base
=
$wgLDAPBaseDNs
[
$_SESSION
[
'wsDomain'
]];
$objectclass
=
$wgLDAPGroupObjectclass
[
$_SESSION
[
'wsDomain'
]];
$attribute
=
$wgLDAPGroupAttribute
[
$_SESSION
[
'wsDomain'
]];
$this
->
printDebug
(
"Entering getGroups"
,
1
);
//Search for the groups this user is in
$filter
=
"(&($attribute=$dn)(objectclass=$objectclass))"
;
$this
->
printDebug
(
"Search string: $filter"
,
2
);
if
(
isset
(
$wgLDAPProxyAgent
)
)
{
//We'll try to bind as the proxyagent as the proxyagent should normally have more
//rights than the user. If the proxyagent fails to bind, we will still be able
//to search as the normal user (which is why we don't return on fail).
$this
->
printDebug
(
"Binding as the proxyagentDN"
,
1
);
$bind
=
$this
->
bindAs
(
$ldapconn
,
$wgLDAPProxyAgent
,
$wgLDAPProxyAgentPassword
);
}
$info
=
ldap_get_entries
(
$ldapconn
,@
ldap_search
(
$ldapconn
,
$base
,
$filter
));
//We need to shift because the first entry will be a count
array_shift
(
$info
);
$groups
=
array
();
foreach
(
$info
as
$i
)
{
$mem
=
strtolower
(
$i
[
'dn'
]);
array_push
(
$groups
,
$mem
);
}
$this
->
printDebug
(
"Returned groups:"
.
implode
(
","
,
$groups
)
.
""
,
2
);
return
$groups
;
}
function
getPasswordHash
(
$password
)
{
global
$wgLDAPPasswordHash
;
$this
->
printDebug
(
"Entering getPasswordHash"
,
1
);
//Set the password hashing based upon admin preference
switch
(
$wgLDAPPasswordHash
)
{
case
'crypt'
:
$pass
=
'{CRYPT}'
.
crypt
(
$password
);
break
;
case
'clear'
:
$pass
=
$password
;
break
;
default
:
$pwd_md5
=
base64_encode
(
pack
(
'H*'
,
sha1
(
$password
)));
$pass
=
"{SHA}"
.
$pwd_md5
;
break
;
}
$this
->
printDebug
(
"Password is $pass"
,
2
);
return
$pass
;
}
function
printDebug
(
$debugText
,
$debugVal
)
{
global
$wgLDAPDebug
;
if
(
$wgLDAPDebug
>
$debugVal
)
{
echo
$debugText
.
"<br>"
;
}
}
function
bindAs
(
$ldapconn
,
$userdn
=
null
,
$password
=
null
)
{
//Let's see if the user can authenticate.
if
(
$userdn
==
null
||
$password
==
null
)
{
$bind
=
@
ldap_bind
(
$ldapconn
);
}
else
{
$bind
=
@
ldap_bind
(
$ldapconn
,
$userdn
,
$password
);
}
if
(!
$bind
)
{
$this
->
printDebug
(
"Failed to bind as $userdn"
,
1
);
$this
->
printDebug
(
"with password: $password"
,
3
);
return
false
;
}
return
true
;
}
}
/**
* Some extension information init
*/
$wgExtensionCredits
[
'other'
][]
=
array
(
'name'
=>
'LDAP Authentication Plugin'
,
'version'
=>
'1.0h'
,
'author'
=>
'Ryan Lane'
,
'description'
=>
'LDAP Authentication plugin with support for numerous LDAP authentication methods.'
,
'url'
=>
'http://meta.wikimedia.org/wiki/LDAP_Authentication'
);
?>
File Metadata
Details
Attached
Mime Type
text/x-php
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1275
Default Alt Text
LdapAuthentication.php (28 KB)
Attached To
Mode
T2814: LDAP Authentication
Attached
Detach File
Event Timeline
Log In to Comment