PluginProbe
Metricool – Social media and site statistics / 2.0.2
Metricool – Social media and site statistics v2.0.2
2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.27 trunk
metricool / app / Features / Onboarding / Services / CreateAccountService.php

CreateAccountService.php in Metricool – Social media and site statistics 2.0.2, at app/Features/Onboarding/Services/CreateAccountService.php

138 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Metricool\Features\Onboarding\Services;
4
5 use GuzzleHttp\Exception\GuzzleException;
6 use Metricool\Features\Onboarding\Exceptions\CreateAccountException;
7 use Metricool\Features\Onboarding\OnboardingService;
8 use Metricool\Http\Metricool\MetricoolApi;
9 use Metricool\Http\RSPAL\RspalApiClient;
10 use Metricool\Services\DashboardService;
11 use Metricool\Support\Helpers\Storages\EnvironmentConfig;
12 use Throwable;
13
14 class CreateAccountService
15 {
16 private RspalApiClient $rspal;
17 private MetricoolApi $api;
18 private EnvironmentConfig $env;
19 private OnboardingService $onboarding;
20 private OAuthService $oauth;
21 private DashboardService $dashboard;
22
23 public function __construct(
24 RspalApiClient $rspal,
25 MetricoolApi $api,
26 EnvironmentConfig $env,
27 OnboardingService $onboarding,
28 OAuthService $oauth,
29 DashboardService $dashboard
30 ) {
31 $this->rspal = $rspal;
32 $this->api = $api;
33 $this->env = $env;
34 $this->onboarding = $onboarding;
35 $this->oauth = $oauth;
36 $this->dashboard = $dashboard;
37 }
38
39 /**
40 * Creates a new Metricool account and authenticate the MetricoolClient.
41 * @throws CreateAccountException
42 */
43 public function createAccount(string $captcha, string $email, string $password, bool $newsletters): bool
44 {
45 $globalError = sprintf(
46 /* translators: %1$s is opening link and %2$s is closing link */
47 __('Something went wrong. Please try again or %1$sleave a support message%2$s.', 'metricool'),
48 '<a href="' . $this->env->get('frontend.trusted_urls.new_support_ticket') . '" target="_blank">',
49 '</a>',
50 );
51
52 // First, check if the password is valid
53 if (!$this->isValidPassword($password)) {
54 throw new CreateAccountException(esc_html__('Password is not valid.', 'metricool'), 'Password is not valid', 422);
55 }
56
57 // Attempt to create the account
58 try {
59 $signupResponse = $this->rspal->signUp([
60 'username' => $email,
61 'newsletters' => $newsletters,
62 ], [
63 'RSPAL-RecaptchaV3Token' => $captcha
64 ]);
65 } catch (Throwable $e) {
66 throw new CreateAccountException(wp_kses_post($globalError), esc_html($e->getMessage()), 500);
67 }
68
69 // A 400 response means e-mail exists
70 if ($signupResponse->getStatusCode() == 400) {
71 throw new CreateAccountException(wp_kses_post($globalError), 'Email or password error', 422);
72 }
73
74 // Check if the response contains the required fields
75 if (empty($signupResponse->data->accessToken) || empty($signupResponse->data->refreshToken)) {
76 throw new CreateAccountException(wp_kses_post($globalError), 'Signup response is missing required fields', 500);
77 }
78
79 // Parse the user ID from the access token
80 $userId = $this->oauth->parseUserIdFromAccessToken($signupResponse->data->accessToken);
81 if (empty($userId)) {
82 throw new CreateAccountException(wp_kses_post($globalError), 'Failed to parse user ID from access token', 500);
83 }
84
85 // Authenticate the Metricool API Client
86 $this->api->authenticate(
87 $userId,
88 (string) $signupResponse->data->accessToken,
89 (string) $signupResponse->data->refreshToken,
90 $signupResponse->data->expires ?? 300
91 );
92
93 // Update the user's password
94 try {
95 $this->api->userCredentials()
96 ->updatePassword('', $password);
97 } catch (Throwable $e) {
98 $this->api->logout();
99
100 throw new CreateAccountException(wp_kses_post($globalError), esc_html($e->getMessage()), 500);
101 }
102
103 // Attempt to automatically set the blog information, complete the onboarding process on success
104 try {
105 $this->onboarding->finalizeOnboarding($this->findConnectedBrandId());
106 } catch (Throwable $e) {
107 throw new CreateAccountException(wp_kses_post($globalError), esc_html($e->getMessage()), 500);
108 }
109
110 $this->dashboard->setShowWelcomeScreen();
111
112 return true;
113 }
114
115
116 /**
117 * Get the blogId from the API. This is needed to connect the brand and complete the onboarding process.
118 * @throws GuzzleException
119 */
120 private function findConnectedBrandId(): ?string
121 {
122 $brands = $this->api->brands()->all();
123
124 // Get the brand when there is only one, abort if there are more
125 $brand = (count($brands) === 1 ? (array) $brands[0] : []);
126
127 return $brand['id'] ?? null;
128 }
129
130 /**
131 * Check if the password is: between 8 and 20 characters long, contains at least one uppercase letter, one lowercase letter, one number, and one special character
132 */
133 protected function isValidPassword(string $password): bool
134 {
135 return (bool) preg_match('/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{8,20}$/', $password);
136 }
137 }
138