[WIP] v5 Develop: New LDAP implementation (#6352)

* Fixed missing oauth tables during setup.

* WIP New LDAP implementation

* WIP New LDAP implementation

* WIP New LDAP implementation


Merge remote-tracking branch 'origin/WIP_LDAP' into WIP_LDAP

* WIP New LDAP implementation

Added Adldap2 to handle ldap intergration.

* Updated per PR quality review

* Added specific LDAP settings method

* Corrected version number

* Added return documentation

* Added imports

* Changed class to be injected into controller

* Updated with PR suggestions
This commit is contained in:
Wes Hulette 2018-12-06 17:05:43 -05:00 committed by snipe
parent 3ed2f55696
commit 34246ee4ef
19 changed files with 1508 additions and 955 deletions

View file

@ -1,14 +1,24 @@
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Setting;
use App\Models\Ldap;
use App\Models\User;
use App\Models\Location;
use Log;
use Exception;
use App\Models\User;
use App\Models\LdapAd;
use App\Models\Location;
use Illuminate\Console\Command;
use Adldap\Models\User as AdldapUser;
/**
* LDAP / AD sync command.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
class LdapSync extends Command
{
/**
@ -16,23 +26,79 @@ class LdapSync extends Command
*
* @var string
*/
protected $signature = 'snipeit:ldap-sync {--location=} {--location_id=} {--base_dn=} {--summary} {--json_summary}';
protected $signature = 'snipeit:ldap-sync
{--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 sync';
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.
*
* @return void
*/
public function __construct()
public function __construct(LdapAd $ldap)
{
parent::__construct();
$this->ldap = $ldap;
$this->settings = $this->ldap->ldapSettings;
$this->summary = collect();
}
/**
@ -42,206 +108,273 @@ class LdapSync extends Command
*/
public function handle()
{
ini_set('max_execution_time', 600); //600 seconds = 10 minutes
ini_set('max_execution_time', '600'); //600 seconds = 10 minutes
ini_set('memory_limit', '500M');
$ldap_result_username = Setting::getSettings()->ldap_username_field;
$ldap_result_last_name = Setting::getSettings()->ldap_lname_field;
$ldap_result_first_name = Setting::getSettings()->ldap_fname_field;
$ldap_result_active_flag = Setting::getSettings()->ldap_active_flag_field;
$ldap_result_emp_num = Setting::getSettings()->ldap_emp_num;
$ldap_result_email = Setting::getSettings()->ldap_email;
try {
$ldapconn = Ldap::connectToLdap();
Ldap::bindAdminToLdap($ldapconn);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::error($e);
return [];
if ($this->option('dryrun')) {
$this->dryrun = true;
}
$summary = array();
$this->checkIfLdapIsEnabled();
$this->checkLdapConnetion();
$this->setBaseDn();
$this->getUserDefaultLocation();
/*
* Use the default location if set, this is needed for the LDAP users sync page
*/
if (!$this->option('base_dn') && null == $this->defaultLocation) {
$this->getMappedLocations();
}
$this->processLdapUsers();
try {
if ($this->option('base_dn') != '') {
$search_base = $this->option('base_dn');
LOG::debug('Importing users from specified base DN: \"'.$search_base.'\".');
} else {
$search_base = null;
}
$results = Ldap::findLdapUsers($search_base);
} catch (\Exception $e) {
if ($this->option('json_summary')) {
$json_summary = [ "error" => true, "error_message" => $e->getMessage(), "summary" => [] ];
$this->info(json_encode($json_summary));
}
LOG::error($e);
return [];
// 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());
}
/* Determine which location to assign users to by default. */
$location = NULL;
return $this->getSummary();
}
if ($this->option('location')!='') {
$location = Location::where('name', '=', $this->option('location'))->first();
LOG::debug('Location name '.$this->option('location').' passed');
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
} elseif ($this->option('location_id')!='') {
$location = Location::where('id', '=', $this->option('location_id'))->first();
LOG::debug('Location ID '.$this->option('location_id').' passed');
LOG::debug('Importing to '.$location->name.' ('.$location->id.')');
}
/**
* 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 (!isset($location)) {
LOG::debug('That location is invalid or a location was not provided, so no location will be assigned by default.');
}
/* Process locations with explicitly defined OUs, if doing a full import. */
if ($this->option('base_dn')=='') {
// Retrieve locations with a mapped OU, and sort them from the shallowest to deepest OU (see #3993)
$ldap_ou_locations = Location::where('ldap_ou', '!=', '')->get()->toArray();
$ldap_ou_lengths = array();
foreach ($ldap_ou_locations as $location) {
$ldap_ou_lengths[] = strlen($location["ldap_ou"]);
}
array_multisort($ldap_ou_lengths, SORT_ASC, $ldap_ou_locations);
if (sizeof($ldap_ou_locations) > 0) {
LOG::debug('Some locations have special OUs set. Locations will be automatically set for users in those OUs.');
}
// Inject location information fields
for ($i = 0; $i < $results["count"]; $i++) {
$results[$i]["ldap_location_override"] = false;
$results[$i]["location_id"] = 0;
}
// Grab subsets based on location-specific DNs, and overwrite location for these users.
foreach ($ldap_ou_locations as $ldap_loc) {
$location_users = Ldap::findLdapUsers($ldap_loc["ldap_ou"]);
$usernames = array();
for ($i = 0; $i < $location_users["count"]; $i++) {
if (array_key_exists($ldap_result_username, $location_users[$i])) {
$location_users[$i]["ldap_location_override"] = true;
$location_users[$i]["location_id"] = $ldap_loc["id"];
$usernames[] = $location_users[$i][$ldap_result_username][0];
}
if ('ERROR' === $item['status']) {
$this->error('ERROR: '.$item['note']);
}
// Delete located users from the general group.
foreach ($results as $key => $generic_entry) {
if ((is_array($generic_entry)) && (array_key_exists($ldap_result_username, $generic_entry))) {
if (in_array($generic_entry[$ldap_result_username][0], $usernames)) {
unset($results[$key]);
}
}
}
$global_count = $results['count'];
$results = array_merge($location_users, $results);
$results['count'] = $global_count;
}
}
/* Create user account entries in Snipe-IT */
$tmp_pass = substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 20);
$pass = bcrypt($tmp_pass);
for ($i = 0; $i < $results["count"]; $i++) {
if (empty($ldap_result_active_flag) || $results[$i][$ldap_result_active_flag][0] == "TRUE") {
$item = array();
$item["username"] = isset($results[$i][$ldap_result_username][0]) ? $results[$i][$ldap_result_username][0] : "";
$item["employee_number"] = isset($results[$i][$ldap_result_emp_num][0]) ? $results[$i][$ldap_result_emp_num][0] : "";
$item["lastname"] = isset($results[$i][$ldap_result_last_name][0]) ? $results[$i][$ldap_result_last_name][0] : "";
$item["firstname"] = isset($results[$i][$ldap_result_first_name][0]) ? $results[$i][$ldap_result_first_name][0] : "";
$item["email"] = isset($results[$i][$ldap_result_email][0]) ? $results[$i][$ldap_result_email][0] : "" ;
$item["ldap_location_override"] = isset($results[$i]["ldap_location_override"]) ? $results[$i]["ldap_location_override"]:"";
$item["location_id"] = isset($results[$i]["location_id"]) ? $results[$i]["location_id"]:"";
if ( array_key_exists('useraccountcontrol', $results[$i]) ) {
$enabled_accounts = [
'512', '544', '66048', '66080', '262656', '262688', '328192', '328224'
];
$item['activated'] = ( in_array($results[$i]['useraccountcontrol'][0], $enabled_accounts) ) ? 1 : 0;
} else {
$item['activated'] = 0;
// If there is no activated flag, assume this is handled via the OU and activate the users
if (empty($ldap_result_active_flag)) {
$item['activated'] = 1;
}
}
// User exists
$item["createorupdate"] = 'updated';
if (!$user = User::where('username', $item["username"])->first()) {
$user = new User;
$user->password = $pass;
$item["createorupdate"] = 'created';
}
// Create the user if they don't exist.
$user->first_name = e($item["firstname"]);
$user->last_name = e($item["lastname"]);
$user->username = e($item["username"]);
$user->email = e($item["email"]);
$user->employee_num = e($item["employee_number"]);
$user->activated = $item['activated'];
if ($item['ldap_location_override'] == true) {
$user->location_id = $item['location_id'];
} elseif ((isset($location)) && (!empty($location))) {
if ((is_array($location)) && (array_key_exists('id', $location))) {
$user->location_id = $location['id'];
} elseif (is_object($location)) {
$user->location_id = $location->id;
}
}
$user->notes = 'Imported from LDAP';
$user->ldap_import = 1;
$errors = '';
if ($user->save()) {
$item["note"] = $item["createorupdate"];
$item["status"]='success';
} else {
foreach ($user->getErrors()->getMessages() as $key => $err) {
$errors .= $err[0];
}
$item["note"] = $errors;
$item["status"]='error';
}
array_push($summary, $item);
}
}
if ($this->option('summary')) {
for ($x = 0; $x < count($summary); $x++) {
if ($summary[$x]['status']=='error') {
$this->error('ERROR: '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was not imported: '.$summary[$x]['note']);
} else {
$this->info('User '.$summary[$x]['firstname'].' '.$summary[$x]['lastname'].' (username: '.$summary[$x]['username'].') was '.strtoupper($summary[$x]['createorupdate']).'.');
}
}
} else if ($this->option('json_summary')) {
$json_summary = [ "error" => false, "error_message" => "", "summary" => $summary ];
});
} elseif ($this->option('json_summary')) {
$json_summary = [
'error' => false,
'error_message' => '',
'summary' => $this->summary->toArray(),
];
$this->info(json_encode($json_summary));
} else {
return $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
if (!$this->dryrun) {
if ($user->save()) {
$summary['note'] = ($user->wasRecentlyCreated ? 'CREATED' : 'UPDATED');
$summary['status'] = 'SUCCESS';
} else {
$errors = '';
foreach ($user->getErrors()->getMessages() as $error) {
$errors .= $error[0];
}
$summary['note'] = $userMsg.' was not imported. REASON: '.$errors;
$summary['status'] = 'ERROR';
}
}
$summary['note'] = ($user->getOriginal('username') ? 'UPDATED' : 'CREATED');
$this->summary->push($summary);
}
/**
* Process the users to update / create.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param int $page The page to get the result set
*/
private function processLdapUsers(int $page=0): void
{
try {
$ldapUsers = $this->ldap->getLdapUsers($page);
} 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 as $user) {
$this->updateCreateUser($user);
}
if ($ldapUsers->getCurrentPage() < $ldapUsers->getPages()) {
$this->processLdapUsers($ldapUsers->getCurrentPage() + 1);
}
}
/**
* 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');
}
}
/**
* 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 checkLdapConnetion(): 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);
}
}

View file

@ -38,12 +38,10 @@ class Kernel extends ConsoleKernel
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
* @param \Illuminate\Console\Scheduling\Schedule $schedule
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('snipeit:inventory-alerts')->daily();
$schedule->command('snipeit:expiring-alerts')->daily();
$schedule->command('snipeit:expected-checkin')->daily();
@ -53,9 +51,7 @@ class Kernel extends ConsoleKernel
/**
* This method is required by Laravel to handle any console routes
* that are defined in routes/console.php
*
* @return void
* that are defined in routes/console.php.
*/
protected function commands()
{

View file

@ -2,106 +2,88 @@
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Ldap;
use Validator;
use App\Models\Setting;
use Mail;
use App\Notifications\SlackTest;
use Notification;
use App\Notifications\MailTest;
use App\Http\Transformers\LoginAttemptsTransformer;
use DB;
use Mail;
use Validator;
use Notification;
use App\Models\Ldap;
use App\Models\LdapAd;
use App\Models\Setting;
use Illuminate\Http\Request;
use App\Notifications\MailTest;
use App\Notifications\SlackTest;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
use App\Http\Transformers\LoginAttemptsTransformer;
class SettingsController extends Controller
{
public function ldaptest()
{
if (Setting::getSettings()->ldap_enabled!='1') {
\Log::debug('LDAP is not enabled cannot test.');
/**
* Test the ldap settings
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param App\Models\LdapAd $ldap
*
* @return \Illuminate\Http\JsonResponse
*/
public function ldapAdSettingsTest(LdapAd $ldap): JsonResponse
{
if($ldap->ldapSettings['ldap_enabled'] === false) {
Log::info('LDAP is not enabled cannot test.');
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
}
\Log::debug('Preparing to test LDAP connection');
// The connect, bind and resulting users message
$message = [];
Log::info('Preparing to test LDAP user login');
// Test user can connect to the LDAP server
try {
$connection = Ldap::connectToLdap();
try {
\Log::debug('attempting to bind to LDAP for LDAP test');
Ldap::bindAdminToLdap($connection);
return response()->json(['message' => 'It worked!'], 200);
} catch (\Exception $e) {
\Log::debug('Bind failed');
return response()->json(['message' => $e->getMessage()], 400);
//return response()->json(['message' => $e->getMessage()], 500);
}
} catch (\Exception $e) {
\Log::debug('Connection failed');
return response()->json(['message' => $e->getMessage()], 600);
$ldap->testLdapAdUserConnection();
$message['login'] = [
'message' => 'Successfully connected to LDAP server.'
];
} catch (\Exception $ex) {
return response()->json([
'message' => 'Error logging into LDAP server, error: ' . $ex->getMessage() . ' - Verify your that your username and password are correct'
], 400);
}
}
public function ldaptestlogin(Request $request)
{
if (Setting::getSettings()->ldap_enabled!='1') {
\Log::debug('LDAP is not enabled. Cannot test.');
return response()->json(['message' => 'LDAP is not enabled, cannot test.'], 400);
}
$rules = array(
'ldaptest_user' => 'required',
'ldaptest_password' => 'required'
);
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
\Log::debug('LDAP Validation test failed.');
$validation_errors = implode(' ',$validator->errors()->all());
return response()->json(['message' => $validator->errors()->all()], 400);
}
\Log::debug('Preparing to test LDAP login');
Log::info('Preparing to test LDAP bind connection');
// Test user can bind to the LDAP server
try {
$connection = Ldap::connectToLdap();
try {
Ldap::bindAdminToLdap($connection);
\Log::debug('Attempting to bind to LDAP for LDAP test');
try {
$ldap_user = Ldap::findAndBindUserLdap($request->input('ldaptest_user'), $request->input('ldaptest_password'));
if ($ldap_user) {
\Log::debug('It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.');
return response()->json(['message' => 'It worked! '. $request->input('ldaptest_user').' successfully binded to LDAP.'], 200);
}
return response()->json(['message' => 'Login Failed. '. $request->input('ldaptest_user').' did not successfully bind to LDAP.'], 400);
} catch (\Exception $e) {
\Log::debug('LDAP login failed');
return response()->json(['message' => $e->getMessage()], 400);
}
} catch (\Exception $e) {
\Log::debug('Bind failed');
return response()->json(['message' => $e->getMessage()], 400);
//return response()->json(['message' => $e->getMessage()], 500);
}
} catch (\Exception $e) {
\Log::debug('Connection failed');
return response()->json(['message' => $e->getMessage()], 500);
$ldap->testLdapAdBindConnection();
$message['bind'] = [
'message' => 'Successfully binded to LDAP server.'
];
} catch (\Exception $ex) {
return response()->json([
'message' => 'Error binding to LDAP server, error: ' . $ex->getMessage()
], 400);
}
Log::info('Preparing to get sample user set from LDAP directory');
// Get a sample of 10 users so user can verify the data is correct
try {
$users = $ldap->testUserImportSync();
$message['user_sync'] = [
'users' => $users
];
} catch (\Exception $ex) {
$message['user_sync'] = [
'message' => 'Error getting users from LDAP directory, error: ' . $ex->getMessage()
];
return response()->json($message, 400);
}
return response()->json($message, 200);
}
public function slacktest()
{

View file

@ -16,6 +16,7 @@ use Redirect;
use Log;
use View;
use PragmaRX\Google2FA\Google2FA;
use App\Models\LdapAd;
/**
* This controller handles authentication for the user, including local
@ -39,15 +40,24 @@ class LoginController extends Controller
*/
protected $redirectTo = '/';
/**
* An LdapAd instance
*
* @var \App\Models\LdapAd
*/
protected $ldapAd;
/**
* Create a new authentication controller instance.
*
* @return void
*/
public function __construct()
public function __construct(LdapAd $ldapAd)
{
$this->middleware('guest', ['except' => ['logout','postTwoFactorAuth','getTwoFactorAuth','getTwoFactorEnroll']]);
\Session::put('backUrl', \URL::previous());
$this->ldapAd = $ldapAd;
}
function showLoginForm(Request $request)
@ -64,6 +74,29 @@ class LoginController extends Controller
return view('auth.login');
}
/**
* Log in a user by LDAP
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param Request $request
*
* @return User
*
* @throws Exception
*/
private function loginViaLdap(Request $request): User
{
try {
return $this->ldapAd->ldapLogin($request->input('username'), $request->input('password'));
} catch (\Exception $ex) {
LOG::debug("LDAP user login: " . $ex->getMessage());
throw new \Exception($ex->getMessage());
}
}
private function loginViaRemoteUser(Request $request)
{
$remote_user = $request->server('REMOTE_USER');
@ -85,53 +118,6 @@ class LoginController extends Controller
}
}
private function loginViaLdap(Request $request)
{
LOG::debug("Binding user to LDAP.");
$ldap_user = Ldap::findAndBindUserLdap($request->input('username'), $request->input('password'));
if (!$ldap_user) {
LOG::debug("LDAP user ".$request->input('username')." not found in LDAP or could not bind");
throw new \Exception("Could not find user in LDAP directory");
} else {
LOG::debug("LDAP user ".$request->input('username')." successfully bound to LDAP");
}
// Check if the user already exists in the database and was imported via LDAP
$user = User::where('username', '=', Input::get('username'))->whereNull('deleted_at')->where('ldap_import', '=', 1)->where('activated', '=', '1')->first();
LOG::debug("Local auth lookup complete");
// The user does not exist in the database. Try to get them from LDAP.
// If user does not exist and authenticates successfully with LDAP we
// will create it on the fly and sign in with default permissions
if (!$user) {
LOG::debug("Local user ".Input::get('username')." does not exist");
LOG::debug("Creating local user ".Input::get('username'));
if ($user = Ldap::createUserFromLdap($ldap_user)) { //this handles passwords on its own
LOG::debug("Local user created.");
} else {
LOG::debug("Could not create local user.");
throw new \Exception("Could not create local user");
}
// If the user exists and they were imported from LDAP already
} else {
LOG::debug("Local user ".$request->input('username')." exists in database. Updating existing user against LDAP.");
$ldap_attr = Ldap::parseAndMapLdapAttributes($ldap_user);
if (Setting::getSettings()->ldap_pw_sync=='1') {
$user->password = bcrypt($request->input('password'));
}
$user->email = $ldap_attr['email'];
$user->first_name = $ldap_attr['firstname'];
$user->last_name = $ldap_attr['lastname'];
$user->save();
} // End if(!user)
return $user;
}
/**
* Account sign in form processing.
*
@ -163,6 +149,7 @@ class LoginController extends Controller
if (Setting::getSettings()->ldap_enabled=='1') {
LOG::debug("LDAP is enabled.");
try {
LOG::debug("Attempting to log user in by LDAP authentication.");
$user = $this->loginViaLdap($request);
Auth::login($user, true);

View file

@ -943,7 +943,7 @@ class SettingsController extends Controller
$setting->custom_forgot_pass_url = $request->input('custom_forgot_pass_url');
if ($setting->save()) {
return redirect()->route('settings.index')
return redirect()->route('settings.ldap.index')
->with('success', trans('admin/settings/message.update.success'));
}

View file

@ -6,25 +6,44 @@ use App\Models\Ldap;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Artisan;
use App\Models\LdapAd;
class LDAPImportController extends Controller
{
/**
* An Ldap instance.
*
* @var LdapAd
*/
protected $ldap;
/**
* Return view for LDAP import
* __construct.
*
* @param LdapAd $ldap
*/
public function __construct(LdapAd $ldap)
{
$this->ldap = $ldap;
}
/**
* Return view for LDAP import.
*
* @author Aladin Alaily
* @since [v1.8]
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return \Illuminate\Contracts\View\View
*
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create()
{
$this->authorize('update', User::class);
try {
$ldapconn = Ldap::connectToLdap();
Ldap::bindAdminToLdap($ldapconn);
$this->ldap->testLdapAdUserConnection();
} catch (\Exception $e) {
return redirect()->route('users.index')->with('error', $e->getMessage());
}
@ -32,19 +51,21 @@ class LDAPImportController extends Controller
return view('users/ldap');
}
/**
* LDAP form processing.
*
* @author Aladin Alaily
* @since [v1.8]
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
// Call Artisan LDAP import command.
$location_id = $request->input('location_id');
Artisan::call('snipeit:ldap-sync', ['--location_id' => $location_id, '--json_summary' => true]);
Artisan::call('snipeit:ldapAd-sync', ['--location_id' => $location_id, '--json_summary' => true]);
// Collect and parse JSON summary.
$ldap_results_json = Artisan::output();
@ -54,8 +75,9 @@ class LDAPImportController extends Controller
if ($ldap_results['error']) {
return redirect()->back()->withInput()->with('error', $ldap_results['error_message']);
}
return redirect()->route('ldap/user')
->with('success', "LDAP Import successful.")
->with('success', 'LDAP Import successful.')
->with('summary', $ldap_results['summary']);
}
}

View file

@ -1,292 +0,0 @@
<?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;
$ldap_use_tls = Setting::getSettings()->ldap_tls;
// If we are ignoring the SSL cert we need to setup the environment variable
// before we create the connection
if ($ldap_server_cert_ignore=='1') {
putenv('LDAPTLS_REQCERT=never');
}
// If the user specifies where CA Certs are, make sure to use them
if (env("LDAPTLS_CACERT")) {
putenv("LDAPTLS_CACERT=".env("LDAPTLS_CACERT"));
}
$connection = @ldap_connect($ldap_host);
if (!$connection) {
throw new Exception('Could not connect to LDAP server at '.$ldap_host.'. Please check your LDAP server name and port number in your settings.');
}
// Needed for AD
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0);
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, $ldap_version);
ldap_set_option($connection, LDAP_OPT_NETWORK_TIMEOUT, 20);
if ($ldap_use_tls=='1') {
ldap_start_tls($connection);
}
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
*
*/
static function findAndBindUserLdap($username, $password)
{
$settings = Setting::getSettings();
$connection = Ldap::connectToLdap();
$ldap_username_field = $settings->ldap_username_field;
$baseDn = $settings->ldap_basedn;
$userDn = $ldap_username_field.'='.$username.','.$settings->ldap_basedn;
if ($settings->is_ad =='1') {
// Check if they are using the userprincipalname for the username field.
// If they are, we can skip building the UPN to authenticate against AD
if ($ldap_username_field=='userprincipalname') {
$userDn = $username;
} else {
// In case they haven't added an AD domain
$userDn = ($settings->ad_domain != '') ? $username.'@'.$settings->ad_domain : $username.'@'.$settings->email_domain;
}
}
\Log::debug('Attempting to login using distinguished name:'.$userDn);
$filterQuery = $settings->ldap_auth_filter_query . $username;
if (!$ldapbind = @ldap_bind($connection, $userDn, $password)) {
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;
}
if (!$user = ldap_get_attributes($connection, $entry)) {
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
*
*/
static function bindAdminToLdap($connection)
{
$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));
}
}
/**
* Parse and map LDAP attributes based on settings
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
*
* @param $ldapatttibutes
* @return array|bool
*/
static function parseAndMapLdapAttributes($ldapatttibutes)
{
//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] : "" ;
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);
// Create user from LDAP data
if (!empty($item["username"])) {
$user = new User;
$user->first_name = $item["firstname"];
$user->last_name = $item["lastname"];
$user->username = $item["username"];
$user->email = $item["email"];
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);
}
$user->activated = 1;
$user->ldap_import = 1;
$user->notes = 'Imported on first login from LDAP';
if ($user->save()) {
return $user;
} else {
LOG::debug('Could not create user.'.$user->getErrors());
throw new Exception("Could not create user: ".$user->getErrors());
}
}
return false;
}
/**
* Searches LDAP
*
* @author [A. Gianotto] [<snipe@snipe.net>]
* @since [v3.0]
* @param $ldapatttibutes
* @param $base_dn
* @return array|bool
*/
static function findLdapUsers($base_dn = null)
{
$ldapconn = Ldap::connectToLdap();
$ldap_bind = Ldap::bindAdminToLdap($ldapconn);
// Default to global base DN if nothing else is provided.
if (is_null($base_dn)) {
$base_dn = Setting::getSettings()->ldap_basedn;
}
$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 {
// Paginate (non-critical, if not supported by server)
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. ');
}
$search_results = ldap_search($ldapconn, $base_dn, '('.$filter.')');
if (!$search_results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_search').ldap_error($ldapconn));
}
// Get results from page
$results = ldap_get_entries($ldapconn, $search_results);
if (!$results) {
return redirect()->route('users.index')->with('error', trans('admin/users/message.error.ldap_could_not_get_entries').ldap_error($ldapconn));
}
// Add results to result set
$global_count += $results['count'];
$result_set = array_merge($result_set, $results);
@ldap_control_paged_result_response($ldapconn, $search_results, $cookie);
} while ($cookie !== null && $cookie != '');
// Clean up after search
$result_set['count'] = $global_count;
$results = $result_set;
ldap_control_paged_result($ldapconn, 0);
return $results;
}
}

431
app/Models/LdapAd.php Normal file
View file

@ -0,0 +1,431 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Exception;
use Adldap\Adldap;
use App\Traits\UserTrait;
use Adldap\Query\Paginator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Adldap\Models\User as AdldapUser;
use Adldap\Models\ModelNotFoundException;
/**
* LDAP queries.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
class LdapAd extends LdapAdConfiguration
{
use UserTrait;
/**
* @see https://wdmsb.wordpress.com/2014/12/03/descriptions-of-active-directory-useraccountcontrol-value/
*/
const AD_USER_ACCOUNT_CONTROL_FLAGS = ['512', '544', '66048', '66080', '262656', '262688', '328192', '328224'];
/**
* The LDAP results per page.
*/
const PAGE_SIZE = 500;
/**
* A base dn.
*
* @var string
*/
public $baseDn = null;
/**
* Adldap instance.
*
* @var \Adldap\Adldap
*/
protected $ldap;
/**
* __construct.
*/
public function __construct()
{
parent::__construct();
$this->ldap = new Adldap();
$this->ldap->addProvider($this->ldapConfig);
}
/**
* Create a user if they successfully login to the LDAP server.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param string $username
* @param string $password
*
* @return \App\Models\User
*
* @throws Exception
*/
public function ldapLogin(string $username, string $password): User
{
try {
$this->ldap->auth()->attempt($username, $password);
} catch (Exception $e) {
Log::error($e->getMessage());
throw new Exception('Unable to validate user credentials!');
}
// Should we sync the logged in user
if ($this->isLdapSync($record)) {
try {
Log::debug('Attempting to find user in LDAP directory');
$record = $this->ldap->search()->findBy($this->ldapSettings['ldap_username_field'], $username);
} catch (ModelNotFoundException $e) {
Log::error($e->getMessage());
throw new Exception('Unable to find user in LDAP directory!');
}
$this->syncUserLdapLogin($record, $password);
}
return User::where('username', $username)
->whereNull('deleted_at')->where('ldap_import', '=', 1)
->where('activated', '=', '1')->first();
}
/**
* Set the user information based on the LDAP settings.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param \Adldap\Models\User $user
* @param null|Collection $defaultLocation
* @param null|Collection $mappedLocations
*
* @return null|\App\Models\User
*/
public function processUser(AdldapUser $user, ?Collection $defaultLocation=null, ?Collection $mappedLocations=null): ?User
{
// Only sync active users
if ($this->isLdapSync($user)) {
$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);
return $this->setUserModel($snipeUser);
}
// We are not syncing user info
return null;
}
/**
* Set the User model information.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param array $userInfo The user info to save to the database
*
* @return \App\Models\User
*/
public function setUserModel(array $userInfo): User
{
// If the username exists, return the user object, otherwise create a new user object
$user = User::firstOrNew([
'username' => $userInfo['username'],
]);
$user->username = $user->username ?? trim($userInfo['username']);
$user->password = $user->password ?? $this->generateEncyrptedPassword();
$user->first_name = trim($userInfo['firstname']);
$user->last_name = trim($userInfo['lastname']);
$user->email = trim($userInfo['email']);
$user->employee_num = trim($userInfo['employee_number']);
$user->activated = $userInfo['activated'];
$user->location_id = $userInfo['location_id'];
$user->notes = 'Imported from LDAP';
$user->ldap_import = 1;
return $user;
}
/**
* Sync a user who has logged in by LDAP.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param \Adldap\Models\User $record
* @param string $password
*
* @throws Exception
*/
private function syncUserLdapLogin(AdldapUser $record, string $password): void
{
$user = $this->processUser($record);
if (is_null($user->last_login)) {
$user->notes = 'Imported on first login from LDAP2';
}
if ($this->ldapSettings['ldap_pw_sync']) {
Log::debug('Syncing users password with LDAP directory.');
$user->password = bcrypt($password);
}
if (!$user->save()) {
Log::debug('Could not save user. '.$user->getErrors());
throw new Exception('Could not save user: '.$user->getErrors());
}
}
/**
* Check to see if we should sync the user with the LDAP directory.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param \Adldap\Models\User $user
*
* @return bool
*/
private function isLdapSync(AdldapUser $user): bool
{
return (false === $this->ldapSettings['ldap_active_flag'])
|| ('true' == strtolower($user->{$this->ldapSettings['ldap_active_flag']}[0]));
}
/**
* Set the active status of the user.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param \Adldap\Models\User $user
*
* @return int
*/
private function getActiveStatus(AdldapUser $user): int
{
$activeStatus = 0;
/*
* Check to see if we are connected to an AD server
* if so, check the Active Directory User Account Control Flags
*/
if ($this->ldapSettings['is_ad']) {
$activeStatus = (in_array($user->getUserAccountControl(), self::AD_USER_ACCOUNT_CONTROL_FLAGS)) ? 1 : 0;
} else {
// If there is no activated flag, assume this is handled via the OU and activate the users
if (false === $this->ldapSettings['ldap_active_flag']) {
$activeStatus = 1;
}
}
return $activeStatus;
}
/**
* Get a default selected location, or a OU mapped location if available.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param Adldap\Models\User $user
* @param Collection|null $defaultLocation
* @param Collection|null $mappedLocations
*
* @return null|int
*/
private function getLocationId(AdldapUser $user, ?Collection $defaultLocation, ?Collection $mappedLocations): ?int
{
$locationId = null;
// Set the users default locations, if set
if ($defaultLocation) {
$locationId = $defaultLocation->keys()->first();
}
// Check to see if the user is in a mapped location
if ($mappedLocations) {
$location = $mappedLocations->filter(function ($value, $key) use ($user) {
if ($user->inGroup([$value], true)) {
return $key;
}
});
if ($location->count() > 0) {
$locationId = $location->keys()->first();
}
}
return $locationId;
}
/**
* Get the base dn for the query.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
private function getBaseDn(): string
{
if (!is_null($this->baseDn)) {
return $this->baseDn;
}
return $this->ldapSettings['ldap_basedn'];
}
/**
* Format the ldap filter if needed.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return null|string
*/
private function getFilter(): ?string
{
$filter = $this->ldapSettings['ldap_filter'];
if ('' === $filter) {
return null;
}
// Add surrounding parentheses as needed
$paren = mb_substr($filter, 0, 1, 'utf-8');
if ('(' !== $paren) {
return '('.$filter.')';
}
return $filter;
}
/**
* Get the selected fields to return
* This should help with memory on large result sets as we are not returning all fields.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return array
*/
private function getSelectedFields(): array
{
return [
$this->ldapSettings['ldap_username_field'],
$this->ldapSettings['ldap_fname_field'],
$this->ldapSettings['ldap_lname_field'],
$this->ldapSettings['ldap_email'],
$this->ldapSettings['ldap_emp_num'],
'memberOf',
'useraccountcontrol',
];
}
/**
* Test the bind user connection.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
public function testLdapAdBindConnection(): void
{
try {
$this->ldap->search()->ous()->get()->count();
} catch (Exception $th) {
Log::error($th->getMessage());
throw new Exception('Unable to search LDAP directory!');
}
}
/**
* Test the user can connect to the LDAP server.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
public function testLdapAdUserConnection(): void
{
try {
$this->ldap->connect();
} catch (\Adldap\Auth\BindException $e) {
Log::error($e);
throw new Exception('Unable to connect to LDAP directory!');
}
}
/**
* Test the LDAP configuration by returning up to 10 users.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return Collection
*/
public function testUserImportSync(): Collection
{
$testUsers = collect($this->getLdapUsers()->getResults())->chunk(10)->first();
if ($testUsers) {
return $testUsers->map(function ($item) {
return (object) [
'username' => $item->{$this->ldapSettings['ldap_username_field']}[0] ?? null,
'employee_number' => $item->{$this->ldapSettings['ldap_emp_num']}[0] ?? null,
'lastname' => $item->{$this->ldapSettings['ldap_lname_field']}[0] ?? null,
'firstname' => $item->{$this->ldapSettings['ldap_fname_field']}[0] ?? null,
'email' => $item->{$this->ldapSettings['ldap_email']}[0] ?? null,
];
});
}
return collect();
}
/**
* Query the LDAP server to get the users to process and return a page set.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @param int $page The paged results to get
*
* @return \Adldap\Query\Paginator
*/
public function getLdapUsers(int $page=0): Paginator
{
$search = $this->ldap->search()->users()->in($this->getBaseDn());
$filter = $this->getFilter();
if (!is_null($filter)) {
$search = $search->rawFilter($filter);
}
return $search->select($this->getSelectedFields())
->paginate(self::PAGE_SIZE, $page);
}
}

View file

@ -0,0 +1,245 @@
<?php
declare(strict_types=1);
namespace App\Models;
use Exception;
use Illuminate\Support\Collection;
/**
* LDAP configuration merge for Adldap2.
*
* @see https://github.com/Adldap2/Adldap2
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
class LdapAdConfiguration
{
const LDAP_PORT = 389;
const CONNECTION_TIMEOUT = 5;
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'];
/**
* Ldap Settings.
*
* @var Collection
*/
public $ldapSettings;
/**
* LDAP Config.
*
* @var array
*/
public $ldapConfig;
/**
* __construct.
*/
public function __construct()
{
$this->ldapSettings = $this->getSnipeItLdapSettings();
$this->setSnipeItConfig();
}
/**
* Merge the default Adlap config with the SnipeIT config.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function setSnipeItConfig()
{
$this->ldapConfig = $this->setLdapConnectionConfiguration();
$this->certificateCheck();
}
/**
* Get the LDAP settings from the Settings model.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return \Illuminate\Support\Collection
*/
private function getSnipeItLdapSettings(): Collection
{
$ldapSettings = Setting::getLdapSettings()
->map(function ($item, $key) {
// Trim the items
if (is_string($item)) {
$item = trim($item);
}
// Get the boolean value of the LDAP setting, makes it easier to work with them
if (in_array($key, self::LDAP_BOOLEAN_SETTINGS)) {
return boolval($item);
}
// Decrypt the admin password
if ('ldap_pword' === $key) {
try {
return decrypt($item);
} 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.');
}
}
return $item;
});
return $ldapSettings;
}
/**
* Set the server certificate environment variable.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*/
private function certificateCheck(): void
{
// If we are ignoring the SSL cert we need to setup the environment variable
// before we create the connection
if ($this->ldapSettings['ldap_server_cert_ignore']) {
putenv('LDAPTLS_REQCERT=never');
}
// If the user specifies where CA Certs are, make sure to use them
if (env('LDAPTLS_CACERT')) {
putenv('LDAPTLS_CACERT='.env('LDAPTLS_CACERT'));
}
}
/**
* Set the Adlap2 connection configuration values based on SnipeIT settings.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return array
*/
private function setLdapConnectionConfiguration(): array
{
// Create the configuration array.
return [
// Mandatory Configuration Options
'hosts' => $this->getServerUrlBase(),
'base_dn' => $this->ldapSettings['ldap_basedn'],
'username' => $this->ldapSettings['ldap_uname'],
'password' => $this->ldapSettings['ldap_pword'],
// Optional Configuration Options
'schema' => $this->getSchema(),
'account_prefix' => '',
'account_suffix' => '',
'port' => $this->getPort(),
'follow_referrals' => false,
'use_ssl' => $this->isSsl(),
'use_tls' => $this->ldapSettings['ldap_tls'],
'version' => $this->ldapSettings['ldap_version'] ?? self::DEFAULT_LDAP_VERSION,
'timeout' => self::CONNECTION_TIMEOUT,
// Custom LDAP Options
'custom_options' => [
// See: http://php.net/ldap_set_option
// LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD,
],
];
}
/**
* Get the schema to use for the connection.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return string
*/
private function getSchema(): string
{
$schema = \Adldap\Schemas\OpenLDAP::class;
if ($this->ldapSettings['is_ad']) {
$schema = \Adldap\Schemas\ActiveDirectory::class;
}
return $schema;
}
/**
* Get the port number from the connection url.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return int
*/
private function getPort(): int
{
$ldapUrl = $this->ldapSettings['ldap_server'];
if ($ldapUrl) {
$port = parse_url($ldapUrl, PHP_URL_PORT);
if (is_int($port)) {
return $port;
}
}
return self::LDAP_PORT;
}
/**
* Get ldap scheme from url to determin ssl use.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return bool
*/
private function isSsl(): bool
{
if ($this->ldapSettings['ldap_server']) {
$scheme = explode('://', $this->ldapSettings['ldap_server']);
if ('ldap' === strtolower($scheme[0])) {
return false;
}
return true;
}
return false;
}
/**
* Return the base url to the LDAP server.
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return array
*/
private function getServerUrlBase(): array
{
if ($this->ldapSettings['is_ad']) {
return collect(explode(',', $this->ldapSettings['ad_domain']))->map(function ($item) {
return trim($item);
})->toArray();
}
$parts = explode('//', $this->ldapSettings['ldap_server']);
return [
$parts[1],
];
}
}

View file

@ -9,6 +9,9 @@ use Illuminate\Support\Facades\Cache;
use Watson\Validating\ValidatingTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Watson\Validating\ValidatingTrait;
use Schema;
use Illuminate\Support\Collection;
/**
* Settings model.
@ -314,4 +317,43 @@ class Setting extends Model
return 'required|min:'.$settings->pwd_secure_min.$security_rules;
}
/**
* Get the specific LDAP settings
*
* @author Wes Hulette <jwhulette@gmail.com>
*
* @since 5.0.0
*
* @return Collection
*/
public static function getLdapSettings(): Collection
{
$ldapSettings = self::select([
'ldap_enabled',
'ldap_server',
'ldap_uname',
'ldap_pword',
'ldap_basedn',
'ldap_filter',
'ldap_username_field',
'ldap_lname_field',
'ldap_fname_field',
'ldap_auth_filter_query',
'ldap_version',
'ldap_active_flag',
'ldap_emp_num',
'ldap_email',
'ldap_server_cert_ignore',
'ldap_port',
'ldap_tls',
'ldap_pw_sync',
'is_ad',
'ad_domain'
])->first()->getAttributes();
return collect($ldapSettings);
}
}

36
app/Traits/UserTrait.php Normal file
View file

@ -0,0 +1,36 @@
<?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);
}
}

View file

@ -6,6 +6,7 @@
"type": "project",
"require": {
"php": ">=7.1.3",
"adldap2/adldap2": "^9.1",
"bacon/bacon-qr-code": "^1.0",
"doctrine/cache": "^1.6",
"doctrine/common": "^2.7",

118
composer.lock generated
View file

@ -1,11 +1,64 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "f80ee6df7f370344f1ff4e30bc08284d",
"content-hash": "fac7e8b237f1d78fb4057766c2c47da5",
"packages": [
{
"name": "adldap2/adldap2",
"version": "v9.1.3",
"source": {
"type": "git",
"url": "https://github.com/Adldap2/Adldap2.git",
"reference": "f9ba4003a1350b7cad952d3ad22b24c8d3e0d1af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Adldap2/Adldap2/zipball/f9ba4003a1350b7cad952d3ad22b24c8d3e0d1af",
"reference": "f9ba4003a1350b7cad952d3ad22b24c8d3e0d1af",
"shasum": ""
},
"require": {
"ext-ldap": "*",
"illuminate/contracts": "~5.0",
"php": ">=7.0",
"tightenco/collect": "~5.0"
},
"require-dev": {
"mockery/mockery": "~1.0",
"phpunit/phpunit": "~6.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Adldap\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Steve Bauman",
"email": "steven_bauman@outlook.com",
"role": "Developer"
}
],
"description": "A PHP LDAP Package for humans.",
"keywords": [
"active directory",
"ad",
"adLDAP",
"adldap2",
"directory",
"ldap",
"windows"
],
"time": "2018-10-08T22:25:07+00:00"
},
{
"name": "aws/aws-sdk-php",
"version": "3.69.0",
@ -5980,6 +6033,56 @@
],
"time": "2018-06-23T09:21:30+00:00"
},
{
"name": "tightenco/collect",
"version": "v5.7.9",
"source": {
"type": "git",
"url": "https://github.com/tightenco/collect.git",
"reference": "67b8d4a20ce42b32b5f50a141bb0b1ec45aedb53"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tightenco/collect/zipball/67b8d4a20ce42b32b5f50a141bb0b1ec45aedb53",
"reference": "67b8d4a20ce42b32b5f50a141bb0b1ec45aedb53",
"shasum": ""
},
"require": {
"php": "^7.1.3",
"symfony/var-dumper": ">=3.4 <5"
},
"require-dev": {
"mockery/mockery": "^1.0",
"nesbot/carbon": "^1.26.3",
"phpunit/phpunit": "^7.0"
},
"type": "library",
"autoload": {
"files": [
"src/Collect/Support/helpers.php",
"src/Collect/Support/alias.php"
],
"psr-4": {
"Tightenco\\Collect\\": "src/Collect"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylorotwell@gmail.com"
}
],
"description": "Collect - Illuminate Collections as a separate package.",
"keywords": [
"collection",
"laravel"
],
"time": "2018-10-09T17:51:58+00:00"
},
{
"name": "tightenco/ziggy",
"version": "v0.6.8.1",
@ -7416,17 +7519,6 @@
{
"name": "roave/security-advisories",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "67643fa62d521fb76855b0a4cc9a3de7ba38f85b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/67643fa62d521fb76855b0a4cc9a3de7ba38f85b",
"reference": "67643fa62d521fb76855b0a4cc9a3de7ba38f85b",
"shasum": ""
},
"conflict": {
"3f/pygmentize": "<1.2",
"adodb/adodb-php": "<5.20.12",

Binary file not shown.

View file

@ -1,5 +1,5 @@
{
"/js/app.js": "/js/app.js?id=682fc1cd790bc8dc8e58",
"/js/app.js": "/js/app.js?id=d7280b4d128284323dea",
"/css/build/AdminLTE.css": "/css/build/AdminLTE.css?id=f7a5d783fef321018f4c",
"/css/build/app.css": "/css/build/app.css?id=0dfc05b0fe1dcc9b6e3d",
"/css/all.css": "/css/all.css?id=9399418f7ce5805e3571",

View file

@ -464,3 +464,28 @@ $(document).ready(function () {
});
});
/**
* Toggle disabled
*/
(function($){
$.fn.toggleDisabled = function(callback){
return this.each(function(){
var disabled, $this = $(this);
if($this.attr('disabled')){
$this.removeAttr('disabled');
disabled = false;
} else {
$this.attr('disabled', 'disabled');
disabled = true;
}
if(callback && typeof callback === 'function'){
callback(this, disabled);
}
});
};
})(jQuery);

View file

@ -34,7 +34,7 @@
@else
{{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'false', 'class' => 'form-horizontal', 'role' => 'form' ]) }}
{{ Form::open(['method' => 'POST', 'files' => false, 'autocomplete' => 'false', 'class' => 'form-horizontal', 'role' => 'form']) }}
<!-- CSRF Token -->
{{csrf_field()}}
@ -63,64 +63,44 @@
{{ Form::label('ldap_integration', trans('admin/settings/general.ldap_integration')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::checkbox('ldap_enabled', '1', Input::old('ldap_enabled', $setting->ldap_enabled),['class' => 'minimal disabled', 'disabled' => 'disabled']) }}
@else
{{ Form::checkbox('ldap_enabled', '1', Input::old('ldap_enabled', $setting->ldap_enabled),array('class' => 'minimal')) }}
@endcan
{{ Form::checkbox('ldap_enabled', '1', Input::old('ldap_enabled', $setting->ldap_enabled), ['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
{{ trans('admin/settings/general.ldap_enabled') }}
{!! $errors->first('ldap_enabled', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
<!-- AD Flag -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('is_ad', trans('admin/settings/general.ad')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::checkbox('is_ad', '1', Input::old('is_ad', $setting->is_ad),['class' => 'minimal disabled', 'disabled' => 'disabled']) }}
@else
{{ Form::checkbox('is_ad', '1', Input::old('is_ad', $setting->is_ad),array('class' => 'minimal')) }}
@endif
{{ trans('admin/settings/general.is_ad') }}
{!! $errors->first('is_ad', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
<!-- LDAP Password Sync -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('ldap_pw_sync', trans('admin/settings/general.ldap_pw_sync')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::checkbox('ldap_pw_sync', '1', Input::old('ldap_pw_sync', $setting->ldap_pw_sync),['class' => 'minimal disabled', 'disabled' => 'disabled']) }}
@else
{{ Form::checkbox('ldap_pw_sync', '1', Input::old('ldap_pw_sync', $setting->ldap_pw_sync),array('class' => 'minimal')) }}
@endif
{{ Form::checkbox('ldap_pw_sync', '1', Input::old('ldap_pw_sync', $setting->ldap_pw_sync),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
{{ trans('general.yes') }}
<p class="help-block">{{ trans('admin/settings/general.ldap_pw_sync_help') }}</p>
{!! $errors->first('ldap_pw_sync', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
<!-- AD Flag -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('is_ad', trans('admin/settings/general.ad')) }}
</div>
<div class="col-md-9">
{{ Form::checkbox('is_ad', '1', Input::old('is_ad', $setting->is_ad),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
{{ trans('admin/settings/general.is_ad') }}
{!! $errors->first('is_ad', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
<!-- AD Domain -->
<div class="form-group {{ $errors->has('ad_domain') ? 'error' : '' }}">
<div class="col-md-3">
{{ Form::label('ad_domain', trans('admin/settings/general.ad_domain')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ad_domain', Input::old('ad_domain', $setting->ad_domain), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'example.com')) }}
@else
{{ Form::text('ad_domain', Input::old('ad_domain', $setting->ad_domain), array('class' => 'form-control','placeholder' => 'example.com')) }}
@endif
{{ Form::text('ad_domain', Input::old('ad_domain', $setting->ad_domain), ['class' => 'form-control','placeholder' => 'example.com', $setting->demoMode]) }}
<p class="help-block">{{ trans('admin/settings/general.ad_domain_help') }}</p>
{!! $errors->first('ad_domain', '<span class="alert-msg">:message</span>') !!}
</div>
@ -132,11 +112,7 @@
{{ Form::label('ldap_server', trans('admin/settings/general.ldap_server')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_server', Input::old('ldap_server', $setting->ldap_server), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'ldap://ldap.example.com')) }}
@else
{{ Form::text('ldap_server', Input::old('ldap_server', $setting->ldap_server), array('class' => 'form-control','placeholder' => 'ldap://ldap.example.com')) }}
@endif
{{ Form::text('ldap_server', Input::old('ldap_server', $setting->ldap_server), ['class' => 'form-control','placeholder' => 'ldap://ldap.example.com', $setting->demoMode]) }}
<p class="help-block">{{ trans('admin/settings/general.ldap_server_help') }}</p>
{!! $errors->first('ldap_server', '<span class="alert-msg">:message</span>') !!}
</div>
@ -148,13 +124,7 @@
{{ Form::label('ldap_tls', trans('admin/settings/general.ldap_tls')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::checkbox('ldap_tls', '1', Input::old('ldap_tls', $setting->ldap_tls),['class' => 'minimal disabled', 'disabled' => 'disabled']) }}
@else
{{ Form::checkbox('ldap_tls', '1', Input::old('ldap_tls', $setting->ldap_tls),array('class' => 'minimal')) }}
@endif
{{ Form::checkbox('ldap_tls', '1', Input::old('ldap_tls', $setting->ldap_tls),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
{{ trans('admin/settings/general.ldap_tls_help') }}
{!! $errors->first('ldap_tls', '<span class="alert-msg">:message</span>') !!}
</div>
@ -166,13 +136,7 @@
{{ Form::label('ldap_server_cert_ignore', trans('admin/settings/general.ldap_server_cert')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::checkbox('ldap_server_cert_ignore', '1', Input::old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore),['class' => 'minimal disabled', 'disabled' => 'disabled']) }}
@else
{{ Form::checkbox('ldap_server_cert_ignore', '1', Input::old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore),array('class' => 'minimal')) }}
@endif
{{ Form::checkbox('ldap_server_cert_ignore', '1', Input::old('ldap_server_cert_ignore', $setting->ldap_server_cert_ignore),['class' => 'minimal '. $setting->demoMode, $setting->demoMode]) }}
{{ trans('admin/settings/general.ldap_server_cert_ignore') }}
{!! $errors->first('ldap_server_cert_ignore', '<span class="alert-msg">:message</span>') !!}
<p class="help-block">{{ trans('admin/settings/general.ldap_server_cert_help') }}</p>
@ -185,11 +149,7 @@
{{ Form::label('ldap_uname', trans('admin/settings/general.ldap_uname')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_uname', Input::old('ldap_uname', $setting->ldap_uname), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'binduser@example.com')) }}
@else
{{ Form::text('ldap_uname', Input::old('ldap_uname', $setting->ldap_uname), array('class' => 'form-control','placeholder' => 'binduser@example.com')) }}
@endif
{{ Form::text('ldap_uname', Input::old('ldap_uname', $setting->ldap_uname), ['class' => 'form-control','placeholder' => 'binduser@example.com', $setting->demoMode]) }}
{!! $errors->first('ldap_uname', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -200,11 +160,7 @@
{{ Form::label('ldap_pword', trans('admin/settings/general.ldap_pword')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords'))
{{ Form::password('ldap_pword', array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'binduserpassword')) }}
@else
{{ Form::password('ldap_pword', array('class' => 'form-control','placeholder' => 'binduserpassword')) }}
@endif
{{ Form::password('ldap_pword', ['class' => 'form-control','placeholder' => 'binduserpassword', $setting->demoMode]) }}
{!! $errors->first('ldap_pword', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -215,11 +171,7 @@
{{ Form::label('ldap_basedn', trans('admin/settings/general.ldap_basedn')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_basedn', Input::old('ldap_basedn', $setting->ldap_basedn), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'cn=users/authorized,dc=example,dc=com')) }}
@else
{{ Form::text('ldap_basedn', Input::old('ldap_basedn', $setting->ldap_basedn), array('class' => 'form-control','placeholder' => 'cn=users/authorized,dc=example,dc=com')) }}
@endif
{{ Form::text('ldap_basedn', Input::old('ldap_basedn', $setting->ldap_basedn), ['class' => 'form-control', 'placeholder' => 'cn=users/authorized,dc=example,dc=com', $setting->demoMode]) }}
{!! $errors->first('ldap_basedn', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -230,11 +182,7 @@
{{ Form::label('ldap_filter', trans('admin/settings/general.ldap_filter')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_filter', Input::old('ldap_filter', $setting->ldap_filter), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => '&(cn=*)')) }}
@else
{{ Form::text('ldap_filter', Input::old('ldap_filter', $setting->ldap_filter), array('class' => 'form-control','placeholder' => '&(cn=*)')) }}
@endif
{{ Form::text('ldap_filter', Input::old('ldap_filter', $setting->ldap_filter), ['class' => 'form-control','placeholder' => '&(cn=*)', $setting->demoMode]) }}
{!! $errors->first('ldap_filter', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -245,11 +193,7 @@
{{ Form::label('ldap_username_field', trans('admin/settings/general.ldap_username_field')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_username_field', Input::old('ldap_username_field', $setting->ldap_username_field), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'samaccountname')) }}
@else
{{ Form::text('ldap_username_field', Input::old('ldap_username_field', $setting->ldap_username_field), array('class' => 'form-control','placeholder' => 'samaccountname')) }}
@endif
{{ Form::text('ldap_username_field', Input::old('ldap_username_field', $setting->ldap_username_field), ['class' => 'form-control','placeholder' => 'samaccountname', $setting->demoMode]) }}
{!! $errors->first('ldap_username_field', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -260,11 +204,7 @@
{{ Form::label('ldap_lname_field', trans('admin/settings/general.ldap_lname_field')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_lname_field', Input::old('ldap_lname_field', $setting->ldap_lname_field), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'sn')) }}
@else
{{ Form::text('ldap_lname_field', Input::old('ldap_lname_field', $setting->ldap_lname_field), array('class' => 'form-control','placeholder' => 'sn')) }}
@endif
{{ Form::text('ldap_lname_field', Input::old('ldap_lname_field', $setting->ldap_lname_field), ['class' => 'form-control','placeholder' => 'sn', $setting->demoMode]) }}
{!! $errors->first('ldap_lname_field', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -275,11 +215,7 @@
{{ Form::label('ldap_fname_field', trans('admin/settings/general.ldap_fname_field')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_fname_field', Input::old('ldap_fname_field', $setting->ldap_fname_field), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'givenname')) }}
@else
{{ Form::text('ldap_fname_field', Input::old('ldap_fname_field', $setting->ldap_fname_field), array('class' => 'form-control','placeholder' => 'givenname')) }}
@endif
{{ Form::text('ldap_fname_field', Input::old('ldap_fname_field', $setting->ldap_fname_field), ['class' => 'form-control', 'placeholder' => 'givenname', $setting->demoMode]) }}
{!! $errors->first('ldap_fname_field', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -290,11 +226,7 @@
{{ Form::label('ldap_auth_filter_query', trans('admin/settings/general.ldap_auth_filter_query')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_auth_filter_query', Input::old('ldap_auth_filter_query', $setting->ldap_auth_filter_query), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => '"uid="')) }}
@else
{{ Form::text('ldap_auth_filter_query', Input::old('ldap_auth_filter_query', $setting->ldap_auth_filter_query), array('class' => 'form-control','placeholder' => '"uid="')) }}
@endif
{{ Form::text('ldap_auth_filter_query', Input::old('ldap_auth_filter_query', $setting->ldap_auth_filter_query), ['class' => 'form-control','placeholder' => '"uid="', $setting->demoMode]) }}
{!! $errors->first('ldap_auth_filter_query', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -305,11 +237,7 @@
{{ Form::label('ldap_version', trans('admin/settings/general.ldap_version')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_version', Input::old('ldap_version', $setting->ldap_version), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => '3')) }}
@else
{{ Form::text('ldap_version', Input::old('ldap_version', $setting->ldap_version), array('class' => 'form-control','placeholder' => '3')) }}
@endif
{{ Form::text('ldap_version', Input::old('ldap_version', $setting->ldap_version), ['class' => 'form-control','placeholder' => '3', $setting->demoMode]) }}
{!! $errors->first('ldap_version', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -320,11 +248,7 @@
{{ Form::label('ldap_active_flag', trans('admin/settings/general.ldap_active_flag')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_active_flag', Input::old('ldap_active_flag', $setting->ldap_active_flag), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => '')) }}
@else
{{ Form::text('ldap_active_flag', Input::old('ldap_active_flag', $setting->ldap_active_flag), array('class' => 'form-control','placeholder' => '')) }}
@endif
{{ Form::text('ldap_active_flag', Input::old('ldap_active_flag', $setting->ldap_active_flag), ['class' => 'form-control','placeholder' => '', $setting->demoMode]) }}
{!! $errors->first('ldap_active_flag', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -335,11 +259,7 @@
{{ Form::label('ldap_emp_num', trans('admin/settings/general.ldap_emp_num')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_emp_num', Input::old('ldap_emp_num', $setting->ldap_emp_num), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => '')) }}
@else
{{ Form::text('ldap_emp_num', Input::old('ldap_emp_num', $setting->ldap_emp_num), array('class' => 'form-control','placeholder' => '')) }}
@endif
{{ Form::text('ldap_emp_num', Input::old('ldap_emp_num', $setting->ldap_emp_num), ['class' => 'form-control','placeholder' => '', $setting->demoMode]) }}
{!! $errors->first('ldap_emp_num', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@ -350,65 +270,32 @@
{{ Form::label('ldap_email', trans('admin/settings/general.ldap_email')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('ldap_email', Input::old('ldap_email', $setting->ldap_email), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => '')) }}
@else
{{ Form::text('ldap_email', Input::old('ldap_email', $setting->ldap_email), array('class' => 'form-control','placeholder' => '')) }}
@endif
{{ Form::text('ldap_email', Input::old('ldap_email', $setting->ldap_email), ['class' => 'form-control','placeholder' => '', $setting->demoMode]) }}
{!! $errors->first('ldap_email', '<span class="alert-msg">:message</span>') !!}
</div>
</div>
@if ($setting->ldap_enabled)
<!-- LDAP test -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}
</div>
<div class="col-md-9" id="ldaptestrow">
<a class="btn btn-default btn-sm pull-left" id="ldaptest" style="margin-right: 10px;">Test LDAP</a>
</div>
<div class="col-md-9 col-md-offset-3">
<span id="ldaptesticon"></span>
<span id="ldaptestresult"></span>
<span id="ldapteststatus"></span>
</div>
<div class="col-md-9 col-md-offset-3">
<p class="help-block">{{ trans('admin/settings/general.ldap_login_sync_help') }}</p>
</div>
</div>
<!-- LDAP Login test -->
<div class="form-group">
<div class="col-md-3">
{{ Form::label('test_ldap_login', 'Test LDAP Login') }}
</div>
<div class="col-md-9">
<div class="row">
<div class="col-md-4">
<input type="text" name="ldaptest_user" id="ldaptest_user" class="form-control" placeholder="LDAP username">
</div>
<div class="col-md-4">
<input type="password" name="ldaptest_password" id="ldaptest_password" class="form-control" placeholder="LDAP password">
</div>
<!-- LDAP test -->
<div class="form-group">
<div class="col-md-3">
<a class="btn btn-default btn-sm" id="ldaptestlogin" style="margin-right: 10px;">Test LDAP</a>
{{ Form::label('test_ldap_sync', 'Test LDAP Sync') }}
</div>
</div>
</div>
<div class="col-md-9 col-md-offset-3">
<span id="ldaptestloginicon"></span>
<span id="ldaptestloginresult"></span>
<span id="ldaptestloginstatus"></span>
</div>
<div class="col-md-9 col-md-offset-3">
<p class="help-block">{{ trans('admin/settings/general.ldap_login_test_help') }}</p>
<div class="col-md-9" id="ldaptestrow">
<a {{ $setting->demoMode }} class="btn btn-default btn-sm pull-left" id="ldaptest" style="margin-right: 10px;">Test LDAP Syncronization</a>
</div>
<div class="col-md-9 col-md-offset-3">
<br />
<div id="ldapad_test_results" class="hidden well well-sm"></div>
</div>
<div class="col-md-9 col-md-offset-3">
<p class="help-block">{{ trans('admin/settings/general.ldap_login_sync_help') }}</p>
</div>
</div>
</div>
@endif
<!-- LDAP Forgotten password -->
@ -417,11 +304,7 @@
{{ Form::label('custom_forgot_pass_url', trans('admin/settings/general.custom_forgot_pass_url')) }}
</div>
<div class="col-md-9">
@if (config('app.lock_passwords')===true)
{{ Form::text('custom_forgot_pass_url', Input::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), array('class' => 'form-control', 'disabled'=>'disabled','placeholder' => 'https://my.ldapserver-forgotpass.com')) }}
@else
{{ Form::text('custom_forgot_pass_url', Input::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), array('class' => 'form-control','placeholder' => 'https://my.ldapserver-forgotpass.com')) }}
@endif
{{ Form::text('custom_forgot_pass_url', Input::old('custom_forgot_pass_url', $setting->custom_forgot_pass_url), ['class' => 'form-control','placeholder' => 'https://my.ldapserver-forgotpass.com', $setting->demoMode]) }}
<p class="help-block">{{ trans('admin/settings/general.custom_forgot_pass_url_help') }}</p>
{!! $errors->first('custom_forgot_pass_url', '<span class="alert-msg">:message</span>') !!}
</div>
@ -435,15 +318,12 @@
<a class="btn btn-link text-left" href="{{ route('settings.index') }}">{{ trans('button.cancel') }}</a>
</div>
<div class="text-right col-md-6">
<button type="submit" class="btn btn-success"><i class="fa fa-check icon-white"></i> {{ trans('general.save') }}</button>
<button {{ $setting->demoMode }} type="submit" class="btn btn-success"><i class="fa fa-check icon-white"></i> {{ trans('general.save') }}</button>
</div>
</div>
</div> <!-- /box -->
</div> <!-- /.col-md-8-->
</div> <!-- /.row-->
@ -452,143 +332,111 @@
@stop
@section('moar_scripts')
<script nonce="{{ csrf_token() }}">
$("#ldaptest").click(function(){
$("#ldaptestrow").removeClass('text-success');
$("#ldaptestrow").removeClass('text-danger');
$("#ldapteststatus").html('');
$("#ldaptesticon").html('<i class="fa fa-spinner spin"></i> Testing LDAP Binding...');
$.ajax({
url: '{{ route('api.settings.ldaptest') }}',
type: 'GET',
headers: {
"X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
},
data: {},
dataType: 'json',
@push('js')
<script nonce="{{ csrf_token() }}">
success: function (data) {
$("#ldaptesticon").html('');
$("#ldapteststatus").addClass('text-success');
$("#ldapteststatus").html('<i class="fa fa-check text-success"></i> Connection to LDAP server established!');
},
/**
* Check to see if is_ad is checked, if not disable the ad_domain field
*/
$(function() {
if( $('#is_ad').prop('checked') === false) {
$('#ad_domain').prop('disabled', 'disabled');
} else {
$('#ldap_server').prop('disabled', 'disabled');
}
});
error: function (data) {
/**
* Toggle the server info based on the is_ad checkbox
*/
$('#is_ad').on('ifClicked', function(){
$('#ad_domain').toggleDisabled();
$('#ldap_server').toggleDisabled();
});
if (data.responseJSON) {
var bind_errors = data.responseJSON.message;
} else {
var bind_errors;
/**
* Test the LDAP connection settings
*/
$("#ldaptest").click(function () {
$("#ldapad_test_results").removeClass('hidden text-success text-danger');
$("#ldapad_test_results").html('');
$("#ldapad_test_results").html('<i class="fa fa-spinner spin"></i> Testing LDAP Connection, Binding & Query ...');
$.ajax({
url: '{{ route('api.settings.ldaptest') }}',
type: 'GET',
headers: {
"X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
},
data: {},
dataType: 'json',
success: function (data) {
$("#ldapad_test_results").html('');
let html = buildLdapTestResults(data)
$("#ldapad_test_results").html(
html
);
},
error: function (data) {
$("#ldapad_test_results").html('');
$("#ldapad_test_results").addClass('text-danger');
let errorIcon = '<i class="fa fa-exclamation-triangle text-danger"></i>' + ' ';
if (data.status == 500) {
$('#ldapad_test_results').html(errorIcon + '500 Server Error');
} else if (data.status == 400) {
let errorMessage = '';
if( typeof data.responseJSON.user_sync !== 'undefined') {
errorMessage = data.responseJSON.user_sync.message;
}
var bind_error_text;
$("#ldaptesticon").html('');
$("#ldapteststatus").addClass('text-danger');
$("#ldaptesticon").html('<i class="fa fa-exclamation-triangle text-danger"></i>');
if (data.status == 500) {
$('#ldapteststatus').html('500 Server Error');
} else if (data.status == 400) {
if (typeof bind_errors !='string') {
for (i = 0; i < bind_errors.length; i++) {
if (bind_errors[i]) {
bind_error_text += '<li>Error: ' + bind_errors[i];
}
}
} else {
bind_error_text = bind_errors;
}
$('#ldapteststatus').html(bind_error_text);
} else {
$('#ldapteststatus').html(data.responseText.message);
if( typeof data.responseJSON.message !== 'undefined') {
errorMessage = data.responseJSON.message;
}
$('#ldapad_test_results').html(errorIcon + errorMessage);
} else {
$('#ldapad_test_results').html(errorIcon + data.responseText.message);
}
});
}
});
});
$("#ldaptestlogin").click(function(){
$("#ldaptestloginrow").removeClass('text-success');
$("#ldaptestloginrow").removeClass('text-danger');
$("#ldaptestloginstatus").removeClass('text-danger');
$("#ldaptestloginstatus").html('');
$("#ldaptestloginicon").html('<i class="fa fa-spinner spin"></i> Testing LDAP Authentication...');
$.ajax({
url: '{{ route('api.settings.ldaptestlogin') }}',
type: 'POST',
headers: {
"X-Requested-With": 'XMLHttpRequest',
"X-CSRF-TOKEN": $('meta[name="csrf-token"]').attr('content')
},
data: {
'ldaptest_user': $('#ldaptest_user').val(),
'ldaptest_password': $('#ldaptest_password').val()
},
/**
* Build the results html table
*/
function buildLdapTestResults(results) {
let html = '<ul style="list-style: none;padding-left: 5px;">'
html += '<li class="text-success"><i class="fa fa-check" aria-hidden="true"></i> ' + results.login.message + ' </li>'
html += '<li class="text-success"><i class="fa fa-check" aria-hidden="true"></i> ' + results.bind.message + ' </li>'
html += '</ul>'
html += '<div>A sample of 10 users returned from the LDAP server based on your settings:</div>'
html += '<table class="table table-bordered table-condensed" style="background-color: #fff">'
html += buildLdapResultsTableHeader()
html += buildLdapResultsTableBody(results.user_sync.users)
html += '<table>'
return html;
}
dataType: 'json',
function buildLdapResultsTableHeader(user)
{
var keys = ['Employee Number', 'Username', 'First Name', 'Last Name','Email']
let header = '<thead><tr>'
for (var i in keys) {
header += '<th>' + keys[i] + '</th>'
}
header += "</tr></thead>"
return header;
}
success: function (data) {
$("#ldaptestloginicon").html('');
$("#ldaptestloginrow").addClass('text-success');
$("#ldaptestloginstatus").addClass('text-success');
$("#ldaptestloginstatus").html('<i class="fa fa-check text-success"></i> User authenticated against LDAP successfully!');
},
error: function (data) {
if (data.responseJSON) {
var errors = data.responseJSON.message;
} else {
var errors;
}
var error_text = '';
$("#ldaptestloginicon").html('');
$("#ldaptestloginstatus").addClass('text-danger');
$("#ldaptestloginicon").html('<i class="fa fa-exclamation-triangle text-danger"></i>');
if (data.status == 500) {
$('#ldaptestloginstatus').html('500 Server Error');
} else if (data.status == 400) {
if (typeof errors !='string') {
for (i = 0; i < errors.length; i++) {
if (errors[i]) {
error_text += '<li>Error: ' + errors[i];
}
}
} else {
error_text = errors;
}
$('#ldaptestloginstatus').html(error_text);
} else {
$('#ldaptestloginstatus').html(data.responseText.message);
}
}
});
});
</script>
@stop
function buildLdapResultsTableBody(users)
{
let body = '<tbody>'
for (var i in users) {
body += '<tr><td>' + users[i].employee_number + '</td><td>' + users[i].username + '</td><td>' + users[i].firstname + '</td><td>' + users[i].lastname + '</td><td>' + users[i].email + '</td></tr>'
}
body += "</tbody>"
return body;
}
</script>
@endpush

View file

@ -27,13 +27,17 @@ LDAP User Sync
<div class="box-body">
<!-- location_id-->
<div class="form-group {{ $errors->has('location_id') ? 'has-error' : '' }}">
<!-- Location -->
<div class="col-xs-12">
<!-- Location -->
@include ('partials.forms.edit.location-select', ['translated_name' => trans('general.location'), 'fieldname' => 'location_id'])
<div class="col-md-4">
<button type="submit" class="btn btn-warning" id="sync">
<i id="sync-button-icon" class="fa fa-refresh icon-white"></i> <span id="sync-button-text">Synchronize</span>
</button>
</div>
<div class="col-xs-12 text-center">
<button type="submit" class="btn btn-warning" id="sync">
<i id="sync-button-icon" class="fa fa-refresh icon-white"></i> <span id="sync-button-text">Synchronize</span>
</button>
</div>
</div>
</div>
</div>
@ -43,6 +47,7 @@ LDAP User Sync
<p>
{{ trans('admin/users/general.ldap_config_text') }}
</p>
<p><a href="{{ route('settings.ldap.index') }}">LDAP Settings Page</a></p>
</div>
</div>
@ -63,7 +68,7 @@ LDAP User Sync
</tr>
@foreach (Session::get('summary') as $entry)
<tr {!! ($entry['status']=='success') ? 'class="success"' : 'class="danger"' !!}>
<tr {!! ($entry['status']=='SUCCESS') ? 'class="success"' : 'class="danger"' !!}>
<td>{{ $entry['username'] }}</td>
<td>{{ $entry['employee_number'] }}</td>
<td>{{ $entry['firstname'] }}</td>

View file

@ -552,7 +552,7 @@ Route::group(['prefix' => 'v1','namespace' => 'Api'], function () {
/*--- Settings API ---*/
Route::get('settings/ldaptest', [
'as' => 'api.settings.ldaptest',
'uses' => 'SettingsController@ldaptest'
'uses' => 'SettingsController@ldapAdSettingsTest'
]);
Route::get('settings/login-attempts', [