Finalize the LDAP integration and complete SSO support in the login page (#128)

This commit is contained in:
Alex Tselegidis 2024-05-13 23:36:54 +02:00
parent b0ffe4ff1f
commit 5967864e4a
67 changed files with 1994 additions and 17 deletions

View file

@ -94,6 +94,16 @@ const EVENT_MINIMUM_DURATION = 5; // Minutes
const DEFAULT_COMPANY_COLOR = '#ffffff';
const LDAP_DEFAULT_FILTER = '(&(objectClass=person)(|(cn={{KEYWORD}})(sn={{KEYWORD}})(mail={{KEYWORD}})(givenName={{KEYWORD}})(uid={{KEYWORD}})))';
const LDAP_DEFAULT_FIELD_MAPPING = [
'first_name' => 'givenname',
'last_name' => 'sn',
'email' => 'mail',
'phone_number' => 'telephonenumber',
'username' => 'cn',
];
/*
|--------------------------------------------------------------------------
| Webhook Actions

View file

@ -34,6 +34,7 @@ class Admins extends EA_Controller
'notes',
'timezone',
'language',
'ldap_dn',
'settings',
];

View file

@ -38,6 +38,7 @@ class Customers extends EA_Controller
'custom_field_3',
'custom_field_4',
'custom_field_5',
'ldap_dn',
];
/**

View file

@ -0,0 +1,132 @@
<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.5.0
* ---------------------------------------------------------------------------- */
/**
* LDAP settings controller.
*
* Handles LDAP settings related operations.
*
* @package Controllers
*/
class Ldap_settings extends EA_Controller
{
/**
* Ldap_settings constructor.
*/
public function __construct()
{
parent::__construct();
$this->load->model('settings_model');
$this->load->library('accounts');
$this->load->library('ldap_client');
}
/**
* Render the settings page.
*/
public function index(): void
{
session(['dest_url' => site_url('ldap_settings')]);
$user_id = session('user_id');
if (cannot('view', PRIV_SYSTEM_SETTINGS)) {
if ($user_id) {
abort(403, 'Forbidden');
}
redirect('login');
return;
}
$role_slug = session('role_slug');
script_vars([
'user_id' => $user_id,
'role_slug' => $role_slug,
'ldap_settings' => $this->settings_model->get('name like "ldap_%"'),
'ldap_default_filter' => LDAP_DEFAULT_FILTER,
'ldap_default_field_mapping' => LDAP_DEFAULT_FIELD_MAPPING,
]);
html_vars([
'page_title' => lang('ldap'),
'active_menu' => PRIV_SYSTEM_SETTINGS,
'user_display_name' => $this->accounts->get_user_display_name($user_id),
'roles' => $this->roles_model->get(),
]);
$this->load->view('pages/ldap_settings');
}
/**
* Save general settings.
*/
public function save(): void
{
try {
if (cannot('edit', PRIV_SYSTEM_SETTINGS)) {
throw new RuntimeException('You do not have the required permissions for this task.');
}
$settings = request('ldap_settings', []);
foreach ($settings as $setting) {
$existing_setting = $this->settings_model
->query()
->where('name', $setting['name'])
->get()
->row_array();
if (!empty($existing_setting)) {
$setting['id'] = $existing_setting['id'];
}
$this->settings_model->save($setting);
}
response();
} catch (Throwable $e) {
json_exception($e);
}
}
/**
* Search the LDAP directory.
*
* @return void
*/
public function search(): void
{
try {
if (cannot('edit', PRIV_SYSTEM_SETTINGS)) {
throw new RuntimeException('You do not have the required permissions for this task.');
}
if (!extension_loaded('ldap')) {
throw new RuntimeException('The LDAP extension is not loaded.');
}
$keyword = request('keyword');
$entries = $this->ldap_client->search($keyword);
json_response($entries);
} catch (Throwable $e) {
json_exception($e);
}
}
}

View file

@ -28,6 +28,7 @@ class Login extends EA_Controller
parent::__construct();
$this->load->library('accounts');
$this->load->library('ldap_client');
$this->load->library('email_messages');
script_vars([
@ -75,6 +76,10 @@ class Login extends EA_Controller
$user_data = $this->accounts->check_login($username, $password);
if (empty($user_data)) {
$user_data = $this->ldap_client->check_login($username, $password);
}
if (empty($user_data)) {
throw new InvalidArgumentException('Invalid credentials provided, please try again.');
}

View file

@ -35,6 +35,7 @@ class Providers extends EA_Controller
'timezone',
'language',
'is_private',
'ldap_dn',
'id_roles',
'settings',
'services',
@ -52,6 +53,11 @@ class Providers extends EA_Controller
'services' => [],
];
public array $optional_provider_setting_fields = [
'working_plan' => null,
'working_plan_exceptions' => '{}',
];
/**
* Providers constructor.
*/
@ -66,6 +72,8 @@ class Providers extends EA_Controller
$this->load->library('accounts');
$this->load->library('timezones');
$this->load->library('webhooks_client');
$this->optional_provider_setting_fields['working_plan'] = setting('company_working_plan');
}
/**
@ -168,6 +176,8 @@ class Providers extends EA_Controller
$this->providers_model->optional($provider, $this->optional_provider_fields);
$this->providers_model->optional($provider['settings'], $this->optional_provider_setting_fields);
$provider_id = $this->providers_model->save($provider);
$provider = $this->providers_model->find($provider_id);
@ -221,6 +231,8 @@ class Providers extends EA_Controller
$this->providers_model->optional($provider, $this->optional_provider_fields);
$this->providers_model->optional($provider['settings'], $this->optional_provider_setting_fields);
$provider_id = $this->providers_model->save($provider);
$provider = $this->providers_model->find($provider_id);

View file

@ -36,6 +36,7 @@ class Secretaries extends EA_Controller
'timezone',
'language',
'is_private',
'ldap_dn',
'id_roles',
'settings',
'providers',

View file

@ -63,6 +63,7 @@
* @property Caldav_Sync $caldav_sync
* @property Ics_file $ics_file
* @property Instance $instance
* @property Ldap_client $ldap_client
* @property Notifications $notifications
* @property Permissions $permissions
* @property Synchronization $synchronization

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['default_timezone'] = 'Default Timezone';
$lang['default_timezone_hint'] = 'Set the default timezone value that will be used for new records.';
$lang['default_language'] = 'Default Language';
$lang['default_language_hint'] = 'Set the default language value that will be used for new records.';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -461,4 +461,20 @@ $lang['sync_method_prompt'] = 'Which sync method would you like to use?';
$lang['caldav_server'] = 'CalDAV Server';
$lang['caldav_connection_info_prompt'] = 'Please enter the connection information of the target CalDAV server.';
$lang['connect'] = 'Connect';
$lang['ldap'] = 'LDAP';
$lang['ldap_info'] = 'This integration enables you to connect to an existing LDAP server and automatically import users into Easy!Appointments and let them SSO with their directory password (username must match).';
$lang['host'] = 'Host';
$lang['port'] = 'Port';
$lang['user_dn'] = 'User DN';
$lang['base_dn'] = 'Base DN';
$lang['keyword'] = 'Keyword';
$lang['ldap_search_hint'] = 'Provide a keyword to search through the LDAP directory for users that match the filter criteria.';
$lang['ldap_extension_not_loaded'] = 'The LDAP PHP extension is not loaded, but is required for this integration to work.';
$lang['field_mapping'] = 'Field Mapping';
$lang['content'] = 'Content';
$lang['active'] = 'Active';
$lang['user_imported'] = 'The user record was imported successfully.';
$lang['import'] = 'Import';
$lang['ldap_dn'] = 'LDAP DN';
$lang['role'] = 'Role';
// End

View file

@ -161,4 +161,24 @@ class Accounts
->get()
->num_rows() > 0;
}
/**
* Get a user record based on the provided username value
*
* @param string $username
*
* @return array|null
*/
public function get_user_by_username(string $username): ?array
{
$user_settings = $this->CI->db->get_where('user_settings', ['username' => $username])->row_array();
if (!$user_settings) {
return null;
}
$user_id = $user_settings['id_users'];
return $this->CI->users_model->find($user_id);
}
}

View file

@ -0,0 +1,236 @@
<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.4.0
* ---------------------------------------------------------------------------- */
/**
* Ldap_client library.
*
* Handles LDAP related functionality.
*
* @package Libraries
*/
class Ldap_client
{
/**
* @var EA_Controller|CI_Controller
*/
protected EA_Controller|CI_Controller $CI;
/**
* Ldap_client constructor.
*/
public function __construct()
{
$this->CI = &get_instance();
$this->CI->load->model('roles_model');
$this->CI->load->library('timezones');
$this->CI->load->library('accounts');
}
/**
* Validate the provided password with an LDAP hashed password.
*
* @param string $password
* @param string $hashed_password
*
* @return bool
*/
public function validate_password(string $password, string $hashed_password): bool
{
if (empty($hashed_password) || ($hashed_password[0] !== '{' && $password === $hashed_password)) {
return false;
}
if (str_starts_with($hashed_password, '{MD5}')) {
$encrypted_password = '{MD5}' . base64_encode(md5($password, true));
} elseif (str_starts_with($hashed_password, '{SHA1}')) {
$encrypted_password = '{SHA}' . base64_encode(sha1($password, true));
} elseif (str_starts_with($hashed_password, '{SSHA}')) {
$salt = substr(base64_decode(substr($hashed_password, 6)), 20);
$encrypted_password = '{SSHA}' . base64_encode(sha1($password . $salt, true) . $salt);
} else {
throw new RuntimeException('Unsupported password hash format');
}
return $hashed_password === $encrypted_password;
}
/**
* Try authenticating the user with LDAP
*
* @param string $username
* @param string $password
*
* @return array|null
*
* @throws Exception
*/
public function check_login(string $username, string $password): ?array
{
if (empty($username)) {
throw new InvalidArgumentException('No username value provided.');
}
// Check LDAP environment and configuration
if (!extension_loaded('ldap')) {
throw new RuntimeException('The LDAP extension is not loaded.');
}
$ldap_is_active = setting('ldap_is_active');
if (!$ldap_is_active) {
return null;
}
// Match user by username
$user = $this->CI->accounts->get_user_by_username($username);
if (empty($user['ldap_dn'])) {
return null;
}
// Connect to LDAP server
$host = setting('ldap_host');
$port = (int) setting('ldap_port');
$user_dn = setting('ldap_user_dn');
$ldap_password = setting('ldap_password');
$connection = ldap_connect($host, $port);
if (!$connection) {
throw new Exception('Could not connect to LDAP server: ' . ldap_error($connection));
}
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0); // We need this for doing an LDAP search.
$bind = ldap_bind($connection, $user_dn, $ldap_password);
if (!$bind) {
throw new Exception('LDAP bind failed: ' . ldap_error($connection));
}
// Check the provided password against the LDAP service
$filter = '(objectclass=*)';
$result = ldap_search($connection, $user['ldap_dn'], $filter);
if (!$result) {
return null;
}
$ldap_entries = ldap_get_entries($connection, $result);
foreach ($ldap_entries as $ldap_entry) {
if (!is_array($ldap_entry) || empty($ldap_entry['dn']) || $ldap_entry['dn'] !== $user['ldap_dn']) {
continue;
}
if (!$this->validate_password($password, $ldap_entry['userpassword'][0])) {
continue;
}
$role = $this->CI->roles_model->find($user['id_roles']);
$default_timezone = $this->CI->timezones->get_default_timezone();
return [
'user_id' => $user['id'],
'user_email' => $user['email'],
'username' => $username,
'timezone' => !empty($user['timezone']) ? $user['timezone'] : $default_timezone,
'language' => !empty($user['language']) ? $user['language'] : Config::LANGUAGE,
'role_slug' => $role['slug'],
];
}
return null;
}
/**
* Search the LDAP server based on the provided keyword and configuration.
*
* @param string $keyword
*
* @return array
*
* @throws Exception
*/
public function search(string $keyword): array
{
$host = setting('ldap_host');
$port = (int) setting('ldap_port');
$user_dn = setting('ldap_user_dn');
$password = setting('ldap_password');
$base_dn = setting('ldap_base_dn');
$filter = setting('ldap_filter');
$connection = ldap_connect($host, $port);
if (!$connection) {
throw new Exception('Could not connect to LDAP server: ' . ldap_error($connection));
}
ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($connection, LDAP_OPT_REFERRALS, 0); // We need this for doing an LDAP search.
$bind = ldap_bind($connection, $user_dn, $password);
if (!$bind) {
throw new Exception('LDAP bind failed: ' . ldap_error($connection));
}
$wildcard_keyword = !empty($keyword) ? '*' . $keyword . '*' : '*';
$interpolated_filter = str_replace('{{KEYWORD}}', $wildcard_keyword, $filter);
$result = ldap_search($connection, $base_dn, $interpolated_filter);
if (!$result) {
throw new Exception('Search failed: ' . ldap_error($connection));
}
$ldap_entries = ldap_get_entries($connection, $result);
// Flatten the LDAP entries so that they become easier to import
$entries = [];
foreach ($ldap_entries as $ldap_entry) {
if (!is_array($ldap_entry)) {
continue;
}
$entry = [
'dn' => $ldap_entry['dn'],
];
foreach ($ldap_entry as $key => $value) {
if (!is_array($value)) {
continue;
}
$entry[$key] = $value[0] ?? null;
}
$entries[] = $entry;
}
return $entries;
}
}

View file

@ -0,0 +1,102 @@
<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.3.2
* ---------------------------------------------------------------------------- */
class Migration_Add_ldap_rows_to_settings_table extends EA_Migration
{
/**
* Upgrade method.
*/
public function up()
{
$now = date('Y-m-d H:i:s');
$timestamps = [
'create_datetime' => $now,
'update_datetime' => $now,
];
if (!$this->db->get_where('settings', ['name' => 'ldap_is_active'])->num_rows()) {
$this->db->insert('settings', [...$timestamps, 'name' => 'ldap_is_active', 'value' => '0']);
}
if (!$this->db->get_where('settings', ['name' => 'ldap_host'])->num_rows()) {
$this->db->insert('settings', [...$timestamps, 'name' => 'ldap_host', 'value' => '']);
}
if (!$this->db->get_where('settings', ['name' => 'ldap_port'])->num_rows()) {
$this->db->insert('settings', [...$timestamps, 'name' => 'ldap_port', 'value' => '']);
}
if (!$this->db->get_where('settings', ['name' => 'ldap_user_dn'])->num_rows()) {
$this->db->insert('settings', [...$timestamps, 'name' => 'ldap_user_dn', 'value' => '']);
}
if (!$this->db->get_where('settings', ['name' => 'ldap_password'])->num_rows()) {
$this->db->insert('settings', [...$timestamps, 'name' => 'ldap_password', 'value' => '']);
}
if (!$this->db->get_where('settings', ['name' => 'ldap_base_dn'])->num_rows()) {
$this->db->insert('settings', [...$timestamps, 'name' => 'ldap_base_dn', 'value' => '']);
}
if (!$this->db->get_where('settings', ['name' => 'ldap_filter'])->num_rows()) {
$this->db->insert('settings', [...$timestamps, 'name' => 'ldap_filter', 'value' => LDAP_DEFAULT_FILTER]);
}
if (!$this->db->get_where('settings', ['name' => 'ldap_field_mapping'])->num_rows()) {
$this->db->insert('settings', [
...$timestamps,
'name' => 'ldap_field_mapping',
'value' => json_encode(LDAP_DEFAULT_FIELD_MAPPING, JSON_PRETTY_PRINT),
]);
}
}
/**
* Downgrade method.
*/
public function down()
{
if ($this->db->get_where('settings', ['name' => 'ldap_is_active'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_is_active']);
}
if ($this->db->get_where('settings', ['name' => 'ldap_host'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_host']);
}
if ($this->db->get_where('settings', ['name' => 'ldap_port'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_port']);
}
if ($this->db->get_where('settings', ['name' => 'ldap_user_dn'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_user_dn']);
}
if ($this->db->get_where('settings', ['name' => 'ldap_password'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_password']);
}
if ($this->db->get_where('settings', ['name' => 'ldap_base_dn'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_base_dn']);
}
if ($this->db->get_where('settings', ['name' => 'ldap_filter'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_filter']);
}
if ($this->db->get_where('settings', ['name' => 'ldap_field_mapping'])->num_rows()) {
$this->db->delete('settings', ['name' => 'ldap_field_mapping']);
}
}
}

View file

@ -0,0 +1,43 @@
<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.4.0
* ---------------------------------------------------------------------------- */
class Migration_Add_ldap_dn_column_to_users_table extends EA_Migration
{
/**
* Upgrade method.
*/
public function up()
{
if (!$this->db->field_exists('ldap_dn', 'users')) {
$fields = [
'ldap_dn' => [
'type' => 'TEXT',
'null' => true,
'after' => 'is_private',
],
];
$this->dbforge->add_column('users', $fields);
}
}
/**
* Downgrade method.
*/
public function down()
{
if ($this->db->field_exists('ldap_dn', 'users')) {
$this->dbforge->drop_column('users', 'ldap_dn');
}
}
}

View file

@ -45,6 +45,7 @@ class Admins_model extends EA_Model
'timezone' => 'timezone',
'language' => 'language',
'notes' => 'notes',
'ldapDn' => 'ldap_dn',
'roleId' => 'id_roles',
];
@ -556,6 +557,8 @@ class Admins_model extends EA_Model
'zip' => $admin['zip_code'],
'notes' => $admin['notes'],
'timezone' => $admin['timezone'],
'language' => $admin['language'],
'ldapDn' => $admin['ldap_dn'],
'settings' => [
'username' => $admin['settings']['username'],
'notifications' => filter_var($admin['settings']['notifications'], FILTER_VALIDATE_BOOLEAN),
@ -624,6 +627,14 @@ class Admins_model extends EA_Model
$decoded_resource['timezone'] = $admin['timezone'];
}
if (array_key_exists('language', $admin)) {
$decoded_resource['language'] = $admin['language'];
}
if (array_key_exists('ldapDn', $admin)) {
$decoded_resource['ldap_dn'] = $admin['ldapDn'];
}
if (array_key_exists('settings', $admin)) {
if (empty($decoded_resource['settings'])) {
$decoded_resource['settings'] = [];

View file

@ -49,6 +49,7 @@ class Customers_model extends EA_Model
'customField4' => 'custom_field_4',
'customField5' => 'custom_field_5',
'notes' => 'notes',
'ldapDn' => 'ldap_dn',
];
/**
@ -461,6 +462,7 @@ class Customers_model extends EA_Model
'customField3' => $customer['custom_field_3'],
'customField4' => $customer['custom_field_4'],
'customField5' => $customer['custom_field_5'],
'ldapDn' => $customer['ldap_dn'],
];
$customer = $encoded_resource;
@ -532,6 +534,10 @@ class Customers_model extends EA_Model
$decoded_resource['custom_field_5'] = $customer['customField5'];
}
if (array_key_exists('ldapDn', $customer)) {
$decoded_resource['ldap_dn'] = $customer['ldapDn'];
}
if (array_key_exists('notes', $customer)) {
$decoded_resource['notes'] = $customer['notes'];
}

View file

@ -47,6 +47,7 @@ class Providers_model extends EA_Model
'language' => 'language',
'notes' => 'notes',
'isPrivate' => 'is_private',
'ldapDn' => 'ldap_dn',
'roleId' => 'id_roles',
];
@ -749,6 +750,7 @@ class Providers_model extends EA_Model
'zip' => $provider['zip_code'],
'notes' => $provider['notes'],
'is_private' => $provider['is_private'],
'ldapDn' => $provider['ldap_dn'],
'timezone' => $provider['timezone'],
];
@ -869,6 +871,10 @@ class Providers_model extends EA_Model
$decoded_resource['is_private'] = (bool) $provider['isPrivate'];
}
if (array_key_exists('ldapDn', $provider)) {
$decoded_resource['ldap_dn'] = $provider['ldapDn'];
}
if (array_key_exists('settings', $provider)) {
if (empty($decoded_resource['settings'])) {
$decoded_resource['settings'] = [];

View file

@ -45,6 +45,7 @@ class Secretaries_model extends EA_Model
'timezone' => 'timezone',
'language' => 'language',
'notes' => 'notes',
'ldapDn' => 'ldap_dn',
'roleId' => 'id_roles',
];
@ -601,6 +602,8 @@ class Secretaries_model extends EA_Model
'notes' => $secretary['notes'],
'providers' => $secretary['providers'],
'timezone' => $secretary['timezone'],
'language' => $secretary['language'],
'ldapDn' => $secretary['ldap_dn'],
'settings' => [
'username' => $secretary['settings']['username'],
'notifications' => filter_var($secretary['settings']['notifications'], FILTER_VALIDATE_BOOLEAN),
@ -669,6 +672,14 @@ class Secretaries_model extends EA_Model
$decoded_resource['timezone'] = $secretary['timezone'];
}
if (array_key_exists('language', $secretary)) {
$decoded_resource['language'] = $secretary['language'];
}
if (array_key_exists('ldapDn', $secretary)) {
$decoded_resource['ldap_dn'] = $secretary['ldapDn'];
}
if (array_key_exists('providers', $secretary)) {
$decoded_resource['providers'] = $secretary['providers'];
}

View file

@ -44,6 +44,7 @@ class Users_model extends EA_Model
'zip' => 'zip_code',
'timezone' => 'timezone',
'language' => 'language',
'ldapDn' => 'ldap_dn',
'notes' => 'notes',
'roleId' => 'id_roles',
];
@ -125,7 +126,7 @@ class Users_model extends EA_Model
$settings['salt'] = generate_salt();
$settings['password'] = hash_password($settings['salt'], $settings['password']);
$this->save_settings($user['id'], $settings);
$this->set_settings($user['id'], $settings);
return $user['id'];
}
@ -138,7 +139,7 @@ class Users_model extends EA_Model
*
* @throws InvalidArgumentException
*/
protected function save_settings(int $user_id, array $settings): void
protected function set_settings(int $user_id, array $settings): void
{
if (empty($settings)) {
throw new InvalidArgumentException('The settings argument cannot be empty.');
@ -156,6 +157,22 @@ class Users_model extends EA_Model
}
}
/**
* Get the user settings.
*
* @param int $user_id User ID.
*
* @throws InvalidArgumentException
*/
public function get_settings(int $user_id): array
{
$settings = $this->db->get_where('user_settings', ['id_users' => $user_id])->row_array();
unset($settings['id_users'], $settings['password'], $settings['salt']);
return $settings;
}
/**
* Set the value of a user setting.
*
@ -200,7 +217,7 @@ class Users_model extends EA_Model
throw new RuntimeException('Could not update user.');
}
$this->save_settings($user['id'], $settings);
$this->set_settings($user['id'], $settings);
return $user['id'];
}
@ -236,9 +253,7 @@ class Users_model extends EA_Model
$this->cast($user);
$user['settings'] = $this->db->get_where('user_settings', ['id_users' => $user_id])->row_array();
unset($user['settings']['id_users'], $user['settings']['password'], $user['settings']['salt']);
$user['settings'] = $this->get_settings($user['id']);
return $user;
}
@ -346,10 +361,7 @@ class Users_model extends EA_Model
foreach ($users as &$user) {
$this->cast($user);
$user['settings'] = $this->db->get_where('user_settings', ['id_users' => $user['id']])->row_array();
unset($user['settings']['id_users'], $user['settings']['password'], $user['settings']['salt']);
$user['settings'] = $this->get_settings($user['id']);
}
return $users;
@ -383,10 +395,7 @@ class Users_model extends EA_Model
foreach ($users as &$user) {
$this->cast($user);
$user['settings'] = $this->db->get_where('user_settings', ['id_users' => $user['id']])->row_array();
unset($user['settings']['id_users'], $user['settings']['password'], $user['settings']['salt']);
$user['settings'] = $this->get_settings($user['id']);
}
return $users;

View file

@ -0,0 +1,105 @@
<?php
/**
* Local variables.
*
* @var array $roles
*/
?>
<div id="ldap-import-modal" class="modal fade">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-lg">
<div class="modal-content">
<div class="modal-header">
<h3 class="modal-title"><?= lang('import') ?></h3>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label" for="ldap-import-ldap-dn">
<?= lang('ldap_dn') ?>
<span class="text-danger">*</span>
</label>
<input id="ldap-import-ldap-dn" class="form-control required" maxlength="256">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-import-role-slug">
<?= lang('role') ?>
<span class="text-danger">*</span>
</label>
<select id="ldap-import-role-slug" class="form-control required">
<?php foreach ($roles as $role): ?>
<option value="<?= $role['slug'] ?>">
<?= $role['name'] ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label" for="ldap-import-first-name">
<?= lang('first_name') ?>
<span class="text-danger">*</span>
</label>
<input id="ldap-import-first-name" class="form-control required" maxlength="256">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-import-last-name">
<?= lang('last_name') ?>
<span class="text-danger">*</span>
</label>
<input id="ldap-import-last-name" class="form-control required" maxlength="256">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-import-email">
<?= lang('email') ?>
<span class="text-danger">*</span>
</label>
<input id="ldap-import-email" class="form-control required" max="512">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-import-phone-number">
<?= lang('phone_number') ?>
<span class="text-danger">*</span>
</label>
<input id="ldap-import-phone-number" class="form-control required" max="128">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-import-username">
<?= lang('username') ?>
</label>
<input id="ldap-import-username" class="form-control" maxlength="256">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-import-password">
<?= lang('password') ?>
</label>
<input type="password" id="ldap-import-password" class="form-control"
maxlength="512" autocomplete="new-password">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">
<?= lang('cancel') ?>
</button>
<button id="ldap-import-save" class="btn btn-primary">
<i class="fas fa-check-square me-2"></i>
<?= lang('save') ?>
</button>
</div>
</div>
</div>
</div>
<?php section('scripts'); ?>
<script src="<?= asset_url('assets/js/components/ldap_import_modal.js') ?>"></script>
<?php end_section('scripts'); ?>

View file

@ -208,6 +208,15 @@
]); ?>
</div>
<?php if (setting('ldap_is_active')): ?>
<div class="mb-3">
<label for="ldap-dn" class="form-label">
<?= lang('ldap_dn') ?>
</label>
<input type="text" id="ldap-dn" class="form-control" maxlength="100" disabled/>
</div>
<?php endif; ?>
<div>
<label class="form-label mb-3">
<?= lang('options') ?>

View file

@ -185,6 +185,15 @@
]); ?>
</div>
<?php if (setting('ldap_is_active')): ?>
<div class="mb-3">
<label for="ldap-dn" class="form-label">
<?= lang('ldap_dn') ?>
</label>
<input type="text" id="ldap-dn" class="form-control" maxlength="100" disabled/>
</div>
<?php endif; ?>
<?php component('custom_fields', [
'disabled' => true,
]); ?>

View file

@ -111,6 +111,29 @@
</div>
</div>
<div class="col-sm-6 mb-4">
<div class="card h-100">
<div class="card-header">
<h5 class="fw-light text-black-50 mb-0">
<?= lang('ldap') ?>
</h5>
</div>
<div class="card-body">
<div class="mb-3 integration-info">
<small>
<?= lang('ldap_info') ?>
</small>
</div>
</div>
<div class="card-footer bg-white border-0">
<a href="<?= site_url('ldap_settings') ?>" class="btn btn-outline-primary w-100">
<i class="fas fa-cogs me-2"></i>
<?= lang('configure') ?>
</a>
</div>
</div>
</div>
<?php slot('after_integration_cards'); ?>
</div>
</div>

View file

@ -0,0 +1,165 @@
<?php extend('layouts/backend_layout'); ?>
<?php section('content'); ?>
<div id="ldap-settings-page" class="container backend-page">
<div class="row">
<div class="col-sm-3 offset-sm-1">
<?php component('settings_nav'); ?>
</div>
<div id="ldap-settings" class="col-sm-6">
<form>
<fieldset>
<div class="d-flex justify-content-between align-items-center border-bottom mb-4 py-2">
<h4 class="text-black-50 mb-0 fw-light">
<?= lang('ldap') ?>
</h4>
<div>
<a href="<?= site_url('integrations') ?>" class="btn btn-outline-primary me-2">
<i class="fas fa-chevron-left me-2"></i>
<?= lang('back') ?>
</a>
<?php if (can('edit', PRIV_SYSTEM_SETTINGS)): ?>
<button type="button" id="save-settings" class="btn btn-primary">
<i class="fas fa-check-square me-2"></i>
<?= lang('save') ?>
</button>
<?php endif; ?>
</div>
</div>
<?php if (!extension_loaded('ldap')): ?>
<div class="alert alert-warning">
<?= lang('ldap_extension_not_loaded') ?>
</div>
<?php endif; ?>
<div class="row">
<div class="col-12">
<div class="mb-3">
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" id="ldap-is-active" data-field="ldap_is_active">
<label class="form-check-label" for="ldap-is-active">
<?= lang('active') ?>
</label>
</div>
</div>
<div class="mb-3">
<label class="form-label" for="ldap-host">
<?= lang('host') ?>
</label>
<input id="ldap-host" class="form-control" data-field="ldap_host">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-port">
<?= lang('port') ?>
</label>
<input id="ldap-port" class="form-control" data-field="ldap_port">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-user_dn">
<?= lang('user_dn') ?>
</label>
<input id="ldap-user_dn" class="form-control" data-field="ldap_user_dn">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-password">
<?= lang('password') ?>
</label>
<input id="ldap-password" type="password" class="form-control" data-field="ldap_password">
</div>
<div class="mb-3">
<label class="form-label" for="ldap-base-dn">
<?= lang('base_dn') ?>
</label>
<input id="ldap-base-dn" class="form-control" data-field="ldap_base_dn">
</div>
<div class="mb-3">
<div class="d-flex mb-2">
<label class="form-label mb-0" for="ldap-filter">
<?= lang('filter') ?>
</label>
<button type="button" class="btn btn-sm btn-outline-secondary py-0 ms-auto" id="ldap-reset-filter">
<i class="fas fa-undo me-2"></i>
<?= lang('reset') ?>
</button>
</div>
<input id="ldap-filter" class="form-control" data-field="ldap_filter">
</div>
<div class="mb-3">
<div class="d-flex mb-2">
<label class="form-label mb-0" for="ldap-field-mapping">
<?= lang('field_mapping') ?>
</label>
<button type="button" class="btn btn-sm btn-outline-secondary py-0 ms-auto" id="ldap-reset-field-mapping">
<i class="fas fa-undo me-2"></i>
<?= lang('reset') ?>
</button>
</div>
<textarea id="ldap-field-mapping" class="form-control" rows="5" data-field="ldap_field_mapping"></textarea>
</div>
</div>
</div>
<?php slot('after_primary_appointment_fields'); ?>
</fieldset>
</form>
<div class="d-flex justify-content-between align-items-center border-bottom mb-4 py-2">
<h4 class="text-black-50 mb-0 fw-light">
<?= lang('search') ?>
</h4>
</div>
<p class="text-muted small">
<?= lang('ldap_search_hint') ?>
</p>
<form id="ldap-search-form" class="mb-3">
<label class="form-label" for="ldap-search-keyword">
<?= lang('keyword') ?>
</label>
<div class="input-group">
<input id="ldap-search-keyword" class="form-control">
<button type="submit" class="btn btn-outline-primary">
<?= lang('search') ?>
</button>
</div>
</form>
<div id="ldap-search-results" class="mb-3">
<!-- JS -->
</div>
</div>
</div>
</div>
<?php component('ldap_import_modal', [
'roles' => vars('roles'),
]); ?>
<?php end_section('content'); ?>
<?php section('scripts'); ?>
<script src="<?= asset_url('assets/js/utils/url.js') ?>"></script>
<script src="<?= asset_url('assets/js/http/customers_http_client.js') ?>"></script>
<script src="<?= asset_url('assets/js/http/providers_http_client.js') ?>"></script>
<script src="<?= asset_url('assets/js/http/secretaries_http_client.js') ?>"></script>
<script src="<?= asset_url('assets/js/http/admins_http_client.js') ?>"></script>
<script src="<?= asset_url('assets/js/http/ldap_settings_http_client.js') ?>"></script>
<script src="<?= asset_url('assets/js/pages/ldap_settings.js') ?>"></script>
<?php end_section('scripts'); ?>

View file

@ -230,6 +230,15 @@
]); ?>
</div>
<?php if (setting('ldap_is_active')): ?>
<div class="mb-3">
<label for="ldap-dn" class="form-label">
<?= lang('ldap_dn') ?>
</label>
<input type="text" id="ldap-dn" class="form-control" maxlength="100" disabled/>
</div>
<?php endif; ?>
<div>
<label class="form-label mb-3">
<?= lang('options') ?>

View file

@ -207,6 +207,15 @@
]); ?>
</div>
<?php if (setting('ldap_is_active')): ?>
<div class="mb-3">
<label for="ldap-dn" class="form-label">
<?= lang('ldap_dn') ?>
</label>
<input type="text" id="ldap-dn" class="form-control" maxlength="100" disabled/>
</div>
<?php endif; ?>
<div>
<label class="form-label mb-3">
<?= lang('options') ?>

View file

@ -0,0 +1,170 @@
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.5.0
* ---------------------------------------------------------------------------- */
/**
* LDAP import modal component.
*
* This module implements the LDAP import modal functionality.
*
* This module requires the following scripts:
*
* - App.Http.Customers
* - App.Http.Providers
* - App.Http.Secretaries
* - App.Http.Admins
*/
App.Components.LdapImportModal = (function () {
const $modal = $('#ldap-import-modal');
const $save = $('#ldap-import-save');
const $firstName = $('#ldap-import-first-name');
const $lastName = $('#ldap-import-last-name');
const $email = $('#ldap-import-email');
const $phoneNumber = $('#ldap-import-phone-number');
const $username = $('#ldap-import-username');
const $ldapDn = $('#ldap-import-ldap-dn');
const $roleSlug = $('#ldap-import-role-slug');
const $password = $('#ldap-import-password');
let deferred;
function validate() {
$modal.find('.is-invalid').removeClass('is-invalid');
// Check required fields.
let missingRequiredField = false;
$modal.find('.required').each((index, requiredField) => {
if ($(requiredField).val() === '' || $(requiredField).val() === null) {
$(requiredField).addClass('is-invalid');
missingRequiredField = true;
}
});
return !missingRequiredField;
}
function getHttpClient(roleSlug) {
switch (roleSlug) {
case App.Layouts.Backend.DB_SLUG_CUSTOMER:
return App.Http.Customers;
case App.Layouts.Backend.DB_SLUG_PROVIDER:
return App.Http.Providers;
case App.Layouts.Backend.DB_SLUG_SECRETARY:
return App.Http.Secretaries;
case App.Layouts.Backend.DB_SLUG_ADMIN:
return App.Http.Admins;
default:
throw new Error(`Unsupported role slug provided: ${roleSlug}`);
}
}
function getUser(roleSlug) {
const user = {
first_name: $firstName.val(),
last_name: $lastName.val(),
email: $email.val(),
phone_number: $phoneNumber.val(),
ldap_dn: $ldapDn.val(),
};
if (roleSlug !== App.Layouts.Backend.DB_SLUG_CUSTOMER) {
user.settings = {
username: $username.val(),
password: $password.val(),
notification: 1,
};
}
return user;
}
/**
* Go through the available values and try to load as many as possible based on the provided mapping.
*
* @param {Object} entry
* @param {Object} ldapFieldMapping
*/
function loadEntry(entry, ldapFieldMapping) {
$ldapDn.val(entry.dn);
$firstName.val(entry?.[ldapFieldMapping?.first_name] ?? '');
$lastName.val(entry?.[ldapFieldMapping?.last_name] ?? '');
$email.val(entry?.[ldapFieldMapping?.email] ?? '');
$phoneNumber.val(entry?.[ldapFieldMapping?.phone_number] ?? '');
$username.val(entry?.[ldapFieldMapping?.username] ?? '');
$password.val('');
}
function onSaveClick() {
if (!validate()) {
return;
}
const roleSlug = $roleSlug.val();
const user = getUser(roleSlug);
const httpClient = getHttpClient(roleSlug);
httpClient.store(user).done(() => {
deferred.resolve();
deferred = undefined;
$modal.modal('hide');
});
}
function onModalHidden() {
resetModal();
if (deferred) {
deferred.reject();
}
}
function resetModal() {
$modal.find('input, select, textarea').val('');
$modal.find(':checkbox').prop('checked', false);
$roleSlug.val(App.Layouts.Backend.DB_SLUG_PROVIDER);
}
/**
* Open the import modal for an LDAP entry.
*
* @param {Object} entry
* @param {Object} ldapFieldMapping
*
* @return {Object} $.Deferred
*/
function open(entry, ldapFieldMapping) {
resetModal();
deferred = $.Deferred();
loadEntry(entry, ldapFieldMapping);
$modal.modal('show');
return deferred.promise();
}
/**
* Initialize the module.
*/
function initialize() {
$save.on('click', onSaveClick);
$modal.on('hidden.bs.modal', onModalHidden);
}
document.addEventListener('DOMContentLoaded', initialize);
return {
open,
};
})();

View file

@ -0,0 +1,58 @@
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.5.0
* ---------------------------------------------------------------------------- */
/**
* LDAP Settings HTTP client.
*
* This module implements the LDAP settings related HTTP requests.
*/
App.Http.LdapSettings = (function () {
/**
* Save LDAP settings.
*
* @param {Object} ldapSettings
*
* @return {Object}
*/
function save(ldapSettings) {
const url = App.Utils.Url.siteUrl('ldap_settings/save');
const data = {
csrf_token: vars('csrf_token'),
ldap_settings: ldapSettings,
};
return $.post(url, data);
}
/**
* Search LDAP server.
*
* @param {String} keyword
*
* @return {Object}
*/
function search(keyword) {
const url = App.Utils.Url.siteUrl('ldap_settings/search');
const data = {
csrf_token: vars('csrf_token'),
keyword,
};
return $.post(url, data);
}
return {
save,
search,
};
})();

View file

@ -29,6 +29,7 @@ App.Pages.Admins = (function () {
const $notes = $('#notes');
const $language = $('#language');
const $timezone = $('#timezone');
const $ldapDn = $('#ldap-dn');
const $username = $('#username');
const $password = $('#password');
const $passwordConfirmation = $('#password-confirm');
@ -186,6 +187,7 @@ App.Pages.Admins = (function () {
notes: $notes.val(),
language: $language.val(),
timezone: $timezone.val(),
ldap_dn: $ldapDn.val(),
settings: {
username: $username.val(),
notifications: Number($notifications.prop('checked')),
@ -364,6 +366,7 @@ App.Pages.Admins = (function () {
$notes.val(admin.notes);
$language.val(admin.language);
$timezone.val(admin.timezone);
$ldapDn.val(admin.ldap_dn);
$username.val(admin.settings.username);
$calendarView.val(admin.settings.calendar_view);

View file

@ -27,6 +27,7 @@ App.Pages.Customers = (function () {
const $zipCode = $('#zip-code');
const $timezone = $('#timezone');
const $language = $('#language');
const $ldapDn = $('#ldap-dn');
const $customField1 = $('#custom-field-1');
const $customField2 = $('#custom-field-2');
const $customField3 = $('#custom-field-3');
@ -138,6 +139,7 @@ App.Pages.Customers = (function () {
custom_field_3: $customField3.val(),
custom_field_4: $customField4.val(),
custom_field_5: $customField5.val(),
ldap_dn: $ldapDn.val(),
};
if ($id.val()) {
@ -288,6 +290,7 @@ App.Pages.Customers = (function () {
$notes.val(customer.notes);
$timezone.val(customer.timezone);
$language.val(customer.language || 'english');
$ldapDn.val(customer.ldap_dn);
$customField1.val(customer.custom_field_1);
$customField2.val(customer.custom_field_2);
$customField3.val(customer.custom_field_3);

View file

@ -0,0 +1,246 @@
/* ----------------------------------------------------------------------------
* Easy!Appointments - Online Appointment Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.5.0
* ---------------------------------------------------------------------------- */
/**
* LDAP settings page.
*
* This module implements the functionality of the LDAP settings page.
*/
App.Pages.LdapSettings = (function () {
const $saveSettings = $('#save-settings');
const $searchForm = $('#ldap-search-form');
const $searchKeyword = $('#ldap-search-keyword');
const $searchResults = $('#ldap-search-results');
const $ldapFilter = $('#ldap-filter');
const $ldapFieldMapping = $('#ldap-field-mapping');
const $resetFilter = $('#ldap-reset-filter');
const $resetFieldMapping = $('#ldap-reset-field-mapping');
/**
* Check if the form has invalid values.
*
* @return {Boolean}
*/
function isInvalid() {
try {
$('#ldap-settings .is-invalid').removeClass('is-invalid');
// Validate required fields.
let missingRequiredFields = false;
$('#ldap-settings .required').each((index, requiredField) => {
const $requiredField = $(requiredField);
if (!$requiredField.val()) {
$requiredField.addClass('is-invalid');
missingRequiredFields = true;
}
});
if (missingRequiredFields) {
throw new Error(lang('fields_are_required'));
}
return false;
} catch (error) {
App.Layouts.Backend.displayNotification(error.message);
return true;
}
}
function deserialize(ldapSettings) {
ldapSettings.forEach((ldapSetting) => {
const $field = $('[data-field="' + ldapSetting.name + '"]');
$field.is(':checkbox')
? $field.prop('checked', Boolean(Number(ldapSetting.value)))
: $field.val(ldapSetting.value);
});
}
function serialize() {
const ldapSettings = [];
$('[data-field]').each((index, field) => {
const $field = $(field);
ldapSettings.push({
name: $field.data('field'),
value: $field.is(':checkbox') ? Number($field.prop('checked')) : $field.val(),
});
});
return ldapSettings;
}
function getLdapFieldMapping() {
const jsonLdapFieldMapping = $ldapFieldMapping.val();
return JSON.parse(jsonLdapFieldMapping);
}
/**
* Save the current server settings.
*/
function saveSettings() {
if (isInvalid()) {
App.Layouts.Backend.displayNotification(lang('settings_are_invalid'));
return;
}
const ldapSettings = serialize();
return App.Http.LdapSettings.save(ldapSettings);
}
/**
* Search the LDAP server based on a keyword.
*/
function searchServer() {
$searchResults.empty();
const keyword = $searchKeyword.val();
if (!keyword) {
return;
}
App.Http.LdapSettings.search(keyword).done((entries) => {
$searchResults.empty();
if (!entries?.length) {
renderNoRecordsFound().appendTo($searchResults);
return;
}
entries.forEach((entry) => {
renderEntry(entry).appendTo($searchResults);
});
});
}
/**
* Save the account information.
*/
function onSaveSettingsClick() {
saveSettings().done(() => {
App.Layouts.Backend.displayNotification(lang('settings_saved'));
});
}
/**
* Set the field value back to the original state.
*/
function onResetFilterClick() {
$ldapFilter.val(vars('ldap_default_filter'));
}
/**
* Set the field value back to the original state.
*/
function onResetFieldMappingClick() {
const defaultFieldMapping = vars('ldap_default_field_mapping');
const jsonDefaultFieldMapping = JSON.stringify(defaultFieldMapping, null, 2);
$ldapFieldMapping.val(jsonDefaultFieldMapping);
}
/**
* Handle the LDAP import button click
*/
function onLdapImportClick(event) {
const $target = $(event.target);
const $card = $target.closest('.card');
const entry = $card.data('entry');
const ldapFieldMapping = getLdapFieldMapping();
App.Components.LdapImportModal.open(entry, ldapFieldMapping).done(() => {
App.Layouts.Backend.displayNotification(lang('user_imported'));
});
}
/**
* Render the no-records-found message
*/
function renderNoRecordsFound() {
return $(`
<div class="text-muted fst-italic">
${lang('no_records_found')}
</div>
`);
}
/**
* Render the LDAP entry data on screen
*
* @param {Object} entry
*/
function renderEntry(entry) {
if (!entry?.dn) {
return;
}
const $entry = $(`
<div class="card mb-2">
<div class="card-header">
${entry.dn}
</div>
<div class="card-body">
<p class="d-block mb-2">${lang('content')}</p>
<pre class="overflow-y-auto bg-light rounded p-2" style="max-height: 200px">${JSON.stringify(entry, null, 2)}</pre>
<div class="d-lg-flex">
<button class="btn btn-outline-primary ldap-import ms-lg-auto">
${lang('import')}
</button>
</div>
</div>
</div>
`);
$entry.data('entry', entry);
return $entry;
}
/**
* Save the current connection settings and then search the directory based on the provided keyword.
*
* @param {Object} event
*/
function onSearchFormSubmit(event) {
event.preventDefault();
saveSettings().done(() => {
searchServer();
});
}
/**
* Initialize the module.
*/
function initialize() {
$saveSettings.on('click', onSaveSettingsClick);
$resetFilter.on('click', onResetFilterClick);
$resetFieldMapping.on('click', onResetFieldMappingClick);
$searchForm.on('submit', onSearchFormSubmit);
$searchResults.on('click', '.ldap-import', onLdapImportClick);
const ldapSettings = vars('ldap_settings');
deserialize(ldapSettings);
}
document.addEventListener('DOMContentLoaded', initialize);
return {};
})();

View file

@ -30,6 +30,7 @@ App.Pages.Providers = (function () {
const $notes = $('#notes');
const $language = $('#language');
const $timezone = $('#timezone');
const $ldapDn = $('#ldap-dn');
const $username = $('#username');
const $password = $('#password');
const $passwordConfirmation = $('#password-confirm');
@ -168,6 +169,7 @@ App.Pages.Providers = (function () {
notes: $notes.val(),
language: $language.val(),
timezone: $timezone.val(),
ldap_dn: $ldapDn.val(),
settings: {
username: $username.val(),
working_plan: JSON.stringify(workingPlanManager.get()),
@ -384,6 +386,7 @@ App.Pages.Providers = (function () {
$notes.val(provider.notes);
$language.val(provider.language);
$timezone.val(provider.timezone);
$ldapDn.val(provider.ldap_dn);
$username.val(provider.settings.username);
$calendarView.val(provider.settings.calendar_view);

View file

@ -29,6 +29,7 @@ App.Pages.Secretaries = (function () {
const $notes = $('#notes');
const $language = $('#language');
const $timezone = $('#timezone');
const $ldapDn = $('#ldap-dn');
const $username = $('#username');
const $password = $('#password');
const $passwordConfirmation = $('#password-confirm');
@ -190,6 +191,7 @@ App.Pages.Secretaries = (function () {
notes: $notes.val(),
language: $language.val(),
timezone: $timezone.val(),
ldap_dn: $ldapDn.val(),
settings: {
username: $username.val(),
notifications: Number($notifications.prop('checked')),
@ -374,6 +376,7 @@ App.Pages.Secretaries = (function () {
$notes.val(secretary.notes);
$language.val(secretary.language);
$timezone.val(secretary.timezone);
$ldapDn.val(secretary.ldap_dn);
$username.val(secretary.settings.username);
$calendarView.val(secretary.settings.calendar_view);

View file

@ -41,7 +41,7 @@ window.App.Utils.Message = (function () {
text: lang('close'),
className: 'btn btn-outline-primary',
click: function (event, messageModal) {
messageModal.dispose();
messageModal.hide();
},
},
];

View file

@ -28,7 +28,7 @@
],
"minimum-stability": "stable",
"require": {
"php": ">=8.0",
"php": ">=8.1",
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
@ -40,7 +40,7 @@
"monolog/monolog": "^2.8.0",
"google/apiclient": "^2.12.6",
"guzzlehttp/guzzle": "^7.5.0",
"sabre/vobject": "^4.5"
"sabre/vobject": "^4.5",
},
"require-dev": {
"roave/security-advisories": "dev-master",

View file

@ -1936,6 +1936,8 @@ components:
type: string
customField5:
type: string
ldapDn:
type: string
notes:
type: string
example:
@ -1953,6 +1955,7 @@ components:
customField3: Value3
customField4: Value4
customField5: Value5
ldapDn: null
notes: This is a test customer.
CustomerPayload:
type: object
@ -1985,6 +1988,8 @@ components:
type: string
customField5:
type: string
ldapDn:
type: string
notes:
type: string
example:
@ -2001,6 +2006,7 @@ components:
customField3: Value3
customField4: Value4
customField5: Value5
ldapDn: null
notes: This is a test customer.
ServiceRecord:
type: object
@ -2122,6 +2128,10 @@ components:
type: string
timezone:
type: string
language:
type: string
ldapDn:
type: string
settings:
type: object
properties:
@ -2146,6 +2156,8 @@ components:
zip: '12345'
notes: This is a test admin.
timezone: UTC
language: english
ldapDn: null
settings:
username: jasondoe
password: Password@123
@ -2176,6 +2188,8 @@ components:
type: string
language:
type: string
ldapDn:
type: string
settings:
type: object
properties:
@ -2200,6 +2214,7 @@ components:
notes: This is a test admin.
timezone: UTC
language: english
ldapDn: null
settings:
username: jasondoe
password: Password@123
@ -2234,6 +2249,8 @@ components:
type: string
isPrivate:
type: boolean
ldapDn:
type: string
services:
type: array
items:
@ -2286,6 +2303,7 @@ components:
language: english
services: [ ]
isPrivate: false
ldapDn: null
settings:
username: chrisdoe
password: Password@123
@ -2351,6 +2369,8 @@ components:
type: string
isPrivate:
type: boolean
ldapDn:
type: string
services:
type: array
items:
@ -2402,6 +2422,7 @@ components:
timezone: UTC
language: english
isPrivate: false
ldapDn: null
services: [ ]
settings:
username: chrisdoe
@ -2468,6 +2489,8 @@ components:
type: string
language:
type: string
ldapDn:
type: string
providers:
type: array
items:
@ -2497,6 +2520,7 @@ components:
notes: This is a test service.
timezone: UTC
language: english
ldapDn: null
providers: [ ]
settings:
username: jessydoe
@ -2528,6 +2552,8 @@ components:
type: string
language:
type: string
ldapDn:
type: string
providers:
type: array
items:
@ -2556,6 +2582,7 @@ components:
notes: This is a test service.
timezone: UTC
language: english
ldapDn: null
providers: [ ]
settings:
username: jessydoe