PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 trunk All 48 releases
fluent-cart / app / Services / CustomerIdentity / EmailVerificationService.php

EmailVerificationService.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.6.5, at app/Services/CustomerIdentity/EmailVerificationService.php

106 lines 3.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Services\CustomerIdentity;
4
5 use FluentCart\Api\Resource\CustomerResource;
6 use FluentCart\App\Models\Customer;
7 use FluentCart\Framework\Support\Arr;
8
9 /** Tracks inbox proof independently from WordPress authentication. */
10 class EmailVerificationService
11 {
12 const META_KEY = '_fct_email_verification';
13
14 const PENDING_CLAIM_META_KEY = '_fct_email_claim';
15
16 /** Proof captured before WordPress consumes the reset key; never persisted. */
17 private static $passwordResetProof = [];
18
19 public static function capturePasswordResetProof($user): void
20 {
21 unset(static::$passwordResetProof[$user->ID]);
22 $cookie = Arr::get($_COOKIE, 'wp-resetpass-' . COOKIEHASH, '');
23 $postedKey = Arr::get($_POST, 'rp_key', '');
24 if (!is_string($cookie) || !is_string($postedKey) || !$postedKey) {
25 return;
26 }
27 $parts = explode(':', wp_unslash($cookie), 2);
28 if (count($parts) !== 2 || !hash_equals($parts[1], wp_unslash($postedKey))) {
29 return;
30 }
31 // Check the emailed key while it is still valid, not merely the reset hook.
32 $validated = check_password_reset_key($parts[1], $parts[0]);
33 if (is_wp_error($validated) || (int) $validated->ID !== (int) $user->ID) {
34 return;
35 }
36 static::$passwordResetProof[$user->ID] = [
37 'email' => $validated->user_email,
38 'password_hash' => $validated->user_pass,
39 ];
40 }
41
42 public static function verifyAfterPasswordReset($user): void
43 {
44 $proof = static::$passwordResetProof[$user->ID] ?? null;
45 unset(static::$passwordResetProof[$user->ID]);
46 if (!$proof) {
47 return;
48 }
49 clean_user_cache($user->ID);
50 $current = get_userdata($user->ID);
51 if (!$current || !static::isSame($proof['email'], $current->user_email)
52 || hash_equals($proof['password_hash'], $current->user_pass)) {
53 return;
54 }
55 EmailClaimService::confirmPasswordReset((int) $user->ID, $proof['email']);
56 }
57
58 public static function markPending(int $userId, string $email): void
59 {
60 update_user_meta($userId, static::META_KEY, [
61 'email' => static::normalize($email),
62 'verified' => false,
63 ]);
64 // Even changing away and back must invalidate the previous link.
65 delete_user_meta($userId, static::PENDING_CLAIM_META_KEY);
66 delete_user_meta($userId, CustomerRecoveryService::META_KEY);
67 CustomerResource::resetCurrentCustomerRuntimeCache();
68 }
69
70 public static function markVerified(int $userId, string $email): void
71 {
72 update_user_meta($userId, static::META_KEY, [
73 'email' => static::normalize($email),
74 'verified' => true,
75 ]);
76 CustomerResource::resetCurrentCustomerRuntimeCache();
77 }
78
79 public static function isRequired(int $userId): bool
80 {
81 $user = $userId ? get_userdata($userId) : false;
82 if (!$user) {
83 return true;
84 }
85
86 $state = get_user_meta($userId, static::META_KEY, true);
87 if (!is_array($state) || Arr::get($state, 'verified') !== true || !static::isSame(Arr::get($state, 'email', ''), $user->user_email)) {
88 return true;
89 }
90
91 // Check live customer data too: direct edits may bypass WordPress hooks.
92 $customer = Customer::query()->where('user_id', $userId)->orderBy('id', 'ASC')->first();
93 return $customer && !static::isSame($customer->email, $user->user_email);
94 }
95
96 public static function normalize($email): string
97 {
98 return strtolower(trim((string) $email));
99 }
100
101 public static function isSame($first, $second): bool
102 {
103 return static::normalize($first) === static::normalize($second);
104 }
105 }
106