2016-07-13 05:48:16 -07:00
< ? php
namespace App\Models ;
use Illuminate\Database\Eloquent\Model ;
use App\Models\User ;
use App\Models\Setting ;
use Exception ;
use Input ;
use Log ;
class Ldap extends Model
{
/**
* Makes a connection to LDAP using the settings in Admin > Settings .
*
* @ author [ A . Gianotto ] [ < snipe @ snipe . net > ]
* @ since [ v3 . 0 ]
* @ return connection
*/
public static function connectToLdap ()
{
$ldap_host = Setting :: getSettings () -> ldap_server ;
$ldap_version = Setting :: getSettings () -> ldap_version ;
$ldap_server_cert_ignore = Setting :: getSettings () -> ldap_server_cert_ignore ;
2016-07-22 16:00:37 -07:00
$ldap_use_tls = Setting :: getSettings () -> ldap_tls ;
2016-07-13 05:48:16 -07:00
// If we are ignoring the SSL cert we need to setup the environment variable
// before we create the connection
2016-07-22 16:21:16 -07:00
if ( $ldap_server_cert_ignore == '1' ) {
2016-07-13 05:48:16 -07:00
putenv ( 'LDAPTLS_REQCERT=never' );
}
2016-08-02 02:36:00 -07:00
// If the user specifies where CA Certs are, make sure to use them
2016-12-29 14:02:18 -08:00
if ( env ( " LDAPTLS_CACERT " )) {
putenv ( " LDAPTLS_CACERT= " . env ( " LDAPTLS_CACERT " ));
2016-08-02 02:36:00 -07:00
}
2016-07-22 16:21:16 -07:00
$connection = @ ldap_connect ( $ldap_host );
2016-07-13 05:48:16 -07:00
if ( ! $connection ) {
2016-07-22 16:21:16 -07:00
throw new Exception ( 'Could not connect to LDAP server at ' . $ldap_host . '. Please check your LDAP server name and port number in your settings.' );
2016-07-13 05:48:16 -07:00
}
// Needed for AD
ldap_set_option ( $connection , LDAP_OPT_REFERRALS , 0 );
ldap_set_option ( $connection , LDAP_OPT_PROTOCOL_VERSION , $ldap_version );
2017-09-18 19:11:38 -07:00
ldap_set_option ( $connection , LDAP_OPT_NETWORK_TIMEOUT , 20 );
2016-07-13 05:48:16 -07:00
2016-07-22 16:00:37 -07:00
if ( $ldap_use_tls == '1' ) {
ldap_start_tls ( $connection );
}
2016-07-13 05:48:16 -07:00
return $connection ;
}
/**
* Binds / authenticates the user to LDAP , and returns their attributes .
*
* @ author [ A . Gianotto ] [ < snipe @ snipe . net > ]
* @ since [ v3 . 0 ]
* @ param $username
* @ param $password
* @ param bool | false $user
* @ return bool true if the username and / or password provided are valid
* false if the username and / or password provided are invalid
* array of ldap_attributes if $user is true
*
*/
2016-07-14 16:27:32 -07:00
static function findAndBindUserLdap ( $username , $password )
{
$settings = Setting :: getSettings ();
2016-07-13 05:48:16 -07:00
$connection = Ldap :: connectToLdap ();
2016-07-14 16:27:32 -07:00
$ldap_username_field = $settings -> ldap_username_field ;
$baseDn = $settings -> ldap_basedn ;
2017-10-16 15:29:06 -07:00
$userDn = $ldap_username_field . '=' . $username . ',' . $settings -> ldap_basedn ;
2016-07-13 05:48:16 -07:00
2016-12-29 14:02:18 -08:00
if ( $settings -> is_ad == '1' ) {
2017-10-16 15:29:06 -07:00
// Check if they are using the userprincipalname for the username field.
2016-08-30 08:34:17 -07:00
// If they are, we can skip building the UPN to authenticate against AD
2016-12-29 14:02:18 -08:00
if ( $ldap_username_field == 'userprincipalname' ) {
2016-08-30 08:34:17 -07:00
$userDn = $username ;
2016-07-14 15:52:52 -07:00
} else {
2016-08-30 08:34:17 -07:00
// In case they haven't added an AD domain
2017-10-16 15:29:06 -07:00
$userDn = ( $settings -> ad_domain != '' ) ? $username . '@' . $settings -> ad_domain : $username . '@' . $settings -> email_domain ;
2016-07-14 15:52:52 -07:00
}
2016-07-13 15:18:29 -07:00
}
2017-10-16 09:00:51 -07:00
\Log :: debug ( 'Attempting to login using distinguished name:' . $userDn );
2016-07-14 16:27:32 -07:00
$filterQuery = $settings -> ldap_auth_filter_query . $username ;
2016-07-13 05:48:16 -07:00
2016-07-14 15:52:52 -07:00
if ( ! $ldapbind = @ ldap_bind ( $connection , $userDn , $password )) {
2016-07-13 05:48:16 -07:00
return false ;
}
if ( ! $results = ldap_search ( $connection , $baseDn , $filterQuery )) {
throw new Exception ( 'Could not search LDAP: ' );
}
if ( ! $entry = ldap_first_entry ( $connection , $results )) {
return false ;
}
2017-08-31 11:00:08 -07:00
if ( ! $user = ldap_get_attributes ( $connection , $entry )) {
2016-07-13 05:48:16 -07:00
return false ;
}
return $user ;
}
/**
* Binds / authenticates an admin to LDAP for LDAP searching / syncing .
* Here we also return a better error if the app key is donked .
*
* @ author [ A . Gianotto ] [ < snipe @ snipe . net > ]
* @ since [ v3 . 0 ]
* @ param bool | false $user
* @ return bool true if the username and / or password provided are valid
* false if the username and / or password provided are invalid
*
*/
2016-07-14 16:27:32 -07:00
static function bindAdminToLdap ( $connection )
{
2016-07-13 05:48:16 -07:00
$ldap_username = Setting :: getSettings () -> ldap_uname ;
// Lets return some nicer messages for users who donked their app key, and disable LDAP
try {
$ldap_pass = \Crypt :: decrypt ( Setting :: getSettings () -> ldap_pword );
} catch ( Exception $e ) {
throw new Exception ( 'Your app key has changed! Could not decrypt LDAP password using your current app key, so LDAP authentication has been disabled. Login with a local account, update the LDAP password and re-enable it in Admin > Settings.' );
}
if ( ! $ldapbind = @ ldap_bind ( $connection , $ldap_username , $ldap_pass )) {
throw new Exception ( 'Could not bind to LDAP: ' . ldap_error ( $connection ));
}
}
/**
2016-07-14 23:49:32 -07:00
* Parse and map LDAP attributes based on settings
*
* @ author [ A . Gianotto ] [ < snipe @ snipe . net > ]
* @ since [ v3 . 0 ]
2016-07-13 05:48:16 -07:00
*
* @ param $ldapatttibutes
* @ return array | bool
*/
2016-07-14 23:49:32 -07:00
static function parseAndMapLdapAttributes ( $ldapatttibutes )
2016-07-13 05:48:16 -07:00
{
//Get LDAP attribute config
$ldap_result_username = Setting :: getSettings () -> ldap_username_field ;
$ldap_result_emp_num = Setting :: getSettings () -> ldap_emp_num ;
$ldap_result_last_name = Setting :: getSettings () -> ldap_lname_field ;
$ldap_result_first_name = Setting :: getSettings () -> ldap_fname_field ;
$ldap_result_email = Setting :: getSettings () -> ldap_email ;
// Get LDAP user data
$item = array ();
$item [ " username " ] = isset ( $ldapatttibutes [ $ldap_result_username ][ 0 ]) ? $ldapatttibutes [ $ldap_result_username ][ 0 ] : " " ;
$item [ " employee_number " ] = isset ( $ldapatttibutes [ $ldap_result_emp_num ][ 0 ]) ? $ldapatttibutes [ $ldap_result_emp_num ][ 0 ] : " " ;
$item [ " lastname " ] = isset ( $ldapatttibutes [ $ldap_result_last_name ][ 0 ]) ? $ldapatttibutes [ $ldap_result_last_name ][ 0 ] : " " ;
$item [ " firstname " ] = isset ( $ldapatttibutes [ $ldap_result_first_name ][ 0 ]) ? $ldapatttibutes [ $ldap_result_first_name ][ 0 ] : " " ;
$item [ " email " ] = isset ( $ldapatttibutes [ $ldap_result_email ][ 0 ]) ? $ldapatttibutes [ $ldap_result_email ][ 0 ] : " " ;
2016-07-14 23:49:32 -07:00
return $item ;
}
/**
* Create user from LDAP attributes
*
* @ author [ A . Gianotto ] [ < snipe @ snipe . net > ]
* @ since [ v3 . 0 ]
* @ param $ldapatttibutes
* @ return array | bool
*/
static function createUserFromLdap ( $ldapatttibutes )
{
$item = Ldap :: parseAndMapLdapAttributes ( $ldapatttibutes );
2016-08-04 14:29:28 -07:00
2016-07-13 05:48:16 -07:00
// Create user from LDAP data
if ( ! empty ( $item [ " username " ])) {
2016-07-15 16:38:49 -07:00
$user = new User ;
2016-07-14 23:49:32 -07:00
$user -> first_name = $item [ " firstname " ];
$user -> last_name = $item [ " lastname " ];
$user -> username = $item [ " username " ];
$user -> email = $item [ " email " ];
2016-08-04 14:29:28 -07:00
if ( Setting :: getSettings () -> ldap_pw_sync == '1' ) {
$user -> password = bcrypt ( Input :: get ( " password " ));
} else {
$pass = substr ( str_shuffle ( " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ " ), 0 , 25 );
$user -> password = bcrypt ( $pass );
}
2016-07-14 23:49:32 -07:00
$user -> activated = 1 ;
$user -> ldap_import = 1 ;
$user -> notes = 'Imported on first login from LDAP' ;
if ( $user -> save ()) {
2016-12-01 15:54:13 -08:00
return $user ;
2016-07-13 05:48:16 -07:00
} else {
2016-07-14 23:49:32 -07:00
LOG :: debug ( 'Could not create user.' . $user -> getErrors ());
2016-10-29 05:50:55 -07:00
throw new Exception ( " Could not create user: " . $user -> getErrors ());
2016-07-13 05:48:16 -07:00
}
}
return false ;
}
2016-07-14 23:49:32 -07:00
/**
* Searches LDAP
*
* @ author [ A . Gianotto ] [ < snipe @ snipe . net > ]
* @ since [ v3 . 0 ]
* @ param $ldapatttibutes
2017-01-11 23:37:14 -08:00
* @ param $base_dn
2016-07-14 23:49:32 -07:00
* @ return array | bool
*/
2017-01-11 23:37:14 -08:00
static function findLdapUsers ( $base_dn = null )
2016-12-29 14:02:18 -08:00
{
2016-07-13 05:48:16 -07:00
$ldapconn = Ldap :: connectToLdap ();
$ldap_bind = Ldap :: bindAdminToLdap ( $ldapconn );
2017-01-11 23:37:14 -08:00
// Default to global base DN if nothing else is provided.
if ( is_null ( $base_dn )) {
$base_dn = Setting :: getSettings () -> ldap_basedn ;
}
2016-07-13 05:48:16 -07:00
$filter = Setting :: getSettings () -> ldap_filter ;
// Set up LDAP pagination for very large databases
$page_size = 500 ;
$cookie = '' ;
$result_set = array ();
$global_count = 0 ;
// Perform the search
do {
2016-08-23 11:32:11 -07:00
2016-07-13 05:48:16 -07:00
// Paginate (non-critical, if not supported by server)
2016-08-23 11:32:11 -07:00
if ( ! $ldap_paging = @ ldap_control_paged_result ( $ldapconn , $page_size , false , $cookie )) {
throw new Exception ( 'Problem with your LDAP connection. Try checking the Use TLS setting in Admin > Settings. ' );
}
2016-07-13 05:48:16 -07:00
$search_results = ldap_search ( $ldapconn , $base_dn , '(' . $filter . ')' );
if ( ! $search_results ) {
2018-04-26 16:31:02 -07:00
return redirect () -> route ( 'users.index' ) -> with ( 'error' , trans ( 'admin/users/message.error.ldap_could_not_search' ) . ldap_error ( $ldapconn ));
2016-07-13 05:48:16 -07:00
}
// Get results from page
$results = ldap_get_entries ( $ldapconn , $search_results );
if ( ! $results ) {
2018-04-26 16:31:02 -07:00
return redirect () -> route ( 'users.index' ) -> with ( 'error' , trans ( 'admin/users/message.error.ldap_could_not_get_entries' ) . ldap_error ( $ldapconn ));
2016-07-13 05:48:16 -07:00
}
// Add results to result set
$global_count += $results [ 'count' ];
$result_set = array_merge ( $result_set , $results );
2016-12-07 17:07:48 -08:00
@ ldap_control_paged_result_response ( $ldapconn , $search_results , $cookie );
2016-07-13 05:48:16 -07:00
} while ( $cookie !== null && $cookie != '' );
// Clean up after search
$result_set [ 'count' ] = $global_count ;
$results = $result_set ;
ldap_control_paged_result ( $ldapconn , 0 );
return $results ;
}
}