From 17cb13364b5c366d2e9f2d1e2bf2e8d2081857bd Mon Sep 17 00:00:00 2001 From: Viharm Date: Tue, 15 Dec 2020 23:21:45 +0000 Subject: [PATCH] Feature ldapauth (#507) * Composer dependency added * Updated code for language and config controller * Added LDAP auth code * Added blank discord value for new user during install --- composer.json | 3 +- src/lang/en_US.lang.php | 49 +++++++++++++ .../Config/Controller/ConfigController.php | 69 ++++++++++++++++++- .../Install/Controller/InstallController.php | 1 + src/psm/Service/User.php | 47 ++++++++++--- .../default/module/config/config.tpl.html | 42 +++++++++++ 6 files changed, 201 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 0498c033..31228c8a 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,8 @@ "php-pushover/php-pushover": "dev-master", "paragonie/random_compat": "^2.0", "twig/twig": "~1.35", - "jaxl/jaxl": "^3.1" + "jaxl/jaxl": "^3.1", + "viharm/psm-ldap-auth": "^1.1" }, "autoload": { "files": [ diff --git a/src/lang/en_US.lang.php b/src/lang/en_US.lang.php index ff6895ac..0d64ce17 100644 --- a/src/lang/en_US.lang.php +++ b/src/lang/en_US.lang.php @@ -342,6 +342,53 @@ $sm_lang = array( 'jabber_password' => 'Password', 'jabber_password_description' => 'Fill only to set or change.', 'jabber_check' => 'Check your Jabber account if message was received.', + 'dirauth_status' => 'Authenticate with directory service', + 'authdir_host_locn' => 'Directory host', + 'authdir_host_port' => 'Directory port', + 'authdir_type' => 'Service type', + 'authdir_type_description' => 'OpenLDAP: Directory is an OpenLDAP service.
AD + DS: Directory is an Active Directory Domain Service.
AD + LDS: Directory is an Active Directory Lightweight Directory + Service.', + 'authdir_type_openldap' => 'OpenLDAP', + 'authdir_type_adds' => 'AD DS', + 'authdir_type_adlds' => 'AD LDS', + 'authdir_userdomain' => 'Active Directory domain', + 'authdir_userdomain_description' => 'User domain for Active Directory. This is typically the NETBIOS domain + for AD DS and the DNS domain for AD LDS. Not used for OpenLDAP + directories.', + 'authdir_ldapver' => 'LDAP protocol version', + 'authdir_ldapver_description' => 'Version of the LDAP specification. This is typically Version 3 (default). + Version 2 was deprecated in 2003 (RFC3494).', + 'authdir_ldapfollowref' => 'Follow referrals', + 'authdir_ldapfollowref_description' => 'Follow referrals if the specified server refers to another server for + the required information. Leave unchecked if you are unaware of this + functionality.', + 'authdir_basedn' => 'Base DN*', + 'authdir_basedn_description' => 'Base distinguished name (DN) of the directory service. E.g., + dc=domain,dc=tld. This is a required field.', + 'authdir_usernameattrib' => 'Username attribute', + 'authdir_usernameattrib_description' => 'Attribute used by the directory service to refer to the username of + the user.', + 'authdir_groupnameattrib' => 'Group name attribute', + 'authdir_groupnameattrib_description' => 'Attribute used by the directory service to refer to the group name + of a group. This is used to check for group membership.', + 'authdir_groupmemattrib' => 'Group member attribute', + 'authdir_groupmemattrib_description' => 'Attribute used by the directory service to refer to the group(s) of + which the user is a member. This is used to check for group + membership.', + 'authdir_usercontainerrdn' => 'User container RDN', + 'authdir_usercontainerrdn_description' => 'Relative distinguished name of the users container in the + directory. E.g., ou=Users', + 'authdir_groupcontainerrdn' => 'Group container RDN', + 'authdir_groupcontainerrdn_description' => 'Relative distinguished name of the groups container in the + directory. E.g., ou=Groups', + 'authdir_groupname' => 'Authorised directory group', + 'authdir_groupname_description' => 'Directory group authorised to access application. Directory users not + members of this group will not be authenticated (currently not available + for AD).', + 'authdir_defaultrole' => 'Default role', + 'authdir_defaultrole_description' => 'Default role to be assigned to users logging in for the first time.', 'alert_type' => 'Select when you\'d like to be notified.', 'alert_type_description' => 'Status change: You will receive a notification when a server has a change in status. So from online -> offline or offline -> online.

Offline: You will receive a notification when a server goes offline for the *FIRST TIME ONLY*. For example, your cronjob is every 15 minutes and your server goes down at 1 am and stays down till 6 am. You will get 1 notification at 1 am and that\'s it.

Always: You will receive a notification every time the script runs and a site is down, even if the site has been offline for hours.', 'alert_type_status' => 'Status change', @@ -368,6 +415,7 @@ $sm_lang = array( 'tab_webhook' => 'Webhook', 'tab_telegram' => 'Telegram', 'tab_jabber' => 'Jabber', + 'tab_auth' => 'Authentication', 'settings_email' => 'Email settings', 'settings_sms' => 'Text message settings', 'settings_discord' => 'Discord settings', @@ -378,6 +426,7 @@ $sm_lang = array( 'settings_notification' => 'Notification settings', 'settings_log' => 'Log settings', 'settings_proxy' => 'Proxy settings', + 'settings_dirauth' => 'LDAP settings', 'auto_refresh' => 'Auto-refresh', 'auto_refresh_description' => 'Auto-refresh servers page.
Time in seconds, if 0 the page won\'t refresh.', 'test' => 'Test', diff --git a/src/psm/Module/Config/Controller/ConfigController.php b/src/psm/Module/Config/Controller/ConfigController.php index df524c4d..c93ca375 100644 --- a/src/psm/Module/Config/Controller/ConfigController.php +++ b/src/psm/Module/Config/Controller/ConfigController.php @@ -58,6 +58,8 @@ class ConfigController extends AbstractController 'log_jabber', 'show_update', 'combine_notifications', + 'dirauth_status', + 'authdir_ldapfollowref', ); /** @@ -85,7 +87,18 @@ class ConfigController extends AbstractController 'jabber_username', 'jabber_domain', 'user_agent', - 'site_title' + 'site_title', + 'authdir_host_locn', + 'authdir_host_port', + 'authdir_userdomain', + 'authdir_ldapver', + 'authdir_basedn', + 'authdir_usernameattrib', + 'authdir_groupnameattrib', + 'authdir_groupmemattrib', + 'authdir_usercontainerrdn', + 'authdir_groupcontainerrdn', + 'authdir_groupname', ); /** @@ -162,6 +175,20 @@ class ConfigController extends AbstractController ); } + foreach (array("20", "10") as $authdir_defaultrole) { + $tpl_data['authdir_defaultroles'][] = array( + 'value' => $authdir_defaultrole, + 'label' => psm_get_lang('users', 'level_' . $authdir_defaultrole), + ); + } + + foreach (array("openldap", "adds", "adlds") as $authdir_type) { + $tpl_data['authdir_type'][] = array( + 'value' => $authdir_type, + 'label' => psm_get_lang('config', 'authdir_type_' . $authdir_type), + ); + } + $tpl_data['email_smtp_security'] = array( array( 'value' => '', @@ -181,6 +208,10 @@ class ConfigController extends AbstractController $config['sms_gateway'] : current($sms_gateways); $tpl_data['alert_type_selected'] = isset($config['alert_type']) ? $config['alert_type'] : ''; + $tpl_data['authdir_type_selected'] = isset($config['authdir_type']) ? + $config['authdir_type'] : ''; + $tpl_data['authdir_defaultrole_selected'] = isset($config['authdir_defaultrole']) ? + $config['authdir_defaultrole'] : '20'; $tpl_data['email_smtp_security_selected'] = isset($config['email_smtp_security']) ? $config['email_smtp_security'] : ''; $tpl_data['auto_refresh_servers'] = isset($config['auto_refresh_servers']) ? @@ -244,6 +275,8 @@ class ConfigController extends AbstractController 'site_title' => $_POST['site_title'], 'sms_gateway' => $_POST['sms_gateway'], 'alert_type' => $_POST['alert_type'], + 'authdir_defaultrole' => $_POST['authdir_defaultrole'], + 'authdir_type' => $_POST['authdir_type'], 'email_smtp_security' => in_array($_POST['email_smtp_security'], array('', 'ssl', 'tls')) ? $_POST['email_smtp_security'] @@ -296,6 +329,8 @@ class ConfigController extends AbstractController if (isset($_POST['general_submit'])) { $this->default_tab = 'general'; + } elseif (isset($_POST['auth_submit'])) { + $this->default_tab = 'auth'; } elseif (isset($_POST['email_submit']) || !empty($_POST['test_email'])) { $this->default_tab = 'email'; } elseif (isset($_POST['sms_submit']) || !empty($_POST['test_sms'])) { @@ -546,6 +581,7 @@ class ConfigController extends AbstractController 'label_tab_webhook' => psm_get_lang('config', 'tab_webhook'), 'label_tab_telegram' => psm_get_lang('config', 'tab_telegram'), 'label_tab_jabber' => psm_get_lang('config', 'tab_jabber'), + 'label_tab_auth' => psm_get_lang('config', 'tab_auth'), 'label_settings_email' => psm_get_lang('config', 'settings_email'), 'label_settings_sms' => psm_get_lang('config', 'settings_sms'), 'label_settings_discord' => psm_get_lang('config', 'settings_discord'), @@ -553,6 +589,7 @@ class ConfigController extends AbstractController 'label_settings_pushover' => psm_get_lang('config', 'settings_pushover'), 'label_settings_telegram' => psm_get_lang('config', 'settings_telegram'), 'label_settings_jabber' => psm_get_lang('config', 'settings_jabber'), + 'label_settings_dirauth' => psm_get_lang('config', 'settings_dirauth'), 'label_settings_notification' => psm_get_lang('config', 'settings_notification'), 'label_settings_log' => psm_get_lang('config', 'settings_log'), 'label_settings_proxy' => psm_get_lang('config', 'settings_proxy'), @@ -613,6 +650,36 @@ class ConfigController extends AbstractController 'label_jabber_domain_description' => psm_get_lang('config', 'jabber_domain_description'), 'label_jabber_password' => psm_get_lang('config', 'jabber_password'), 'label_jabber_password_description' => psm_get_lang('config', 'jabber_password_description'), + 'label_dirauth_status' => psm_get_lang('config', 'dirauth_status'), + 'label_authdir_host_locn' => psm_get_lang('config', 'authdir_host_locn'), + 'label_authdir_host_port' => psm_get_lang('config', 'authdir_host_port'), + 'label_authdir_type' => psm_get_lang('config', 'authdir_type'), + 'label_authdir_type_description' => psm_get_lang('config', 'authdir_type_description'), + 'label_authdir_userdomain' => psm_get_lang('config', 'authdir_userdomain'), + 'label_authdir_userdomain_description' => psm_get_lang('config', 'authdir_userdomain_description'), + 'label_authdir_ldapver' => psm_get_lang('config', 'authdir_ldapver'), + 'label_authdir_ldapver_description' => psm_get_lang('config', 'authdir_ldapver_description'), + 'label_authdir_ldapfollowref' => psm_get_lang('config', 'authdir_ldapfollowref'), + 'label_authdir_ldapfollowref_description' => psm_get_lang('config', 'authdir_ldapfollowref_description'), + 'label_authdir_basedn' => psm_get_lang('config', 'authdir_basedn'), + 'label_authdir_basedn_description' => psm_get_lang('config', 'authdir_basedn_description'), + 'label_authdir_usernameattrib' => psm_get_lang('config', 'authdir_usernameattrib'), + 'label_authdir_usernameattrib_description' => psm_get_lang('config', 'authdir_usernameattrib_description'), + 'label_authdir_groupnameattrib' => psm_get_lang('config', 'authdir_groupnameattrib'), + 'label_authdir_groupnameattrib_description' => + psm_get_lang('config', 'authdir_groupnameattrib_description'), + 'label_authdir_groupmemattrib' => psm_get_lang('config', 'authdir_groupmemattrib'), + 'label_authdir_groupmemattrib_description' => psm_get_lang('config', 'authdir_groupmemattrib_description'), + 'label_authdir_usercontainerrdn' => psm_get_lang('config', 'authdir_usercontainerrdn'), + 'label_authdir_usercontainerrdn_description' => + psm_get_lang('config', 'authdir_usercontainerrdn_description'), + 'label_authdir_groupcontainerrdn' => psm_get_lang('config', 'authdir_groupcontainerrdn'), + 'label_authdir_groupcontainerrdn_description' => + psm_get_lang('config', 'authdir_groupcontainerrdn_description'), + 'label_authdir_groupname' => psm_get_lang('config', 'authdir_groupname'), + 'label_authdir_groupname_description' => psm_get_lang('config', 'authdir_groupname_description'), + 'label_authdir_defaultrole' => psm_get_lang('config', 'authdir_defaultrole'), + 'label_authdir_defaultrole_description' => psm_get_lang('config', 'authdir_defaultrole_description'), 'label_alert_type' => psm_get_lang('config', 'alert_type'), 'label_alert_type_description' => psm_get_lang('config', 'alert_type_description'), 'label_combine_notifications' => psm_get_lang('config', 'combine_notifications'), diff --git a/src/psm/Module/Install/Controller/InstallController.php b/src/psm/Module/Install/Controller/InstallController.php index b1b9bc0b..7afd97f1 100644 --- a/src/psm/Module/Install/Controller/InstallController.php +++ b/src/psm/Module/Install/Controller/InstallController.php @@ -306,6 +306,7 @@ class InstallController extends AbstractController 'webhook_url' => '', 'webhook_json' => '', 'telegram_id' => '', + 'discord' => '', 'jabber' => '' ); diff --git a/src/psm/Service/User.php b/src/psm/Service/User.php index fce6213b..bc01db2a 100644 --- a/src/psm/Service/User.php +++ b/src/psm/Service/User.php @@ -230,20 +230,51 @@ class User { $user_name = trim($user_name); $user_password = trim($user_password); + $ldapauthstatus = false; if (empty($user_name) && empty($user_password)) { return false; } + + $dirauthconfig = psm_get_conf('dirauth_status'); + + // LDAP auth enabled + if ($dirauthconfig === '1') { + $ldaplibpath = realpath( + PSM_PATH_SRC . '..' . DIRECTORY_SEPARATOR . + 'vendor' . DIRECTORY_SEPARATOR . + 'viharm' . DIRECTORY_SEPARATOR . + 'psm-ldap-auth' . DIRECTORY_SEPARATOR . + 'psmldapauth.php' + ); + // If the library is found + if ($ldaplibpath) { + // Delegate the authentication to the PsmLDAPauth module. + // If LDAP auth fails or if library not found, fall back to native auth + include_once($ldaplibpath); + $ldapauthstatus = psmldapauth($user_name, $user_password, $GLOBALS['sm_config'], $this->db_connection); + } + } + $user = $this->getUserByUsername($user_name); - // using PHP 5.5's password_verify() function to check if the provided passwords - // fits to the hash of that user's password - if (!isset($user->user_id)) { - password_verify($user_password, 'dummy_call_against_timing'); - return false; - } elseif (!password_verify($user_password, $user->password)) { - return false; - } + // Authenticated + if ($ldapauthstatus === true) { + // Remove password to prevent it from being saved in the DB. + // Otherwise, user may still be authenticated if LDAP is disabled later. + $user_password = null; + @fn_Debug('Authenticated', $user); + } else { + + // using PHP 5.5's password_verify() function to check if the provided passwords + // fits to the hash of that user's password + if (!isset($user->user_id)) { + password_verify($user_password, 'dummy_call_against_timing'); + return false; + } elseif (!password_verify($user_password, $user->password)) { + return false; + } + } // not authenticated $this->setUserLoggedIn($user->user_id, true); diff --git a/src/templates/default/module/config/config.tpl.html b/src/templates/default/module/config/config.tpl.html index ffb36a54..db5ab029 100644 --- a/src/templates/default/module/config/config.tpl.html +++ b/src/templates/default/module/config/config.tpl.html @@ -7,6 +7,11 @@ role="tab" aria-controls="config-general" aria-selected="{% if general_active %}true{% else %}false{% endif %}">{{ label_general }} +