Files
resourcespace/include/authenticate.php
2025-07-18 16:20:14 +07:00

322 lines
12 KiB
PHP
Executable File

<?php
include_once __DIR__ . '/login_functions.php';
debug("[authenticate.php] Reached authenticate page...");
# authenticate user based on cookie
$valid = true;
$autologgedout = false;
$nocookies = false;
$is_authenticated = false;
if (array_key_exists("user", $_COOKIE) || array_key_exists("user", $_GET) || isset($anonymous_login) || hook('provideusercredentials')) {
debug("[authenticate.php] Attempting to resolve user session...");
$username = "";
// Resolve anonymous login user if it is configured at domain level
if (isset($anonymous_login) && is_array($anonymous_login)) {
foreach ($anonymous_login as $key => $val) {
if ($baseurl == $key) {
$anonymous_login = $val;
}
}
}
// Establish session hash
$session_hash = "";
if (array_key_exists("user", $_GET)) {
$session_hash = $_GET["user"];
} elseif (array_key_exists("user", $_COOKIE)) {
$session_hash = $_COOKIE["user"];
} elseif (isset($anonymous_login)) {
$username = $anonymous_login;
$rs_session = get_rs_session_id(true);
// Always check the browser for anonymous access
browser_check();
}
if (!is_string($session_hash)) {
http_response_code(400);
exit();
}
// Automatic anonymous login, do not require session hash.
$user_select_sql = new PreparedStatementQuery();
if (isset($anonymous_login) && $username == $anonymous_login) {
$user_select_sql->sql = "u.username = ? AND usergroup IN (SELECT ref FROM usergroup)";
$user_select_sql->parameters = ["s",$username];
} else {
$user_select_sql->sql = "u.session=?";
$user_select_sql->parameters = ["s",$session_hash];
}
hook('provideusercredentials');
$userdata = validate_user($user_select_sql, true); // validate user and get user details
if (count($userdata) > 0) {
debug("[authenticate.php] User valid!");
$valid = true;
setup_user($userdata[0]);
if (
$password_expiry > 0
&& !checkperm("p")
&& $allow_password_change
&& in_array($pagename, ["user_change_password","index","collections","user_home"]) === false
&& strlen(trim((string) $userdata[0]["password_last_change"])) > 0
&& getval("modal", "") == ""
&& trim((string) $userdata[0]["origin"]) === "" // Don't force change if ResourceSpace doesn't manage the user's password
) {
# Redirect the user to the password change page if their password has expired.
$last_password_change = time() - strtotime((string) $userdata[0]["password_last_change"]);
if ($last_password_change > ($password_expiry * 60 * 60 * 24)) {
debug("[authenticate.php] Redirecting user to change password...");
?>
<script>
top.location.href="<?php echo $baseurl_short?>pages/user/user_change_password.php?expired=true";
</script>
<?php
}
}
if (
!isset($system_login)
&& strlen(trim((string)$userdata[0]["last_active"])) > 0
&& $userdata[0]["idle_seconds"] > ($session_length * 60)
) {
debug("[authenticate.php] Session length expired!");
# Last active more than $session_length mins ago?
$al = "";
if (isset($anonymous_login)) {
$al = $anonymous_login;
}
if ($session_autologout && $username != $al) { # If auto logout enabled, but this is not the anonymous user, log them out.
debug("[authenticate.php] Autologging out user.");
# Reached the end of valid session time, auto log out the user.
# Remove session
ps_query("update user set logged_in = 0, session = '' where ref= ?", array("i",$userref));
hook("removeuseridcookie");
# Blank cookie / var
rs_setcookie("user", "", -1, "", "", substr($baseurl, 0, 5) == "https", true);
rs_setcookie("user", "", -1, "/pages", "", substr($baseurl, 0, 5) == "https", true);
unset($username);
if (isset($anonymous_login)) {
# If the system is set up with anonymous access, redirect to the home page after logging out.
redirect("pages/home.php");
} else {
$valid = false;
$autologgedout = true;
}
} else {
# Session end reached, but the user may still remain logged in.
# This is a new 'session' for the purposes of statistics.
daily_stat("User session", $userref);
}
}
} else {
$valid = false;
}
} else {
$valid = false;
$nocookies = true;
# Set a cookie that we'll check for again on the login page after the redirection.
# If this cookie is missing, it's assumed that cookies are switched off or blocked and a warning message is displayed.
rs_setcookie('cookiecheck', 'true', 0, '/');
hook("removeuseridcookie");
}
if (!$valid && !isset($system_login)) {
debug("[authenticate.php] User not valid!");
$_SERVER['REQUEST_URI'] = ( isset($_SERVER['REQUEST_URI']) ?
$_SERVER['REQUEST_URI'] : $_SERVER['SCRIPT_NAME'] . ( isset($_SERVER
['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : ''));
$path = $_SERVER["REQUEST_URI"];
debug("[authenticate.php] path = $path");
if (strpos($path, "/ajax") !== false) {
if (isset($_COOKIE["user"])) {
http_response_code(401);
exit($lang['error-sessionexpired']);
} else {
http_response_code(403);
exit($lang['error-permissiondenied']);
}
}
$path = str_replace("ajax=", "ajax_disabled=", $path);# Disable forwarding of the AJAX parameter if this was an AJAX load, otherwise the redirected page will be missing the header/footer.
$redirparams = array();
$redirparams["url"] = isset($anonymous_login) ? "" : $path;
$redirparams["auto"] = $autologgedout ? "true" : "";
$redirparams["nocookies"] = $nocookies ? "true" : "";
if (strpos($path, "ajax") !== false || getval("ajax", "") != "") {
// Perform a javascript redirect as may be directly loading content directly into div.
$url = generateURL($baseurl . "/login.php", $redirparams);
?>
<script>
top.location.href="<?php echo $url ?>";
</script>
<?php
exit();
} else {
$url = generateURL($baseurl . "/login.php", $redirparams);
debug("[authenticate.php] Redirecting to $url");
redirect($url);
exit();
}
}
# Handle IP address restrictions
$ip = get_ip();
if (isset($ip_restrict_group)) {
$ip_restrict = $ip_restrict_group;
if ($ip_restrict_user != "") {
$ip_restrict = $ip_restrict_user;
} # User IP restriction overrides the group-wide setting.
if ($ip_restrict != "") {
$allow = false;
if (!hook('iprestrict')) {
$allow = ip_matches($ip, $ip_restrict);
}
if (!$allow) {
header("HTTP/1.0 403 Access Denied");
exit("Access denied.");
}
}
}
#update activity table
global $pagename;
/*
Login terms have not been accepted? Redirect until user does so
Note: it is considered safe to show the collection bar because even if we enable login terms
later on, when the user might have resources in it, they would not be able to do anything with them
unless they accept terms
*/
if ($terms_login && 0 == $useracceptedterms && in_array($pagename, array("reload_links","browsebar_js","css_override","category_tree_lazy_load","message","terms","collections","login","user_change_password", "user_home")) === false) {
redirect('pages/terms.php?noredir=true&url=' . urlencode("pages/home.php"));
}
if (isset($_SERVER["HTTP_USER_AGENT"])) {
$last_browser = substr($_SERVER["HTTP_USER_AGENT"], 0, 250);
} else {
$last_browser = "unknown";
}
// don't update this table if the System is doing its own operations
if (!isset($system_login)) {
update_user_access($userref, ["logged_in" => 1]);
}
# Add group specific text (if any) when logged in.
if (hook("replacesitetextloader")) {
# this hook expects $site_text to be modified and returned by the plugin
$site_text = hook("replacesitetextloader");
} else {
if (isset($usergroup)) {
// Fetch user group specific content.
$site_text_query = "
SELECT `name`,
`text`,
`page`
FROM site_text
WHERE language = ?
AND specific_to_group = ?
";
$parameters = array
(
"s",$language,
"i",$usergroup
);
if ($pagename != "admin_content") { // Load all content on the admin_content page to allow management.
$site_text_query .= "AND (page = ? OR page = 'all' OR page = '' " . (($pagename == "dash_tile") ? " OR page = 'home'" : "") . ")";
$parameters[] = "s";
$parameters[] = $pagename;
}
$results = ps_query($site_text_query, $parameters, "sitetext", -1, true, 0);
for ($n = 0; $n < count($results); $n++) {
if ($results[$n]['page'] == '') {
$lang[$results[$n]['name']] = $results[$n]['text'];
$customsitetext[$results[$n]['name']] = $results[$n]['text'];
} else {
$lang[$results[$n]['page'] . '__' . $results[$n]['name']] = $results[$n]['text'];
}
}
}
} /* end replacesitetextloader */
$GLOBALS['plugins'] = register_group_access_plugins($usergroup, $plugins ?? []);
// Load user config options
process_config_options(array('usergroup' => $usergroup));
process_config_options(array('user' => $userref));
// Once system wide/user preferences and user group config overrides have loaded, any config based dependencies should be checked and loaded.
if (!$disable_geocoding) {
include_once __DIR__ . '/map_functions.php';
}
hook('handleuserref', '', array($userref));
// Set a trace ID which can be used to correlate events within this request (requires $debug_extended_info)
$trace_id_components = [
getmypid(),
$_SERVER['REQUEST_TIME_FLOAT'],
$GLOBALS['pagename'], # already set in boot.php
http_build_query($_GET),
$GLOBALS['userref'],
];
$GLOBALS['debug_trace_id'] = generate_trace_id($trace_id_components);
debug(sprintf(
'User %s (ID %s) set its debug_trace_id to "%s" (components: %s)',
$GLOBALS['username'],
$GLOBALS['userref'],
$GLOBALS['debug_trace_id'],
json_encode($trace_id_components)
));
$is_authenticated = true;
// Check CSRF Token
$csrf_token = getval($CSRF_token_identifier, "");
if (
$_SERVER["REQUEST_METHOD"] === "POST"
&& !isValidCSRFToken($csrf_token, $usersession)
&& !(isset($anonymous_login) && $username == $anonymous_login)
&& !defined("API_CALL")
) {
http_response_code(400);
if (filter_var(getval("ajax", false), FILTER_VALIDATE_BOOLEAN)) {
include_once __DIR__ . "/ajax_functions.php";
$return['error'] = array(
'title' => $lang["error-csrf-verification"],
'detail' => $lang["error-csrf-verification-failed"]);
echo json_encode(array_merge($return, ajax_response_fail(ajax_build_message($lang["error-csrf-verification-failed"]))));
exit();
}
exit($lang["error-csrf-verification-failed"]);
} elseif (defined('API_CALL') && $_SERVER['REQUEST_METHOD'] === 'POST' && !isValidCSRFToken($csrf_token, $usersession)) {
ajax_send_response(
400,
ajax_response_fail(ajax_build_message("{$lang['error-csrf-verification']}: {$lang['error_invalid_input']}"))
);
}