2020-11-30 17:11:44 -08:00
< ? php
declare ( strict_types = 1 );
namespace App\Console\Commands ;
2021-06-10 13:15:52 -07:00
use Adldap\Models\User as AdldapUser ;
use App\Models\Location ;
2020-11-30 17:11:44 -08:00
use App\Models\User ;
use App\Services\LdapAd ;
2021-06-10 13:15:52 -07:00
use Exception ;
2020-11-30 17:11:44 -08:00
use Illuminate\Console\Command ;
2021-06-10 13:15:52 -07:00
use Log ;
2020-11-30 17:11:44 -08:00
/**
* LDAP / AD sync command .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
class LdapSyncNg extends Command
{
/**
* The name and signature of the console command .
*
* @ var string
*/
protected $signature = ' snipeit : ldap - sync - ng
{ -- location = : A location name }
{ -- location_id = : A location id }
{ -- base_dn = : A diffrent base DN to use }
{ -- summary : Print summary }
{ -- json_summary : Print summary in json format }
{ -- dryrun : Run the sync process but don\ 't update the database}' ;
/**
* The console command description .
*
* @ var string
*/
protected $description = 'Command line LDAP/AD sync' ;
/**
* An LdapAd instance .
*
* @ var \App\Models\LdapAd
*/
private $ldap ;
/**
* LDAP settings collection .
*
* @ var \Illuminate\Support\Collection
*/
private $settings = null ;
/**
* A default location collection .
*
* @ var \Illuminate\Support\Collection
*/
private $defaultLocation = null ;
/**
* Mapped locations collection .
*
* @ var \Illuminate\Support\Collection
*/
private $mappedLocations = null ;
/**
* The summary collection .
*
* @ var \Illuminate\Support\Collection
*/
private $summary ;
/**
* Is dry - run ?
*
* @ var bool
*/
private $dryrun = false ;
/**
* Show users to be imported .
*
* @ var array
*/
private $userlist = [];
/**
* Create a new command instance .
*/
public function __construct ( LdapAd $ldap )
{
parent :: __construct ();
2021-06-10 13:15:52 -07:00
$this -> ldap = $ldap ;
2020-11-30 17:11:44 -08:00
$this -> settings = $this -> ldap -> ldapSettings ;
2021-06-10 13:15:52 -07:00
$this -> summary = collect ();
2020-11-30 17:11:44 -08:00
}
/**
* Execute the console command .
*
* @ return mixed
*/
public function handle ()
{
2021-06-10 13:15:52 -07:00
$dispatcher = \Adldap\Adldap :: getEventDispatcher ();
2020-11-30 17:11:44 -08:00
// Listen for all model events.
$dispatcher -> listen ( 'Adldap\Models\Events\*' , function ( $eventName , array $data ) {
echo $eventName ; // Returns 'Adldap\Models\Events\Updating'
var_dump ( $data ); // Returns [0] => (object) Adldap\Models\Events\Updating;
2021-06-10 13:15:52 -07:00
\Log :: debug ( 'Event: ' . $eventName . ' data - ' . print_r ( $data , true ));
2020-11-30 17:11:44 -08:00
});
$dispatcher -> listen ( 'Adldap\Auth\Events\*' , function ( $eventName , array $data ) {
echo $eventName ; // Returns 'Adldap\Models\Events\Updating'
var_dump ( $data ); // Returns [0] => (object) Adldap\Models\Events\Updating;
2021-06-10 13:15:52 -07:00
\Log :: debug ( 'Event: ' . $eventName . ' data - ' . print_r ( $data , true ));
2020-11-30 17:11:44 -08:00
});
2021-06-10 13:15:52 -07:00
ini_set ( 'max_execution_time' , env ( 'LDAP_TIME_LIM' , '600' )); //600 seconds = 10 minutes
2020-11-30 17:11:44 -08:00
ini_set ( 'memory_limit' , '500M' );
$old_error_reporting = error_reporting (); // grab old error_reporting .ini setting, for later re-enablement
error_reporting ( $old_error_reporting & ~ E_DEPRECATED ); // disable deprecation warnings, for LDAP in PHP 7.4 (and greater)
if ( $this -> option ( 'dryrun' )) {
$this -> dryrun = true ;
}
$this -> checkIfLdapIsEnabled ();
$this -> checkLdapConnection ();
$this -> setBaseDn ();
$this -> getUserDefaultLocation ();
/*
* Use the default location if set , this is needed for the LDAP users sync page
*/
2021-06-10 13:15:52 -07:00
if ( ! $this -> option ( 'base_dn' ) && null == $this -> defaultLocation ) {
2020-11-30 17:11:44 -08:00
$this -> getMappedLocations ();
}
$this -> processLdapUsers ();
// Print table of users
if ( $this -> dryrun ) {
$this -> info ( 'The following users will be synced!' );
$headers = [ 'First Name' , 'Last Name' , 'Username' , 'Email' , 'Employee #' , 'Location Id' , 'Status' ];
$this -> table ( $headers , $this -> summary -> toArray ());
}
error_reporting ( $old_error_reporting ); // re-enable deprecation warnings.
2021-06-10 13:15:52 -07:00
2020-11-30 17:11:44 -08:00
return $this -> getSummary ();
}
/**
* Generate the LDAP sync summary .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*
* @ return string
*/
private function getSummary () : string
{
if ( $this -> option ( 'summary' ) && null === $this -> dryrun ) {
$this -> summary -> each ( function ( $item ) {
$this -> info ( 'USER: ' . $item [ 'note' ]);
if ( 'ERROR' === $item [ 'status' ]) {
$this -> error ( 'ERROR: ' . $item [ 'note' ]);
}
});
} elseif ( $this -> option ( 'json_summary' )) {
$json_summary = [
'error' => false ,
'error_message' => '' ,
'summary' => $this -> summary -> toArray (),
];
$this -> info ( json_encode ( $json_summary ));
}
return '' ;
}
/**
* Create a new user or update an existing user .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*
* @ param \Adldap\Models\User $snipeUser
*/
private function updateCreateUser ( AdldapUser $snipeUser ) : void
{
$user = $this -> ldap -> processUser ( $snipeUser , $this -> defaultLocation , $this -> mappedLocations );
$summary = [
'firstname' => $user -> first_name ,
'lastname' => $user -> last_name ,
'username' => $user -> username ,
'employee_number' => $user -> employee_num ,
'email' => $user -> email ,
'location_id' => $user -> location_id ,
];
// Only update the database if is not a dry run
2021-06-10 13:15:52 -07:00
if ( ! $this -> dryrun ) {
2020-11-30 17:11:44 -08:00
if ( $user -> isDirty ()) { //if nothing on the user changed, don't bother trying to save anything nor put anything in the summary
if ( $user -> save ()) {
2021-06-10 13:15:52 -07:00
$summary [ 'note' ] = ( $user -> wasRecentlyCreated ? 'CREATED' : 'UPDATED' );
2020-11-30 17:11:44 -08:00
$summary [ 'status' ] = 'SUCCESS' ;
} else {
$errors = '' ;
foreach ( $user -> getErrors () -> getMessages () as $error ) {
2021-06-10 13:15:52 -07:00
$errors .= implode ( ', ' , $error );
2020-11-30 17:11:44 -08:00
}
2021-06-10 13:15:52 -07:00
$summary [ 'note' ] = $snipeUser -> getDN () . ' was not imported. REASON: ' . $errors ;
2020-11-30 17:11:44 -08:00
$summary [ 'status' ] = 'ERROR' ;
}
} else {
$summary = null ;
}
}
// $summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED'); // this seems, kinda, like, superfluous, relative to the $summary['note'] thing above, yeah?
2021-06-10 13:15:52 -07:00
if ( $summary ) { //if the $user wasn't dirty, $summary was set to null so that we will skip the following push()
2020-11-30 17:11:44 -08:00
$this -> summary -> push ( $summary );
}
}
/**
* Process the users to update / create .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
private function processLdapUsers () : void
{
try {
2021-06-10 13:15:52 -07:00
\Log :: debug ( 'CAL:LING GET LDAP SUSERS' );
2020-11-30 17:11:44 -08:00
$ldapUsers = $this -> ldap -> getLdapUsers ();
2021-06-10 13:15:52 -07:00
\Log :: debug ( 'END CALLING GET LDAP USERS' );
2020-11-30 17:11:44 -08:00
} catch ( Exception $e ) {
$this -> outputError ( $e );
exit ( $e -> getMessage ());
}
if ( 0 == $ldapUsers -> count ()) {
$msg = 'ERROR: No users found!' ;
Log :: error ( $msg );
if ( $this -> dryrun ) {
$this -> error ( $msg );
}
exit ( $msg );
}
// Process each individual users
foreach ( $ldapUsers -> getResults () as $user ) { // AdLdap2's paginate() method is weird, it gets *everything* and ->getResults() returns *everything*
$this -> updateCreateUser ( $user );
}
}
/**
* Get the mapped locations if a base_dn is provided .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
private function getMappedLocations ()
{
$ldapOuLocation = Location :: where ( 'ldap_ou' , '!=' , '' ) -> select ([ 'id' , 'ldap_ou' ]) -> get ();
$locations = $ldapOuLocation -> sortBy ( function ( $ou , $key ) {
return strlen ( $ou -> ldap_ou );
});
if ( $locations -> count () > 0 ) {
$msg = 'Some locations have special OUs set. Locations will be automatically set for users in those OUs.' ;
LOG :: debug ( $msg );
if ( $this -> dryrun ) {
$this -> info ( $msg );
}
$this -> mappedLocations = $locations -> pluck ( 'ldap_ou' , 'id' ); // TODO: this seems ok-ish, but the key-> value is going location_id -> OU name, and the primary action here is the opposite of that - going from OU's to location ID's.
}
}
/**
* Set the base dn if supplied .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
private function setBaseDn () : void
{
if ( $this -> option ( 'base_dn' )) {
$this -> ldap -> baseDn = $this -> option ( 'base_dn' );
$msg = sprintf ( 'Importing users from specified base DN: "%s"' , $this -> ldap -> baseDn );
LOG :: debug ( $msg );
if ( $this -> dryrun ) {
$this -> info ( $msg );
}
}
}
/**
* Get a default location id for imported users .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
private function getUserDefaultLocation () : void
{
$location = $this -> option ( 'location_id' ) ? ? $this -> option ( 'location' );
if ( $location ) {
$userLocation = Location :: where ( 'name' , '=' , $location )
-> orWhere ( 'id' , '=' , intval ( $location ))
-> select ([ 'name' , 'id' ])
-> first ();
if ( $userLocation ) {
$msg = 'Importing users with default location: ' . $userLocation -> name . ' (' . $userLocation -> id . ')' ;
LOG :: debug ( $msg );
if ( $this -> dryrun ) {
$this -> info ( $msg );
}
$this -> defaultLocation = collect ([
$userLocation -> id => $userLocation -> name ,
]);
} else {
$msg = 'The supplied location is invalid!' ;
LOG :: error ( $msg );
if ( $this -> dryrun ) {
$this -> error ( $msg );
}
exit ( 0 );
}
}
}
/**
* Check if LDAP intergration is enabled .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
private function checkIfLdapIsEnabled () : void
{
if ( false === $this -> settings [ 'ldap_enabled' ]) {
$msg = 'LDAP intergration is not enabled. Exiting sync process.' ;
$this -> info ( $msg );
Log :: info ( $msg );
exit ( 0 );
}
}
/**
* Check to make sure we can access the server .
*
* @ author Wes Hulette < jwhulette @ gmail . com >
*
* @ since 5.0 . 0
*/
private function checkLdapConnection () : void
{
try {
$this -> ldap -> testLdapAdUserConnection ();
$this -> ldap -> testLdapAdBindConnection ();
} catch ( Exception $e ) {
$this -> outputError ( $e );
exit ( 0 );
}
}
/**
* Output the json summary to the screen if enabled .
*
* @ param Exception $error
*/
private function outputError ( $error ) : void
{
if ( $this -> option ( 'json_summary' )) {
$json_summary = [
'error' => true ,
'error_message' => $error -> getMessage (),
'summary' => [],
];
$this -> info ( json_encode ( $json_summary ));
}
$this -> error ( $error -> getMessage ());
LOG :: error ( $error );
}
}