优化了一些问题

This commit is contained in:
2026-08-29 22:03:48 +08:00
parent 1d904e6532
commit 780e1eba21
46 changed files with 4074 additions and 3143 deletions
+188 -10
View File
@@ -139,6 +139,12 @@ class SchemaProvider {
$clean[$key] = $this->sanitize_field_value($data[$key], $field);
}
}
if (!empty($data['__clear_secrets']) && is_array($data['__clear_secrets'])) {
$clean['__clear_secrets'] = array_values(array_intersect(
array_map('sanitize_key', $data['__clear_secrets']),
$this->secret_field_keys($schema)
));
}
return $clean;
}
@@ -266,6 +272,15 @@ class SchemaProvider {
}
}
if ($domain === 'users' && ($section === 'oauth' || $section === '')) {
$stored = get_option('aksrt_settings', []);
$merged = is_array($stored) ? array_merge($stored, $data) : $data;
foreach ((array) ($data['__clear_secrets'] ?? []) as $secret_key) {
$merged[sanitize_key((string) $secret_key)] = '';
}
$errors = array_merge($errors, $this->validate_oauth_settings($merged, is_array($stored) ? $stored : []));
}
if ($domain === 'payment' && $section === 'withdrawal') {
$stored = get_option('aksrt_settings', []);
$merged = is_array($stored) ? array_merge($stored, $data) : $data;
@@ -307,6 +322,132 @@ class SchemaProvider {
return ['valid' => empty($errors), 'errors' => $errors];
}
private function validate_oauth_settings(array $settings, array $stored = []): array {
if (empty($settings['oauth_s'])) {
return [];
}
$errors = [];
$mode = sanitize_key((string) ($settings['oauth_proxy_mode'] ?? 'close'));
$secret_configured = $this->oauth_secret_is_configured($settings['oauth_proxy_secret'] ?? '', $stored['oauth_proxy_secret'] ?? '');
if ($mode === 'client') {
$url = trim((string) ($settings['oauth_proxy_server_url'] ?? ''));
if (!$this->is_secure_oauth_url($url)) {
$errors[] = __('客户端代理模式需要有效的 HTTPS 服务端地址', 'aksrt');
}
if (!$secret_configured) {
$errors[] = __('客户端代理模式需要代理 HMAC 密钥', 'aksrt');
}
if (empty($settings['oauth_proxy_client_providers']) || !is_array($settings['oauth_proxy_client_providers'])) {
$errors[] = __('客户端代理模式至少需要启用一个登录平台', 'aksrt');
}
return $errors;
}
if ($mode === 'server') {
if (!$secret_configured) {
$errors[] = __('服务端代理模式需要代理 HMAC 密钥', 'aksrt');
}
$selected = is_array($settings['oauth_proxy_server_providers'] ?? null) ? $settings['oauth_proxy_server_providers'] : [];
if ($selected === []) {
$errors[] = __('服务端代理模式至少需要允许一个登录平台', 'aksrt');
}
$provider_fields = [
'github' => ['GitHub', 'github_appid', 'github_appkey'],
'gitee' => ['Gitee', 'gitee_appid', 'gitee_appkey'],
'qq' => ['QQ', 'qq_appid', 'qq_appkey'],
'google' => ['Google', 'google_client_id', 'google_client_secret'],
'weibo' => ['微博', 'weibo_appid', 'weibo_appkey'],
'weixin' => ['微信开放平台', 'weixin_appid', 'weixin_appsecret'],
];
foreach ($selected as $provider) {
if (!isset($provider_fields[$provider])) {
continue;
}
[$label, $id_key, $secret_key] = $provider_fields[$provider];
if (trim((string) ($settings[$id_key] ?? '')) === '' || !$this->oauth_secret_is_configured($settings[$secret_key] ?? '', $stored[$secret_key] ?? '')) {
$errors[] = sprintf(__('代理服务端的 %s 平台缺少完整凭据', 'aksrt'), $label);
}
}
return $errors;
}
$providers = [
'github' => ['GitHub', 'github_appid', 'github_appkey'],
'gitee' => ['Gitee', 'gitee_appid', 'gitee_appkey'],
'qq' => ['QQ', 'qq_appid', 'qq_appkey'],
'google' => ['Google', 'google_client_id', 'google_client_secret'],
'weibo' => ['微博', 'weibo_appid', 'weibo_appkey'],
'weixin' => ['微信开放平台', 'weixin_appid', 'weixin_appsecret'],
];
$enabled = 0;
foreach ($providers as $provider => [$label, $id_key, $secret_key]) {
if (empty($settings['oauth_' . $provider . '_s'])) {
continue;
}
$enabled++;
if (trim((string) ($settings[$id_key] ?? '')) === '' || !$this->oauth_secret_is_configured($settings[$secret_key] ?? '', $stored[$secret_key] ?? '')) {
$errors[] = sprintf(__('%s 登录已启用,请填写完整凭据', 'aksrt'), $label);
}
}
if (!empty($settings['oauth_weixingzh_s'])) {
$enabled++;
$gzh_type = sanitize_key((string) ($settings['weixingzh_gzh_type'] ?? 'service'));
$base_ready = $this->oauth_secret_is_configured($settings['weixingzh_token'] ?? '', $stored['weixingzh_token'] ?? '')
&& trim((string) ($settings['weixingzh_appid'] ?? '')) !== '';
$mode_ready = $gzh_type === 'subscribe'
? trim((string) ($settings['weixingzh_code_qrcode'] ?? '')) !== ''
&& trim((string) ($settings['weixingzh_code_keyword'] ?? '')) !== ''
: $this->oauth_secret_is_configured($settings['weixingzh_appsecret'] ?? '', $stored['weixingzh_appsecret'] ?? '');
if (!$base_ready || !$mode_ready) {
$errors[] = $gzh_type === 'subscribe'
? __('微信公众号订阅号登录已启用,请填写 Token、AppID、二维码和回复关键词', 'aksrt')
: __('微信公众号服务号登录已启用,请填写 Token、AppID 和 AppSecret', 'aksrt');
}
}
if (!empty($settings['clogin_s'])) {
$enabled++;
if (!$this->is_secure_oauth_url((string) ($settings['clogin_url'] ?? ''))
|| trim((string) ($settings['clogin_id'] ?? '')) === ''
|| !$this->oauth_secret_is_configured($settings['clogin_key'] ?? '', $stored['clogin_key'] ?? '')
|| empty($settings['clogin_oauth_s'])
) {
$errors[] = __('彩虹聚合登录已启用,请填写 HTTPS 接口地址、AppID、AppKey 并选择平台', 'aksrt');
}
}
if ($enabled === 0) {
$errors[] = __('启用第三方登录后,至少需要启用一个本地登录提供商', 'aksrt');
}
return $errors;
}
private function oauth_secret_is_configured($value, $stored_value = ''): bool {
$value = trim((string) $value);
if ($value === '***') {
return trim((string) $stored_value) !== '';
}
return $value !== '';
}
private function is_secure_oauth_url(string $url): bool {
$url = trim($url);
if ($url === '' || filter_var($url, FILTER_VALIDATE_URL) === false) {
return false;
}
$parts = wp_parse_url($url);
return is_array($parts)
&& strtolower((string) ($parts['scheme'] ?? '')) === 'https'
&& !empty($parts['host'])
&& empty($parts['user'])
&& empty($parts['pass'])
&& empty($parts['fragment']);
}
/**
* Top-level slugs that must never be shadowed by the download route.
*/
@@ -360,13 +501,22 @@ class SchemaProvider {
return $result;
}
public function reset_to_defaults(string $domain, string $section = ''): bool {
if ($section !== '') {
return $this->write_settings($domain, $this->get_defaults($domain, $section), $section);
}
public function reset_to_defaults(string $domain, string $section = ''): bool {
if ($section !== '') {
$defaults = $this->get_defaults($domain, $section);
$schema = $this->get_section_schema($domain, $section);
$defaults['__clear_secrets'] = $schema ? $this->secret_field_keys($schema) : [];
return $this->write_settings($domain, $defaults, $section);
}
return $this->write_settings($domain, $this->get_defaults($domain));
}
$result = true;
foreach ($this->get_sections($domain) as $section_schema) {
$defaults = $this->collect_defaults($section_schema);
$defaults['__clear_secrets'] = $this->secret_field_keys($section_schema);
$result = $this->write_section_settings($section_schema, $defaults) && $result;
}
return $result;
}
private function get_schema_builder_map(): array {
// Builders live under AKSRT\Admin\Schema (Composer PSR-4). Relative
@@ -395,6 +545,10 @@ class SchemaProvider {
if (array_key_exists($field['key'], $stored)) {
$data[$field['key']] = $stored[$field['key']];
}
$stored_value = array_key_exists($field['key'], $stored) ? $stored[$field['key']] : ($data[$field['key']] ?? '');
if (!empty($field['secret'])) {
$data[$field['key']] = trim((string) $stored_value) !== '' ? '***' : '';
}
}
// Captcha: always surface the normalized positive scene list.
@@ -421,13 +575,27 @@ class SchemaProvider {
foreach ($this->get_all_fields($schema) as $field) {
$fields[(string) $field['key']] = $field;
}
$clear_secrets = isset($data['__clear_secrets']) && is_array($data['__clear_secrets'])
? array_values(array_map('sanitize_key', $data['__clear_secrets']))
: [];
foreach ($fields as $key => $field) {
if (!array_key_exists($key, $data)) {
continue;
}
if (!empty($field['secret']) && in_array($key, $clear_secrets, true)) {
unset($stored[$key]);
continue;
}
if (!array_key_exists($key, $data)) {
continue;
}
if (!empty($field['secret'])) {
$value = is_scalar($data[$key]) ? trim((string) $data[$key]) : '';
if ($value === '' || $value === '***') {
continue;
}
}
$stored[$key] = $this->sanitize_field_value($data[$key], $field);
}
}
// Captcha stores a single positive scene list.
if ($option_key === 'aksrt_captcha_config' && function_exists('aksrt_captcha_scenes')) {
@@ -449,6 +617,16 @@ class SchemaProvider {
return $updated || true;
}
private function secret_field_keys(array $schema): array {
$keys = [];
foreach ($this->get_all_fields($schema) as $field) {
if (!empty($field['secret']) && !empty($field['key'])) {
$keys[] = sanitize_key((string) $field['key']);
}
}
return array_values(array_unique($keys));
}
private function collect_defaults(array $schema): array {
$defaults = [];
foreach ($this->get_all_fields($schema) as $field) {