2011-09-25 10:56:03 +02:00
< ? php
2017-12-04 04:38:03 +01:00
2011-09-25 10:56:03 +02:00
/**
* Name : LDAP Authenticate
* Description : Authenticate a user against an LDAP directory
2015-02-04 00:35:24 +01:00
* Version : 1.1
2011-09-25 10:56:03 +02:00
* Author : Mike Macgirvin < http :// macgirvin . com / profile / mike >
2015-02-04 00:35:24 +01:00
* Author : aymhce
2011-09-25 10:56:03 +02:00
*/
2017-12-04 04:38:03 +01:00
2011-09-25 10:56:03 +02:00
/**
2012-01-31 12:06:20 +01:00
* Friendica addon
2017-12-04 04:38:03 +01:00
*
2011-09-25 10:56:03 +02:00
* Module : LDAP Authenticate
*
* Authenticate a user against an LDAP directory
* Useful for Windows Active Directory and other LDAP - based organisations
* to maintain a single password across the organisation .
*
* Optionally authenticates only if a member of a given group in the directory .
*
2017-12-04 04:38:03 +01:00
* By default , the person must have registered with Friendica using the normal registration
2012-01-31 12:06:20 +01:00
* procedures in order to have a Friendica user record , contact , and profile .
2015-02-04 00:35:24 +01:00
* However , it ' s possible with an option to automate the creation of a Friendica basic account .
2011-09-25 10:56:03 +02:00
*
* Note when using with Windows Active Directory : you may need to set TLS_CACERT in your site
2017-12-04 04:38:03 +01:00
* ldap . conf file to the signing cert for your LDAP server .
*
2018-11-25 08:13:45 +01:00
* The configuration options for this module may be set in the config / addon . config . php file
2011-09-25 10:56:03 +02:00
* e . g .:
*
2018-06-28 05:14:29 +02:00
* [ ldapauth ]
* ; ldap hostname server - required
* ldap_server = host . example . com
* ; dn to search users - required
* ldap_searchdn = ou = users , dc = example , dc = com
* ; attribute to find username - required
* ldap_userattr = uid
2015-02-04 16:55:07 +01:00
*
2018-06-28 05:14:29 +02:00
* ; admin dn - optional - only if ldap server dont have anonymous access
* ldap_binddn = cn = admin , dc = example , dc = com
* ; admin password - optional - only if ldap server dont have anonymous access
* ldap_bindpw = password
2011-09-25 10:56:03 +02:00
*
2018-06-28 05:14:29 +02:00
* ; for create Friendica account if user exist in ldap
* ; required an email and a simple ( beautiful ) nickname on user ldap object
* ; active account creation - optional - default none
* ldap_autocreateaccount = true
* ; attribute to get email - optional - default : 'mail'
* ldap_autocreateaccount_emailattribute = mail
* ; attribute to get nickname - optional - default : 'givenName'
* ldap_autocreateaccount_nameattribute = cn
2015-02-04 16:55:07 +01:00
*
* ... etc .
2011-09-25 10:56:03 +02:00
*/
2019-02-03 22:22:03 +01:00
2018-12-26 08:28:16 +01:00
use Friendica\Core\Hook ;
2018-10-30 00:40:18 +01:00
use Friendica\Core\Logger ;
2020-11-19 17:18:48 +01:00
use Friendica\Database\DBA ;
2020-01-19 21:26:41 +01:00
use Friendica\DI ;
2017-12-04 04:38:52 +01:00
use Friendica\Model\User ;
2019-06-23 19:56:21 +02:00
use Friendica\Util\ConfigFileLoader ;
2017-11-07 00:55:24 +01:00
2017-12-04 04:38:03 +01:00
function ldapauth_install ()
{
2018-12-26 08:28:16 +01:00
Hook :: register ( 'load_config' , 'addon/ldapauth/ldapauth.php' , 'ldapauth_load_config' );
Hook :: register ( 'authenticate' , 'addon/ldapauth/ldapauth.php' , 'ldapauth_hook_authenticate' );
2011-09-25 10:56:03 +02:00
}
2019-03-24 12:54:26 +01:00
function ldapauth_load_config ( \Friendica\App $a , ConfigFileLoader $loader )
2018-06-28 05:14:29 +02:00
{
2019-02-10 20:10:59 +01:00
$a -> getConfigCache () -> load ( $loader -> loadAddonConfig ( 'ldapauth' ));
2011-09-25 10:56:03 +02:00
}
2017-12-04 04:38:03 +01:00
function ldapauth_hook_authenticate ( $a , & $b )
{
2020-11-19 17:18:48 +01:00
$user = ldapauth_authenticate ( $b [ 'username' ], $b [ 'password' ]);
if ( ! empty ( $user [ 'uid' ])) {
$b [ 'user_record' ] = User :: getById ( $user [ 'uid' ]);
$b [ 'authenticated' ] = 1 ;
2017-12-04 04:38:03 +01:00
}
2011-09-25 10:56:03 +02:00
}
2017-12-04 04:38:03 +01:00
function ldapauth_authenticate ( $username , $password )
{
2020-01-19 21:21:12 +01:00
$ldap_server = DI :: config () -> get ( 'ldapauth' , 'ldap_server' );
$ldap_binddn = DI :: config () -> get ( 'ldapauth' , 'ldap_binddn' );
$ldap_bindpw = DI :: config () -> get ( 'ldapauth' , 'ldap_bindpw' );
$ldap_searchdn = DI :: config () -> get ( 'ldapauth' , 'ldap_searchdn' );
$ldap_userattr = DI :: config () -> get ( 'ldapauth' , 'ldap_userattr' );
$ldap_group = DI :: config () -> get ( 'ldapauth' , 'ldap_group' );
$ldap_autocreateaccount = DI :: config () -> get ( 'ldapauth' , 'ldap_autocreateaccount' );
$ldap_autocreateaccount_emailattribute = DI :: config () -> get ( 'ldapauth' , 'ldap_autocreateaccount_emailattribute' );
$ldap_autocreateaccount_nameattribute = DI :: config () -> get ( 'ldapauth' , 'ldap_autocreateaccount_nameattribute' );
2017-12-04 04:38:03 +01:00
2020-11-19 17:18:48 +01:00
if ( ! extension_loaded ( 'ldap' ) || ! strlen ( $ldap_server )) {
Logger :: error ( 'Addon not configured or missing php-ldap extension' , [ 'extension_loaded' => extension_loaded ( 'ldap' ), 'server' => $ldap_server ]);
2017-12-04 04:38:03 +01:00
return false ;
}
2020-11-19 17:18:48 +01:00
if ( ! strlen ( $password )) {
Logger :: error ( 'Empty password disallowed' , [ 'provided_password_length' => strlen ( $password )]);
return false ;
}
2017-12-04 04:38:03 +01:00
2020-11-19 17:18:48 +01:00
$connect = @ ldap_connect ( $ldap_server );
2017-12-04 04:38:03 +01:00
if ( $connect === false ) {
2020-11-19 17:18:48 +01:00
Logger :: warning ( 'Could not connect to LDAP server' , [ 'server' => $ldap_server ]);
2017-12-04 04:38:03 +01:00
return false ;
}
@ ldap_set_option ( $connect , LDAP_OPT_PROTOCOL_VERSION , 3 );
@ ldap_set_option ( $connect , LDAP_OPT_REFERRALS , 0 );
if (( @ ldap_bind ( $connect , $ldap_binddn , $ldap_bindpw )) === false ) {
2020-11-19 17:18:48 +01:00
Logger :: warning ( 'Could not bind to LDAP server' , [ 'server' => $ldap_server , 'binddn' => $ldap_binddn , 'errno' => ldap_errno ( $connect ), 'error' => ldap_error ( $connect )]);
2017-12-04 04:38:03 +01:00
return false ;
}
$res = @ ldap_search ( $connect , $ldap_searchdn , $ldap_userattr . '=' . $username );
if ( ! $res ) {
2020-11-19 17:18:48 +01:00
Logger :: notice ( 'LDAP user not found.' , [ 'searchdn' => $ldap_searchdn , 'userattr' => $ldap_userattr , 'username' => $username , 'errno' => ldap_errno ( $connect ), 'error' => ldap_error ( $connect )]);
2017-12-04 04:38:03 +01:00
return false ;
}
$id = @ ldap_first_entry ( $connect , $res );
if ( ! $id ) {
2020-11-19 17:18:48 +01:00
Logger :: notice ( 'Could not retrieve first LDAP entry.' , [ 'searchdn' => $ldap_searchdn , 'userattr' => $ldap_userattr , 'username' => $username , 'errno' => ldap_errno ( $connect ), 'error' => ldap_error ( $connect )]);
2017-12-04 04:38:03 +01:00
return false ;
}
$dn = @ ldap_get_dn ( $connect , $id );
if ( !@ ldap_bind ( $connect , $dn , $password )) {
2020-11-19 17:18:48 +01:00
Logger :: notice ( 'Could not authenticate LDAP user with provided password' , [ 'errno' => ldap_errno ( $connect ), 'error' => ldap_error ( $connect )]);
2017-12-04 04:38:03 +01:00
return false ;
}
$emailarray = [];
$namearray = [];
if ( $ldap_autocreateaccount == " true " ) {
if ( ! strlen ( $ldap_autocreateaccount_emailattribute )) {
$ldap_autocreateaccount_emailattribute = " mail " ;
}
if ( ! strlen ( $ldap_autocreateaccount_nameattribute )) {
$ldap_autocreateaccount_nameattribute = " givenName " ;
}
$emailarray = @ ldap_get_values ( $connect , $id , $ldap_autocreateaccount_emailattribute );
$namearray = @ ldap_get_values ( $connect , $id , $ldap_autocreateaccount_nameattribute );
}
if ( ! strlen ( $ldap_group )) {
2020-11-19 17:18:48 +01:00
ldap_createaccount ( $ldap_autocreateaccount , $username , $password , $emailarray [ 0 ], $namearray [ 0 ]);
2017-12-04 04:38:03 +01:00
return true ;
}
$r = @ ldap_compare ( $connect , $ldap_group , 'member' , $dn );
2020-11-19 17:18:48 +01:00
if ( $r !== true ) {
$errno = @ ldap_errno ( $connect );
if ( $errno === 32 ) {
Logger :: notice ( 'LDAP Access Control Group does not exist' , [ 'errno' => $errno , 'error' => ldap_error ( $connect )]);
} elseif ( $errno === 16 ) {
Logger :: notice ( 'LDAP membership attribute does not exist in access control group' , [ 'errno' => $errno , 'error' => ldap_error ( $connect )]);
2017-12-04 04:38:03 +01:00
} else {
2020-11-19 17:18:48 +01:00
Logger :: notice ( 'Unexpected LDAP error' , [ 'errno' => $errno , 'error' => ldap_error ( $connect )]);
2017-12-04 04:38:03 +01:00
}
2020-11-19 17:18:48 +01:00
2017-12-04 04:38:03 +01:00
@ ldap_close ( $connect );
return false ;
}
2020-11-19 17:18:48 +01:00
if ( $ldap_autocreateaccount == " true " && ! DBA :: exists ( 'user' , [ 'nickname' => $username ])) {
return ldap_createaccount ( $username , $password , $emailarray [ 0 ], $namearray [ 0 ]);
}
2015-02-04 00:35:24 +01:00
2020-11-19 17:18:48 +01:00
try {
$authentication = User :: getAuthenticationInfo ( $username );
return User :: getById ( $authentication [ 'uid' ]);
} catch ( Exception $e ) {
Logger :: notice ( 'LDAP authentication error: ' . $e -> getMessage ());
return false ;
2017-12-04 04:38:03 +01:00
}
2015-02-04 00:35:24 +01:00
}
2020-11-19 17:18:48 +01:00
function ldap_createaccount ( $username , $password , $email , $name )
2017-12-04 04:38:03 +01:00
{
2020-11-19 17:18:48 +01:00
if ( ! strlen ( $email ) || ! strlen ( $name )) {
Logger :: notice ( 'Could not create local user from LDAP data, no email or nickname provided' );
return false ;
}
try {
$user = User :: create ([
'username' => $name ,
'nickname' => $username ,
'email' => $email ,
'password' => $password ,
'verified' => 1
]);
Logger :: info ( 'Local user created from LDAP data' , [ 'username' => $username , 'name' => $name ]);
return $user ;
} catch ( Exception $ex ) {
Logger :: error ( 'Could not create local user from LDAP data' , [ 'username' => $username , 'exception' => $ex -> getMessage ()]);
}
return false ;
2015-02-04 00:35:24 +01:00
}