Files
人事系统开发 1ba6efd8ed MRBS 1.12.2 等保2.0二级整改完整提交
包含:登录失败锁定、90天密码有效期、30分钟会话超时、
强制改密、登录审计日志、屏幕水印、企业背景图、
备案信息固定底部、favicon、JS空集合保护、
会话过期体验优化(403 JSON)、display_errors 关闭、
固定 key 根治 Integrity check failed 等全部改动

注意:config.inc.php/.htaccess/.user.ini 含敏感信息,
通过 .gitignore 排除,勿推送到公开仓库。
2026-09-09 16:55:02 +08:00

229 lines
5.8 KiB
PHP

<?php
namespace MRBS\Auth;
use MRBS\User;
/**
* A class for authenticating against a Wix system. It requires code to be installed in
* http-functions.js in the Wix backend. See wix/README for full details.
*
* It would be nice to be able to use Wix's OAuth2 server, but it seems to be limited to Wix apps.
*
* Another approach might be to use the Wix backend function getMember(), but this doesn't work inside
* http_functions as the URL endpoint for http_functions is not associated with a user session.
*/
class AuthWix extends Auth
{
public function validateUser(
#[\SensitiveParameter]
?string $user,
#[\SensitiveParameter]
?string $pass)
{
if (!isset($user) || !isset($pass))
{
return false;
}
$params = array(
'email' => $user,
'password' => $pass
);
$result = $this->http_functions('validateMember', $params);
if ($result === false)
{
// curl_exec failure: we'll return false anyway
return $result;
}
return (json_decode($result)) ? $user : false;
}
/**
*/
public function canValidateByEmail() : bool
{
return true;
}
/**
*/
public function canValidateByUsername() : bool
{
return false;
}
protected function getUserFresh(string $username) : ?User
{
global $auth;
$params = array('email' => $username);
$result = $this->http_functions('getMemberByEmail', $params);
// The username doesn't exist - return NULL
if (($result === false) || ($result === '') || ($result === json_encode(null)))
{
return null;
}
$result = json_decode($result);
$user = new User($username);
// Set the email address
$user->email = $result->member->loginEmail;
// Set the display name
$property = $auth['wix']['display_name_property'] ?? 'name';
if (isset($result->member->$property) && ($result->member->$property !== ''))
{
$user->display_name = $result->member->$property;
}
else
{
$user->display_name = $result->member->loginEmail;
}
// Set the level
// First get the default level. Any admins defined in the config
// file override settings in the external database.
$user->level = $this->getDefaultLevel($username);
// Then if they are not an admin get their admin status from Wix
if (($user->level < 2) && isset($result->badges) && in_array($auth['wix']['admin_badge'], $result->badges))
{
$user->level = 2;
}
return $user;
}
// Return an array of users, indexed by 'username' and 'display_name'
public function getUsernames() : array
{
global $auth;
$params = array();
if (isset($auth['wix']['display_name_property']))
{
$params['display_name_property'] = $auth['wix']['display_name_property'];
}
$result = $this->http_functions('getMemberNames', $params);
if ($result === false)
{
return array();
}
$users = json_decode($result, true);
self::sortUsers($users);
return $users;
}
private function http_functions(string $function, array $params)
{
global $auth, $server;
// Add a trailing '/' if necessary
if (!str_ends_with($auth['wix']['site_url'], '/'))
{
$auth['wix']['site_url'] .= '/';
}
// Get a user agent to keep the other end happy
if (isset($server['HTTP_USER_AGENT']) && ($server['HTTP_USER_AGENT'] !== ''))
{
$user_agent = $server['HTTP_USER_AGENT'];
}
else
{
$user_agent = 'PHP';
}
// Add in the API key
$params['key'] = $auth['wix']['mrbs_api_key'];
// And the API key secret name in Wix
$params['secret_name'] = $auth['wix']['mrbs_api_key_secret_name'];
// And the limit, for internal use by the Wix backend code
if (isset($auth['wix']['limit']))
{
$params['limit'] = $auth['wix']['limit'];
}
$url = $auth['wix']['site_url'] . "_functions/$function";
self::debug("URL=\"$url\"");
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Necessary to prevent "HTTP/2 stream 0 was not closed cleanly: INTERNAL_ERROR (err 2)" error;
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
// Necessary to prevent "OpenSSL SSL_read: Connection reset by peer, errno 104" error;
curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
// Use compression if we can
if (defined('CURLOPT_ENCODING'))
{
curl_setopt($ch, CURLOPT_ENCODING, '');
}
// Get some debug info in case there's an error
curl_setopt($ch, CURLOPT_VERBOSE, true);
$stream = fopen('php://temp', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $stream);
$result = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($result === false)
{
rewind($stream);
$message = curl_error($ch);
$message .= "\n\nCurl verbose log:\n\n" . stream_get_contents($stream);
trigger_error($message, E_USER_WARNING);
}
elseif ($http_code != 200)
{
trigger_error("Curl received HTTP response code $http_code: $result", E_USER_WARNING);
$result = false;
}
fclose($stream);
// curl_close() doesn't do anything from PHP 8.0 onwards (because the curl
// handle is an object and not a resource) and is deprecated from PHP 8.5.
assert(version_compare(MRBS_MIN_PHP_VERSION, '8.0.0', '<'), "The code below is now redundant.");
if (version_compare(PHP_VERSION, '8.0.0') < 0)
{
curl_close($ch);
}
return $result;
}
private static function debug(string $message) : void
{
global $auth;
if ($auth['wix']['debug'])
{
self::logDebugMessage($message);
}
}
}