Files
wordpress-theme-aksrt/src/AKSRT/Auth/ServicePart1a.php
T
kate522 aaf458af1c refactor(autoload): migrate theme classes to Composer PSR-4
Move 288 types from global AKSRT_* + inc/classes manual requires into
namespace AKSRT\{Domain} under src/AKSRT/, wire composer.json psr-4,
and slim bootstrap to helpers/modules/hooks only. Split multi-class
widget/gateway files, update admin contract audit paths, and document
the layout in ADR/README.
2026-07-17 13:29:22 +08:00

482 lines
17 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* @package AKSRT
*/
namespace AKSRT\Auth;
defined('ABSPATH') || exit;
trait ServicePart1a {
public static function register_user(array $post, array $context = []): array {
// 先 reset pending invite context,避免同一进程内残留污染
self::$pending_invite_inviter_id = 0;
self::$pending_invite_code = '';
$user_login = sanitize_user(wp_unslash($post['user_login'] ?? ''), true);
$user_email = sanitize_email(wp_unslash($post['user_email'] ?? ''));
$password = (string) ($post['user_pass'] ?? '');
$confirm = (string) ($post['user_pass_confirm'] ?? '');
if (!wp_verify_nonce($post['_register_nonce'] ?? '', 'aksrt_register')) {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
if (empty($post['agree_terms'])) {
return ['success' => false, 'error' => __('请先同意服务条款', 'aksrt')];
}
if (empty($user_login) || !validate_username($user_login)) {
return ['success' => false, 'error' => __('请输入有效的用户名', 'aksrt')];
}
if (mb_strlen($user_login) < 3 || mb_strlen($user_login) > 60) {
return ['success' => false, 'error' => __('用户名长度需在 3-60 个字符之间', 'aksrt')];
}
if (username_exists($user_login)) {
return ['success' => false, 'error' => __('该用户名已被使用', 'aksrt')];
}
if (empty($user_email) || !is_email($user_email)) {
return ['success' => false, 'error' => __('请输入有效的邮箱地址', 'aksrt')];
}
if (email_exists($user_email)) {
return ['success' => false, 'error' => __('该邮箱已被其他账号使用', 'aksrt')];
}
$password_check = self::validate_password($password, $confirm);
if ($password_check !== true) {
return ['success' => false, 'error' => $password_check];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('register', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
// ====== 解析邀请码 ======
// 优先级:表单 invite_code(用户显式填写) > cookie aksrt_invite
$invite_registration_enabled = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::invite_registration_enabled();
$invite_mode = $invite_registration_enabled && class_exists('\AKSRT\Settings\Settings')
? \AKSRT\Settings\Settings::invite_register_field_mode()
: 'hidden';
$invite_code = isset($post['invite_code']) ? sanitize_key((string) wp_unslash($post['invite_code'])) : '';
$resolved_inviter_id = 0;
$invite_is_valid = false;
if (!$invite_registration_enabled) {
// 邀请功能关闭,忽略所有邀请码
$invite_code = '';
} elseif ($invite_code !== '') {
if (class_exists('\AKSRT\Credits\Invite')) {
$invite_service = \AKSRT\Credits\Invite::get_instance();
$resolved_inviter_id = $invite_service->resolve_code($invite_code);
$invite_is_valid = $invite_service->validate_code($invite_code);
}
// 必填模式下,邀请码无效时根据后台开关决定是否拒绝
$reject_invalid = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::invite_reject_invalid();
if ($invite_mode === 'required' && $reject_invalid && !$invite_is_valid) {
return ['success' => false, 'error' => __('邀请码无效,请检查后重试', 'aksrt')];
}
} elseif ($invite_mode === 'required') {
return ['success' => false, 'error' => __('请填写邀请码', 'aksrt')];
}
// 把解析到的 inviter_id 暂存到静态字段,user_register hook 内部读取(优先于 cookie)
self::$pending_invite_inviter_id = $resolved_inviter_id;
self::$pending_invite_code = $invite_code;
$user_id = wp_create_user($user_login, $password, $user_email);
if (is_wp_error($user_id)) {
self::$pending_invite_inviter_id = 0;
self::$pending_invite_code = '';
return ['success' => false, 'error' => $user_id->get_error_message()];
}
if ($invite_code !== '' && $resolved_inviter_id <= 0 && class_exists('\AKSRT\Credits\Invite')) {
\AKSRT\Credits\Invite::get_instance()->handle_registration_invite((int) $user_id, $invite_code);
}
// wp_create_user 内部已触发 user_register hook,\AKSRT\Credits\Invite::on_user_register 已读取 pending 字段。
// 这里清空,避免污染后续请求。
self::$pending_invite_inviter_id = 0;
self::$pending_invite_code = '';
$user = get_userdata($user_id);
if (!$user || is_wp_error($user)) {
return ['success' => false, 'error' => __('注册成功,但读取用户信息失败,请联系管理员', 'aksrt')];
}
$oauth_context = isset($context['oauth_bind_data']) && is_array($context['oauth_bind_data']) ? $context['oauth_bind_data'] : [];
$oauth_verified = self::oauth_email_is_verified($oauth_context);
$requires_verify = \AKSRT\Settings\Settings::registration_requires_email_verification() && !$oauth_verified;
if (!$requires_verify) {
self::mark_email_verified($user_id);
return [
'success' => true,
'user_id' => (int) $user_id,
'user' => $user,
'user_login' => $user_login,
];
}
$method = \AKSRT\Settings\Settings::registration_verification_method();
$begin = self::begin_email_verification(
$user,
$method,
[
'obk' => sanitize_text_field((string) ($context['obk'] ?? '')),
]
);
if (empty($begin['success'])) {
self::delete_user_account($user_id);
return $begin;
}
return [
'success' => true,
'user_id' => (int) $user_id,
'user' => $user,
'user_login' => $user_login,
'verification_required' => true,
'verification_method' => $method,
'verify_url' => self::get_email_verification_url((int) $user_id, ['mode' => $method]),
'message' => $begin['message'] ?? '',
];
}
public static function bind_oauth_account(array $post, array $oauth_data): array {
if (!wp_verify_nonce($post['_bind_nonce'] ?? '', 'aksrt_bind')) {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('bind', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
$creds = [
'user_login' => self::resolve_email_to_username((string) ($post['log'] ?? '')),
'user_password' => (string) ($post['pwd'] ?? ''),
'remember' => true,
];
$user = wp_signon($creds);
if (is_wp_error($user)) {
return ['success' => false, 'error' => self::friendly_login_error($user->get_error_message())];
}
if (self::user_requires_email_verification($user)) {
wp_logout();
return [
'success' => false,
'error' => __('该账号尚未完成邮箱验证,请先完成验证后再绑定第三方登录。', 'aksrt'),
'verify_url' => self::get_email_verification_url((int) $user->ID),
];
}
$provider = sanitize_key((string) ($oauth_data['provider'] ?? ''));
if ($provider === '') {
return ['success' => false, 'error' => __('绑定数据无效,请重新使用第三方登录', 'aksrt')];
}
$uid = (int) $user->ID;
update_user_meta($uid, "aksrt_{$provider}_id", $oauth_data['provider_id'] ?? '');
$bindings = get_user_meta($uid, 'aksrt_oauth_bindings', true) ?: [];
if (!is_array($bindings)) {
$bindings = [];
}
if (!in_array($provider, $bindings, true)) {
$bindings[] = $provider;
}
update_user_meta($uid, 'aksrt_oauth_bindings', $bindings);
if (!empty($oauth_data['user_data']['avatar_url'])) {
update_user_meta($uid, 'aksrt_oauth_avatar', esc_url_raw($oauth_data['user_data']['avatar_url']));
}
if (!empty($post['obk'])) {
delete_transient(sanitize_text_field((string) $post['obk']));
}
wp_set_auth_cookie($uid, true);
return [
'success' => true,
'user' => $user,
'provider' => $provider,
];
}
public static function finalize_oauth_registration(int $user_id, array $oauth_data, string $obk = ''): void {
if ($user_id <= 0) {
return;
}
$provider = sanitize_key((string) ($oauth_data['provider'] ?? ''));
if ($provider === '') {
return;
}
update_user_meta($user_id, "aksrt_{$provider}_id", $oauth_data['provider_id'] ?? '');
update_user_meta($user_id, 'aksrt_oauth_bindings', [$provider]);
if (!empty($oauth_data['user_data']['avatar_url'])) {
update_user_meta($user_id, 'aksrt_oauth_avatar', esc_url_raw($oauth_data['user_data']['avatar_url']));
}
if (self::oauth_email_is_verified($oauth_data)) {
EmailVerification::mark_email_verified($user_id);
}
if ($obk !== '') {
delete_transient(sanitize_text_field($obk));
}
wp_set_auth_cookie($user_id, true);
}
public static function get_email_verification_context(array $query): array {
$invalid_payload = [
'mode' => 'invalid',
'user' => null,
'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt'),
'method' => '',
'email' => '',
'uid' => 0,
'verified' => false,
'access_key' => '',
];
$user_id = isset($query['uid']) ? absint($query['uid']) : 0;
if ($user_id <= 0) {
return $invalid_payload;
}
$user = get_userdata($user_id);
if (!$user || is_wp_error($user)) {
return $invalid_payload;
}
$method = self::current_registration_verification_method();
$email = (string) get_user_meta($user_id, self::EMAIL_TARGET_META, true);
$token = sanitize_text_field((string) ($query['token'] ?? ''));
$access = sanitize_text_field((string) ($query['vk'] ?? ''));
// 容错:meta 缺失时(例如历史账号被强制要求验证),回退到设置默认值与 user_email
if (!in_array($method, ['link', 'code'], true)) {
$method = self::current_registration_verification_method();
}
if ($email === '' || !is_email($email)) {
$email = (string) $user->user_email;
}
$masked_email = self::masked_email($email);
$has_access = self::has_valid_email_access_key($user_id, $access);
// token 路径:链接模式直接走 token 校验;不依赖 vk
if ($token !== '') {
if (EmailVerification::is_email_verified($user_id)) {
return [
'mode' => 'invalid',
'user' => $user,
'error' => __('该验证链接已失效或已使用,请重新发起验证流程', 'aksrt'),
'method' => $has_access ? $method : '',
'email' => $has_access ? $masked_email : '',
'uid' => $has_access ? $user_id : 0,
'verified' => true,
'access_key' => $has_access ? $access : '',
];
}
$result = self::verify_email_token($user_id, $token);
if (!empty($result['success'])) {
return [
'mode' => 'completed',
'user' => $user,
'error' => '',
'method' => $method,
'email' => $masked_email,
'uid' => $user_id,
'verified' => true,
'redirect' => $result['redirect'] ?? home_url('/'),
'message' => $result['message'] ?? '',
'access_key' => $has_access ? $access : '',
];
}
return [
'mode' => 'invalid',
'user' => $user,
'error' => $result['error'] ?? __('验证链接无效或已过期', 'aksrt'),
'method' => $has_access ? $method : '',
'email' => $has_access ? $masked_email : '',
'uid' => $has_access ? $user_id : 0,
'verified' => false,
'access_key' => $has_access ? $access : '',
];
}
// 没有 token 也没有 vk:返回完全脱敏的通用错误,避免根据 uid 枚举账号状态
if (!$has_access) {
return $invalid_payload;
}
if (!self::user_requires_email_verification($user)) {
return [
'mode' => 'verified',
'user' => $user,
'error' => '',
'method' => '',
'email' => self::masked_email((string) $user->user_email),
'uid' => $user_id,
'verified' => true,
'access_key' => $access,
];
}
return [
'mode' => $method === 'code' ? 'code' : 'pending',
'user' => $user,
'error' => '',
'method' => $method,
'email' => $masked_email ?: self::masked_email((string) $user->user_email),
'uid' => $user_id,
'verified' => false,
'access_key' => $access,
];
}
public static function verify_email_code_submission(array $post): array {
if (!wp_verify_nonce($post['_verify_email_nonce'] ?? '', 'aksrt_verify_email')) {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
$user_id = isset($post['verify_uid']) ? absint($post['verify_uid']) : 0;
$access = sanitize_text_field((string) ($post['vk'] ?? ''));
$code = sanitize_text_field((string) ($post['email_code'] ?? ''));
if ($user_id <= 0 || $code === '') {
return ['success' => false, 'error' => __('请输入完整的邮箱验证码', 'aksrt')];
}
if (!self::has_valid_email_access_key($user_id, $access)) {
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('verify_email', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
return self::verify_email_code($user_id, $code);
}
public static function resend_email_verification(array $post): array {
if (!wp_verify_nonce($post['_verify_email_nonce'] ?? '', 'aksrt_verify_email')) {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
$user_id = isset($post['verify_uid']) ? absint($post['verify_uid']) : 0;
$access = sanitize_text_field((string) ($post['vk'] ?? ''));
// 必须先持有有效 vk 才允许重发,避免未登录用户用 uid 枚举触发邮件
if ($user_id <= 0 || !self::has_valid_email_access_key($user_id, $access)) {
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
}
// IP 节流:每 IP 每 RESEND_IP_WINDOW 秒最多 RESEND_IP_MAX 次(跨账号合并计数)
$ip = sanitize_text_field((string) ($_SERVER['REMOTE_ADDR'] ?? ''));
if ($ip !== '') {
$ip_key = 'aksrt_email_resend_ip_' . md5($ip);
$ip_attempts = (int) get_transient($ip_key);
if ($ip_attempts >= self::RESEND_IP_MAX) {
self::log_event('verify.resend.ip_throttled', ['uid' => $user_id, 'ip' => $ip]);
return ['success' => false, 'error' => __('操作过于频繁,请稍后再试', 'aksrt')];
}
set_transient($ip_key, $ip_attempts + 1, self::RESEND_IP_WINDOW);
}
$user = get_userdata($user_id);
if (!$user || is_wp_error($user)) {
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
}
if (!self::user_requires_email_verification($user)) {
return ['success' => false, 'error' => __('该账号已完成邮箱验证,无需重复发送', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('verify_email', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
$method = self::current_registration_verification_method();
$email = (string) get_user_meta($user_id, self::EMAIL_TARGET_META, true);
// 容错:meta 缺失时回退到设置默认值与 user_email(兼容历史账号 / meta 被外部清理的情况)
if (!in_array($method, ['link', 'code'], true)) {
$method = \AKSRT\Settings\Settings::registration_verification_method();
}
if ($email === '' || !is_email($email)) {
$email = (string) $user->user_email;
}
if ($email === '' || !is_email($email)) {
return ['success' => false, 'error' => __('当前账号缺少有效邮箱,请联系管理员', 'aksrt')];
}
$requested_at = (int) get_user_meta($user_id, self::EMAIL_REQUESTED_AT_META, true);
if ($requested_at > 0 && (time() - $requested_at) < 60) {
return ['success' => false, 'error' => __('操作过于频繁,请稍后再试', 'aksrt')];
}
return self::begin_email_verification($user, $method, [
'email' => $email,
'obk' => (string) get_user_meta($user_id, self::EMAIL_OBK_META, true),
'force' => true,
]);
}
private static function user_requires_email_verification($user): bool {
return EmailVerification::user_requires_email_verification($user);
}
private static function mark_email_verified(int $user_id): void {
EmailVerification::mark_email_verified($user_id);
}
// ============================================================
// 公开 API:管理员后台 / 内部业务调用(委托 EmailVerification)
// ============================================================
public static function user_is_email_verified(int $user_id): bool {
return EmailVerification::user_is_email_verified($user_id);
}
public static function get_user_verification_state(int $user_id): array {
return EmailVerification::get_user_verification_state($user_id);
}
public static function admin_mark_verified(int $user_id, int $by_admin_id = 0): array {
return EmailVerification::admin_mark_verified($user_id, $by_admin_id);
}
public static function admin_unmark_verified(int $user_id, int $by_admin_id = 0): array {
return EmailVerification::admin_unmark_verified($user_id, $by_admin_id);
}
}