fix LDAP/AD sync: function calls for password creation (#6581)

* - change generatePassword to be more secure (allow duplicate chars)
- move generatePassword from trait to helper
- fix summary output for sync command

* - Don't treat ldap_active_flag as boolean - fixes sync not working at all when ldap field is set
- Sync non activated users (But set activated=0)

* - Read user first before checking against user settings

* Fix failed logins to not throw exceptions
This commit is contained in:
Steffen 2019-01-15 23:05:47 +01:00 committed by snipe
parent f0500e9797
commit 74c099f0b3
5 changed files with 75 additions and 68 deletions

View file

@ -142,14 +142,15 @@ class LdapSync extends Command
* @return string * @return string
*/ */
private function getSummary(): string private function getSummary(): string
{ {
if ($this->option('summary') && null === $this->dryrun) { if ($this->option('summary') && !$this->dryrun) {
$this->summary->each(function ($item) { $this->summary->each(function ($item) {
$this->info('USER: '.$item['note']);
if ('ERROR' === $item['status']) { if ('ERROR' === $item['status']) {
$this->error('ERROR: '.$item['note']); $this->error('ERROR: '.$item['note']);
} }
else {
$this->info('USER: '.$item['note']);
}
}); });
} elseif ($this->option('json_summary')) { } elseif ($this->option('json_summary')) {
$json_summary = [ $json_summary = [
@ -175,6 +176,12 @@ class LdapSync extends Command
private function updateCreateUser(AdldapUser $snipeUser): void private function updateCreateUser(AdldapUser $snipeUser): void
{ {
$user = $this->ldap->processUser($snipeUser, $this->defaultLocation, $this->mappedLocations); $user = $this->ldap->processUser($snipeUser, $this->defaultLocation, $this->mappedLocations);
if(!$user) {
$summary['note'] = sprintf("'%s' was not imported. REASON: User inactive or not found", $snipeUser->ou);
$summary['status'] = 'ERROR';
$this->summary->push($summary);
return;
}
$summary = [ $summary = [
'firstname' => $user->first_name, 'firstname' => $user->first_name,
'lastname' => $user->last_name, 'lastname' => $user->last_name,
@ -186,19 +193,19 @@ class LdapSync extends Command
// Only update the database if is not a dry run // Only update the database if is not a dry run
if (!$this->dryrun) { if (!$this->dryrun) {
if ($user->save()) { if ($user->save()) {
$summary['note'] = ($user->wasRecentlyCreated ? 'CREATED' : 'UPDATED'); $summary['note'] = sprintf("'%s' %s", $user->username, ($user->wasRecentlyCreated ? 'CREATED' : 'UPDATED'));
$summary['status'] = 'SUCCESS'; $summary['status'] = 'SUCCESS';
} else { } else {
$errors = ''; $errors = '';
foreach ($user->getErrors()->getMessages() as $error) { foreach ($user->getErrors()->getMessages() as $error) {
$errors .= $error[0]; $errors .= $error[0];
} }
$summary['note'] = $userMsg.' was not imported. REASON: '.$errors; $summary['note'] = sprintf("'%s' was not imported. REASON: %s", $user->username, $errors);
$summary['status'] = 'ERROR'; $summary['status'] = 'ERROR';
} }
} }
$summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED'); //$summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED');
$this->summary->push($summary); $this->summary->push($summary);
} }

View file

@ -670,7 +670,37 @@ class Helper
return false; return false;
} }
/**
* Generate a random encrypted password.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
public static function generateEncyrptedPassword(): string
{
return bcrypt(Helper::generateUnencryptedPassword());
}
/**
* Get a random unencrypted password.
*
* @author Steffen Buehl <sb@sbuehl.com>
*
* @since 5.0.0
*
* @return string
*/
public static function generateUnencryptedPassword(): string
{
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$password = '';
for ( $i = 0; $i < 20; $i++ ) {
$password .= substr( $chars, random_int( 0, strlen( $chars ) - 1 ), 1 );
}
return $password;
}
} }

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Services; namespace App\Services;
use App\Models\User; use App\Models\User;
use App\Helpers\Helper;
use Exception; use Exception;
use Adldap\Adldap; use Adldap\Adldap;
use Adldap\Query\Paginator; use Adldap\Query\Paginator;
@ -93,16 +94,22 @@ class LdapAd extends LdapAdConfiguration
} }
// Should we sync the logged in user // Should we sync the logged in user
if ($this->isLdapSync($record)) { try {
try { Log::debug('Attempting to find user in LDAP directory');
Log::debug('Attempting to find user in LDAP directory'); $record = $this->ldap->search()->findBy($this->ldapSettings['ldap_username_field'], $username);
$record = $this->ldap->search()->findBy($this->ldapSettings['ldap_username_field'], $username);
} catch (ModelNotFoundException $e) { if($record) {
Log::error($e->getMessage()); if ($this->isLdapSync($record)) {
throw new Exception('Unable to find user in LDAP directory!'); $this->syncUserLdapLogin($record, $password);
}
} }
else {
$this->syncUserLdapLogin($record, $password); Log::error($e->getMessage());
throw new Exception('Unable to find user in LDAP directory!');
}
} catch (ModelNotFoundException $e) {
Log::error($e->getMessage());
throw new Exception('Unable to find user in LDAP directory!');
} }
return User::where('username', $username) return User::where('username', $username)
@ -126,21 +133,19 @@ class LdapAd extends LdapAdConfiguration
public function processUser(AdldapUser $user, ?Collection $defaultLocation=null, ?Collection $mappedLocations=null): ?User public function processUser(AdldapUser $user, ?Collection $defaultLocation=null, ?Collection $mappedLocations=null): ?User
{ {
// Only sync active users // Only sync active users
if ($this->isLdapSync($user)) { if(!$user) {
$snipeUser = []; return null;
$snipeUser['username'] = $user->{$this->ldapSettings['ldap_username_field']}[0] ?? '';
$snipeUser['employee_number'] = $user->{$this->ldapSettings['ldap_emp_num']}[0] ?? '';
$snipeUser['lastname'] = $user->{$this->ldapSettings['ldap_lname_field']}[0] ?? '';
$snipeUser['firstname'] = $user->{$this->ldapSettings['ldap_fname_field']}[0] ?? '';
$snipeUser['email'] = $user->{$this->ldapSettings['ldap_email']}[0] ?? '';
$snipeUser['location_id'] = $this->getLocationId($user, $defaultLocation, $mappedLocations);
$snipeUser['activated'] = $this->getActiveStatus($user);
return $this->setUserModel($snipeUser);
} }
$snipeUser = [];
$snipeUser['username'] = $user->{$this->ldapSettings['ldap_username_field']}[0] ?? '';
$snipeUser['employee_number'] = $user->{$this->ldapSettings['ldap_emp_num']}[0] ?? '';
$snipeUser['lastname'] = $user->{$this->ldapSettings['ldap_lname_field']}[0] ?? '';
$snipeUser['firstname'] = $user->{$this->ldapSettings['ldap_fname_field']}[0] ?? '';
$snipeUser['email'] = $user->{$this->ldapSettings['ldap_email']}[0] ?? '';
$snipeUser['location_id'] = $this->getLocationId($user, $defaultLocation, $mappedLocations);
$snipeUser['activated'] = $this->getActiveStatus($user);
// We are not syncing user info return $this->setUserModel($snipeUser);
return null;
} }
/** /**
@ -161,7 +166,7 @@ class LdapAd extends LdapAdConfiguration
'username' => $userInfo['username'], 'username' => $userInfo['username'],
]); ]);
$user->username = $user->username ?? trim($userInfo['username']); $user->username = $user->username ?? trim($userInfo['username']);
$user->password = $user->password ?? $this->generateEncyrptedPassword(); $user->password = $user->password ?? Helper::generateEncyrptedPassword();
$user->first_name = trim($userInfo['firstname']); $user->first_name = trim($userInfo['firstname']);
$user->last_name = trim($userInfo['lastname']); $user->last_name = trim($userInfo['lastname']);
$user->email = trim($userInfo['email']); $user->email = trim($userInfo['email']);
@ -349,6 +354,7 @@ class LdapAd extends LdapAdConfiguration
$this->ldapSettings['ldap_lname_field'], $this->ldapSettings['ldap_lname_field'],
$this->ldapSettings['ldap_email'], $this->ldapSettings['ldap_email'],
$this->ldapSettings['ldap_emp_num'], $this->ldapSettings['ldap_emp_num'],
$this->ldapSettings['ldap_active_flag'],
'memberOf', 'memberOf',
'useraccountcontrol', 'useraccountcontrol',
]; ];

View file

@ -22,7 +22,7 @@ class LdapAdConfiguration
const LDAP_PORT = 389; const LDAP_PORT = 389;
const CONNECTION_TIMEOUT = 5; const CONNECTION_TIMEOUT = 5;
const DEFAULT_LDAP_VERSION = 3; const DEFAULT_LDAP_VERSION = 3;
const LDAP_BOOLEAN_SETTINGS = ['ldap_enabled', 'ldap_server_cert_ignore', 'ldap_active_flag', 'ldap_tls', 'ldap_tls', 'ldap_pw_sync', 'is_ad']; const LDAP_BOOLEAN_SETTINGS = ['ldap_enabled', 'ldap_server_cert_ignore', 'ldap_tls', 'ldap_tls', 'ldap_pw_sync', 'is_ad'];
/** /**
* Ldap Settings. * Ldap Settings.

View file

@ -1,36 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Traits;
trait UserTrait
{
/**
* Generate a random encrypted password.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
public function generateEncyrptedPassword(): string
{
return bcrypt($this->generateUnencryptedPassword());
}
/**
* Get a random unencrypted password.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
public function generateUnencryptedPassword(): string
{
return substr(str_shuffle('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'), 0, 20);
}
}