mirror of
https://github.com/Cacti/cacti.git
synced 2026-10-23 03:27:02 +09:00
438 lines
15 KiB
PHP
438 lines
15 KiB
PHP
<?php
|
|
/*
|
|
+-------------------------------------------------------------------------+
|
|
| Copyright (C) 2004-2026 The Cacti Group |
|
|
| |
|
|
| This program is free software; you can redistribute it and/or |
|
|
| modify it under the terms of the GNU General Public License |
|
|
| as published by the Free Software Foundation; either version 2 |
|
|
| of the License, or (at your option) any later version. |
|
|
| |
|
|
| This program is distributed in the hope that it will be useful, |
|
|
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
| GNU General Public License for more details. |
|
|
+-------------------------------------------------------------------------+
|
|
| Cacti: The Complete RRDtool-based Graphing Solution |
|
|
+-------------------------------------------------------------------------+
|
|
| This code is designed, written, and maintained by the Cacti Group. See |
|
|
| about.php and/or the AUTHORS file for specific developer information. |
|
|
+-------------------------------------------------------------------------+
|
|
| http://www.cacti.net/ |
|
|
+-------------------------------------------------------------------------+
|
|
*/
|
|
|
|
use Cacti\Auth\BasicAuthLoginProvider;
|
|
use Cacti\Auth\LocalAuthLoginProvider;
|
|
|
|
// include ldap support
|
|
require_once(__DIR__ . '/lib/ldap.php');
|
|
|
|
// set default action
|
|
set_default_action();
|
|
|
|
/**
|
|
* get the username from the post variable
|
|
* For all but basic, this means that two post variables must be
|
|
* set. Additionally, for basic authentication verify the user,
|
|
* and if not valid generate a fatal error.
|
|
*/
|
|
$username = auth_get_username(); // Get the username from either basic auth or the login form
|
|
|
|
// initialize some variables
|
|
$user = []; // An array that will include all user details
|
|
$user_enabled = true; // A variable to let plugins know that the user is enabled
|
|
$guest_user = false; // Indicates the Guest account is being used
|
|
$realm = 0; // The compensated realm used for template and user validation
|
|
$frv_realm = gnrv('realm', 0); // The dropdown value for realm
|
|
$auth_method = read_config_option('auth_method'); // The authentication method for Cacti
|
|
$error = false; // Global variable, will be true if any errors occur
|
|
$error_msg = ''; // The errors message in case there was a login error
|
|
|
|
// global variables for exception handling
|
|
global $error, $error_msg;
|
|
|
|
if (gnrv('action') == 'login' || $auth_method == AUTH_METHOD_BASIC) {
|
|
if ($auth_method > AUTH_METHOD_BASIC && $frv_realm <= 1) {
|
|
// User picked 'local' from dropdown;
|
|
$auth_method = AUTH_METHOD_CACTI;
|
|
} else {
|
|
$auth_method = read_config_option('auth_method');
|
|
}
|
|
|
|
// Compensate as the dropdown for LDAP is off by one
|
|
if ($frv_realm == 2) {
|
|
$realm = 3;
|
|
} elseif ($auth_method == AUTH_METHOD_BASIC) {
|
|
$realm = $auth_method;
|
|
} else {
|
|
$realm = $frv_realm;
|
|
}
|
|
|
|
cacti_log("DEBUG: User '" . $username . "' attempting to login with realm " . $frv_realm . ', using method ' . $auth_method, false, 'AUTH', POLLER_VERBOSITY_DEBUG);
|
|
|
|
switch ($auth_method) {
|
|
case AUTH_METHOD_NONE: // No authentication, should not be reachable
|
|
$error = true;
|
|
$error_msg = __esc('Cacti no longer supports No Authentication mode. Please contact your System Administrator.');
|
|
cacti_log('FATAL: No authentication attempted and not supported.', false, 'AUTH');
|
|
|
|
auth_display_custom_error_message($error_msg);
|
|
|
|
exit;
|
|
case AUTH_METHOD_CACTI: // Local authentication
|
|
cacti_log("DEBUG: Local User '" . $username . "' to attempt login.", false, 'AUTH', POLLER_VERBOSITY_DEBUG);
|
|
|
|
$user = (new LocalAuthLoginProvider())->authenticate($username, gnrv('login_password'))->user ?? [];
|
|
|
|
break;
|
|
case AUTH_METHOD_BASIC: // Basic authentication
|
|
cacti_log("DEBUG: Basic Auth User '" . $username . "' attempting to login.", false, 'AUTH', POLLER_VERBOSITY_DEBUG);
|
|
|
|
$user = (new BasicAuthLoginProvider())->resolve($username)->user ?? [];
|
|
|
|
break;
|
|
case AUTH_METHOD_LDAP: // LDAP Authentication
|
|
case AUTH_METHOD_PROVIDERS: // Login Providers
|
|
cacti_log("DEBUG: Provider User '" . $username . "' to attempt login.", false, 'AUTH', POLLER_VERBOSITY_DEBUG);
|
|
|
|
$user = login_providers_login_process($username);
|
|
|
|
break;
|
|
default: // Login Realm not determined
|
|
$error = true;
|
|
$error_msg = __esc('Unable to determine user Login Realm or Domain. Please contact your System Administrator.');
|
|
|
|
cacti_log("LOGIN FAILED: User '" . $username . "' Unable to determine Login Realm. Exiting.", false, 'AUTH');
|
|
|
|
auth_display_custom_error_message($error_msg);
|
|
|
|
exit;
|
|
}
|
|
|
|
// Create user from template if available
|
|
if (!$error && !cacti_sizeof($user) && get_template_account($username) > 0 && $username != '') {
|
|
$user = auth_login_create_user_from_template($username, $realm);
|
|
}
|
|
|
|
// Guest account checking - Not for builtin
|
|
if (!$error && !cacti_sizeof($user) && get_guest_account() > 0) {
|
|
// Locate guest user record
|
|
$guestRow = db_fetch_row_prepared('SELECT *
|
|
FROM user_auth
|
|
WHERE id = ?',
|
|
[get_guest_account()]
|
|
);
|
|
|
|
$user = is_array($guestRow) ? $guestRow : [];
|
|
|
|
if ($user) {
|
|
cacti_log("LOGIN: Authenticated user '" . $username . "' using guest account '" . $user['username'] . "'", false, 'AUTH');
|
|
|
|
if ($username != '' && get_template_account($username) == 0) {
|
|
raise_message('template_disabled', __('User was Authenticated, but the Template Account is disabled. Using Guest Account'), MESSAGE_LEVEL_WARN);
|
|
}
|
|
|
|
$guest_user = true;
|
|
} else {
|
|
// error
|
|
$error = true;
|
|
$error_msg = __('Access Denied! Guest user id %s does not exist. Please contact your Administrator.', read_config_option('guest_user'));
|
|
|
|
cacti_log("LOGIN FAILED: Unable to locate guest user '" . read_config_option('guest_user') . "'", false, 'AUTH');
|
|
|
|
if ($auth_method == AUTH_METHOD_BASIC) {
|
|
auth_display_custom_error_message($error_msg);
|
|
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We have a valid user, do final checks, log their login attempt, and redirect as required
|
|
if (!$error && cacti_sizeof($user)) {
|
|
$client_addr = get_client_addr();
|
|
|
|
if (!$guest_user) {
|
|
cacti_log("LOGIN: User '" . $user['username'] . "' authenticated from IP Address '" . $client_addr . "'", false, 'AUTH');
|
|
} else {
|
|
cacti_log("LOGIN: Guest User '" . $user['username'] . "' in use from IP Address '" . $client_addr . "'", false, 'AUTH');
|
|
}
|
|
|
|
db_execute_prepared('INSERT IGNORE INTO user_log
|
|
(username, user_id, result, ip, time)
|
|
VALUES (?, ?, 1, ?, NOW())',
|
|
[$username, $user['id'], $client_addr]
|
|
);
|
|
|
|
// check if the user account is enabled with the exception of guest users
|
|
$user_enabled = true;
|
|
|
|
if (!$guest_user && isset($user['enabled'])) {
|
|
$user_enabled = ($user['enabled'] == 'on' ? true : false);
|
|
}
|
|
|
|
// check if the user is enabled
|
|
if (!$user_enabled) {
|
|
$error = true;
|
|
$error_msg = __('Access Denied! User account disabled.');
|
|
|
|
if ($auth_method == AUTH_METHOD_BASIC) {
|
|
auth_display_custom_error_message($error_msg);
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
if (!$error && !auth_user_has_access($user)) {
|
|
// error
|
|
$error = true;
|
|
$error_msg = __('You do not have access to any area of Cacti. Contact your administrator.');
|
|
|
|
cacti_log(sprintf('LOGIN FAILED: User %s with id %s does not have access to any area of Cacti.', $user['username'], $user['id']), false, 'AUTH');
|
|
|
|
if ($auth_method == AUTH_METHOD_BASIC) {
|
|
auth_display_custom_error_message($error_msg);
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
if (!$error && !cacti_auth_transition((int) $user['id'], 'login')) {
|
|
$error = true;
|
|
$error_msg = __('Access Denied! User account locked.');
|
|
}
|
|
|
|
// remember me support. Not for guest of basic auth. The transition
|
|
// gate must pass first so a locked or missing account cannot mint a token.
|
|
if ($auth_method != AUTH_METHOD_BASIC && $user['id'] !== get_guest_account()) {
|
|
if (!$error && isrv('remember_me') && read_config_option('auth_cache_enabled') == 'on' && auth_realm_allows_cookies((int) $realm)) {
|
|
set_auth_cookie($user);
|
|
}
|
|
}
|
|
|
|
if (!$error) {
|
|
// set the php session
|
|
$_SESSION[SESS_USER_ID] = $user['id'];
|
|
$_SESSION[SESS_USER_AGENT] = $_SERVER['HTTP_USER_AGENT'];
|
|
$_SESSION[SESS_CLIENT_ADDR] = get_client_addr();
|
|
|
|
// handle 'force change password'
|
|
if ($user['must_change_password'] == 'on' && $auth_method == AUTH_METHOD_CACTI && $user['password_change'] == 'on') {
|
|
$_SESSION[SESS_CHANGE_PASSWORD] = true;
|
|
}
|
|
|
|
$user = auth_apply_group_login_opts($user);
|
|
|
|
if (user_setting_exists('user_language', $_SESSION[SESS_USER_ID])) {
|
|
$_SESSION[SESS_USER_LANGUAGE] = read_user_setting('user_language');
|
|
}
|
|
|
|
cacti_log("DEBUG: User '" . $username . "' about to re-direct to preferred login page", false, 'AUTH', POLLER_VERBOSITY_DEBUG);
|
|
|
|
auth_login_redirect($user['login_opts']);
|
|
}
|
|
} else {
|
|
$id = db_fetch_cell_prepared('SELECT id
|
|
FROM user_auth
|
|
WHERE username = ?
|
|
AND realm = ?',
|
|
[$username, $frv_realm]
|
|
);
|
|
|
|
switch ($frv_realm) {
|
|
case '0':
|
|
case '1':
|
|
$realm_name = 'Local';
|
|
|
|
break;
|
|
case '2':
|
|
$realm_name = 'LDAP';
|
|
|
|
break;
|
|
default:
|
|
$realm_name = 'Domains LDAP';
|
|
|
|
break;
|
|
}
|
|
|
|
// BAD username/password builtin and LDAP
|
|
db_execute_prepared('INSERT IGNORE INTO user_log
|
|
(username, user_id, result, ip, time)
|
|
VALUES (?, ?, 0, ?, NOW())',
|
|
[$username, !empty($id) ? $id : 0, get_client_addr()]
|
|
);
|
|
|
|
cacti_log('LOGIN FAILED: ' . $realm_name . " login failed for user '" . $username . "' from IP address '" . get_client_addr() . "'.", false, 'AUTH');
|
|
|
|
// Single machine-parseable failure line for fail2ban. This is the universal
|
|
// choke point for a failed login across every realm, so it fires exactly once
|
|
// per attempt (auth_process_lockout() deliberately does not emit its own).
|
|
// $frv_realm is the dropdown value the $realm_name switch above keys on.
|
|
auth_log_failure($username, auth_realm_token((int) $frv_realm), !empty($id) ? 'bad_password' : 'no_such_user');
|
|
}
|
|
}
|
|
|
|
if (api_plugin_hook_function('custom_login', OPER_MODE_NATIVE) == OPER_MODE_RESKIN) {
|
|
return;
|
|
}
|
|
|
|
$selectedTheme = get_selected_theme();
|
|
|
|
html_auth_header(
|
|
'login',
|
|
__('Login to Cacti'),
|
|
__('User Login'),
|
|
__('Enter your Username and Password below'),
|
|
[
|
|
'error' => $error,
|
|
'error_msg' => $error_msg,
|
|
'username' => $username,
|
|
'user_enabled' => $user_enabled,
|
|
'action' => gnrv('action')
|
|
]
|
|
);
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<label for='login_username'><?php print __('Username'); ?></label>
|
|
</td>
|
|
<td>
|
|
<input type='text' class='ui-state-default ui-corner-all' id='login_username' name='login_username' size='15' maxlength='25' value='<?php print htmle($username); ?>' placeholder='<?php print __esc('Username'); ?>'>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<label for='login_password'><?php print __('Password'); ?></label>
|
|
</td>
|
|
<td>
|
|
<input type='password' autocomplete='current-password' class='ui-state-default ui-corner-all' id='login_password' name='login_password' size='15' maxlength='25' placeholder='********'>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
if (read_config_option('auth_method') == AUTH_METHOD_LDAP || read_config_option('auth_method') == AUTH_METHOD_PROVIDERS) {
|
|
$realms = get_auth_realms(true);
|
|
|
|
// try and remember previously selected realm
|
|
if ($frv_realm && array_key_exists($frv_realm, $realms)) {
|
|
foreach ($realms as $key => $realm) {
|
|
$realms[$key]['selected'] = ($frv_realm == $key);
|
|
}
|
|
}
|
|
?>
|
|
<tr>
|
|
<td>
|
|
<label for='realm'><?php print __('Realm'); ?></label>
|
|
</td>
|
|
<td>
|
|
<select id='realm' name='realm' class='ui-state-default ui-corner-all'>
|
|
<?php
|
|
if (cacti_sizeof($realms)) {
|
|
foreach ($realms as $index => $realm) {
|
|
print "\t\t\t\t\t<option value='" . $index . "'" . ($realm['selected'] ? ' selected="selected"' : '') . '>' . htmle($realm['name']) . "</option>\n";
|
|
}
|
|
}
|
|
?>
|
|
</select>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
}
|
|
|
|
$is_https = cacti_is_https();
|
|
|
|
if (read_config_option('auth_cache_enabled') == 'on' && $is_https) { ?>
|
|
<tr>
|
|
<td colspan='2'>
|
|
<input style='vertical-align:-3px;' type='checkbox' id='remember_me' name='remember_me' <?php print(isset($_COOKIE['cacti_remembers']) || !ierv('remember_me') ? 'checked' : ''); ?>>
|
|
<label for='remember_me'><?php print __('Keep me signed in'); ?></label>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
} ?>
|
|
<tr>
|
|
<td colspan='2'>
|
|
<button type='submit' class='ui-button ui-corner-all ui-widget ui-state-active' value='login'><?php print __esc('Login'); ?></button>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
$sso_providers = get_sso_login_providers();
|
|
|
|
if (cacti_sizeof($sso_providers)) {
|
|
foreach ($sso_providers as $sso_provider) { ?>
|
|
<tr>
|
|
<td colspan='2'>
|
|
<a class='ui-button ui-corner-all ui-widget sso-login-link' data-realm='<?php print $sso_provider['realm']; ?>' href='login_sso.php?action=login&realm=<?php print $sso_provider['realm']; ?>'><?php print __esc('Login with %s', $sso_provider['label']); ?></a>
|
|
</td>
|
|
</tr>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
$error_message = '';
|
|
|
|
if ($error_msg) {
|
|
$error_message = $error_msg;
|
|
} else {
|
|
if (gnrv('action') == 'login') {
|
|
$error_message = __('Invalid User Name/Password Please Retype');
|
|
}
|
|
|
|
if ($user_enabled == '0') {
|
|
$error_message = __('User Account Disabled');
|
|
}
|
|
}
|
|
|
|
$focus_control = (empty($username)) ? 'username' : 'password';
|
|
|
|
html_auth_footer('login', $error_message, "
|
|
<script>
|
|
var storage = Storages.localStorage;
|
|
|
|
$(function() {
|
|
$('body').css('height', $(window).height());
|
|
$('.cactiAuthLeft').css('width',parseInt($(window).width()*0.33)+'px');
|
|
$('.cactiAuthRight').css('width',parseInt($(window).width()*0.33)+'px');
|
|
$('#login_{$focus_control}').focus();
|
|
|
|
if (storage.isSet('user_realm')) {
|
|
var preferredRealm = storage.get('user_realm');
|
|
} else {
|
|
var preferredRealm = null;
|
|
}
|
|
|
|
if (preferredRealm == null) {
|
|
preferredRealm = $('#realm option:selected').val();
|
|
}
|
|
|
|
// Restore the preferred realm
|
|
if ($('#realm').length) {
|
|
if (preferredRealm !== null) {
|
|
$('#realm').val(preferredRealm);
|
|
if ($('#realm').selectmenu('instance') !== undefined) {
|
|
$('#realm').selectmenu('refresh');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Control submit in order to store preferred realm
|
|
$('#auth').submit(function(event) {
|
|
event.preventDefault();
|
|
if ($('#realm').length) {
|
|
storage.set('user_realm', $('#realm').val());
|
|
}
|
|
$('#auth').off('submit').trigger('submit');
|
|
});
|
|
|
|
// SSO buttons are plain links (they leave the page for the IdP), so the
|
|
// remember_me checkbox state has to be appended to the href by hand.
|
|
$('.sso-login-link').on('click', function() {
|
|
if ($('#remember_me').is(':checked')) {
|
|
this.href += '&remember_me=1';
|
|
}
|
|
});
|
|
|
|
});
|
|
</script>
|
|
", $error && $frv_realm <= 1);
|