PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.6.6
LatePoint – Calendar Booking Plugin for Appointments and Events v5.6.6
5.6.7 5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / helpers / auth_helper.php
latepoint / lib / helpers Last commit date
activities_helper.php 3 months ago agent_helper.php 3 months ago analytics_helper.php 1 week ago auth_helper.php 6 days ago blocks_helper.php 3 weeks ago booking_helper.php 4 days ago bricks_helper.php 3 months ago bundles_helper.php 4 days ago calendar_helper.php 3 days ago carts_helper.php 3 months ago connector_helper.php 3 months ago csv_helper.php 3 months ago customer_helper.php 1 month ago customer_import_helper.php 1 month ago database_helper.php 1 week ago debug_helper.php 3 months ago defaults_helper.php 3 months ago elementor_helper.php 3 months ago email_helper.php 3 months ago encrypt_helper.php 3 months ago events_helper.php 3 months ago form_helper.php 3 months ago icalendar_helper.php 3 months ago image_helper.php 3 months ago invoices_helper.php 3 weeks ago license_helper.php 3 months ago location_helper.php 3 months ago marketing_systems_helper.php 3 months ago meeting_systems_helper.php 3 months ago menu_helper.php 3 weeks ago meta_helper.php 3 months ago migrations_helper.php 3 months ago money_helper.php 3 months ago notifications_helper.php 3 months ago nps_survey_helper.php 3 months ago order_intent_helper.php 2 months ago orders_helper.php 3 months ago otp_helper.php 3 months ago pages_helper.php 3 months ago params_helper.php 3 months ago payments_helper.php 2 months ago plugin_version_update_helper.php 2 months ago price_breakdown_helper.php 3 months ago process_jobs_helper.php 3 months ago processes_helper.php 3 months ago razorpay_connect_helper.php 1 week ago replacer_helper.php 3 months ago resource_helper.php 4 days ago roles_helper.php 3 weeks ago router_helper.php 3 months ago service_helper.php 3 months ago sessions_helper.php 3 months ago settings_helper.php 1 week ago short_links_systems_helper.php 3 months ago shortcodes_helper.php 3 weeks ago sms_helper.php 3 months ago steps_helper.php 1 week ago stripe_connect_helper.php 1 week ago styles_helper.php 3 months ago support_topics_helper.php 3 months ago time_helper.php 2 months ago timeline_helper.php 2 months ago transaction_helper.php 1 month ago transaction_intent_helper.php 3 months ago util_helper.php 1 month ago version_specific_updates_helper.php 3 months ago whatsapp_helper.php 1 month ago work_periods_helper.php 3 months ago wp_datetime.php 3 months ago wp_user_helper.php 3 months ago
auth_helper.php
901 lines
1 <?php
2
3 class OsAuthHelper {
4
5 public static ?\LatePoint\Misc\User $current_user = null;
6 public static $logged_in_customer_id = false;
7
8 private static int $max_login_attempts_per_contact = 20;
9 private static int $max_login_attempts_per_ip = 20;
10 private static int $login_lockout_minutes = 5;
11
12 public static function reset_current_user() {
13 self::$current_user = null;
14 }
15
16 public static function set_current_user() {
17 if ( \OsWpUserHelper::is_user_logged_in() ) {
18 // if wp user is logged in - load from it
19 self::$current_user = \LatePoint\Misc\User::load_from_wp_user( \OsWpUserHelper::get_current_user() );
20 } else {
21 self::$current_user = new \LatePoint\Misc\User();
22 }
23 $customer = self::get_logged_in_customer();
24 if ( $customer ) {
25 self::$current_user->customer = $customer;
26 }
27 }
28
29 public static function get_current_user(): \LatePoint\Misc\User {
30 if ( ! self::$current_user ) {
31 self::set_current_user();
32 }
33
34 return self::$current_user;
35 }
36
37 public static function get_highest_current_user_id() {
38 $user_id = false;
39 switch ( self::get_highest_current_user_type() ) {
40 case LATEPOINT_USER_TYPE_ADMIN:
41 case LATEPOINT_USER_TYPE_CUSTOM:
42 $user_id = self::get_logged_in_wp_user_id();
43 break;
44 case LATEPOINT_USER_TYPE_AGENT:
45 $user_id = self::get_logged_in_agent_id();
46 break;
47 case LATEPOINT_USER_TYPE_CUSTOMER:
48 $user_id = self::get_logged_in_customer_id();
49 break;
50 }
51
52 return $user_id;
53 }
54
55 public static function get_admin_or_agent_avatar_url() {
56 $avatar_url = LATEPOINT_DEFAULT_AVATAR_URL;
57 if ( self::is_agent_logged_in() ) {
58 $agent = self::get_logged_in_agent();
59 $avatar_url = $agent->get_avatar_url();
60 } elseif ( self::get_logged_in_wp_user_id() ) {
61 $wp_user = self::get_logged_in_wp_user();
62 $avatar_url = get_avatar_url( $wp_user->user_email );
63 }
64
65 return $avatar_url;
66 }
67
68 public static function get_highest_current_user_type() {
69 // check if WP admin is logged in
70 $user_type = false;
71 if ( self::get_current_user()->backend_user_type ) {
72 // backend user, admin, agent or custom role
73 return self::get_current_user()->backend_user_type;
74 } elseif ( self::get_current_user()->customer ) {
75 // customer
76 return LATEPOINT_USER_TYPE_CUSTOMER;
77 }
78
79 return $user_type;
80 }
81
82
83 public static function login_wp_user( $user ) {
84 clean_user_cache( $user->ID );
85 wp_set_current_user( $user->ID );
86 wp_set_auth_cookie( $user->ID );
87 update_user_caches( $user );
88 }
89
90 public static function login_customer( $contact_value, $password, $contact_type = 'email' ) {
91 if ( empty( $contact_value ) || empty( $password ) || ! in_array( $contact_type, self::get_enabled_contact_types_for_customer_auth() ) ) {
92 return false;
93 }
94 $available_contact_types = OsAuthHelper::get_available_contact_types_for_customer_auth();
95 if ( ! isset( $available_contact_types[ $contact_type ] ) ) {
96 return false;
97 }
98
99 if ( ! self::check_login_rate_limit( $contact_value, $contact_type ) ) {
100 return false;
101 }
102
103 if ( self::can_wp_users_login_as_customers() ) {
104 // if WP users enabled - customers can only login using existing password of the wordpress account
105 $wp_user_id = false;
106 if ( $contact_type == 'email' ) {
107 $email = sanitize_email( $contact_value );
108 $wp_user_id = email_exists( $email );
109 } elseif ( $contact_type == 'phone' ) {
110 $phone = OsUtilHelper::sanitize_phone_number( $contact_value );
111 // find latepoint customer by phone because wp users don't have a phone field
112 $customer = new OsCustomerModel();
113 $customer = $customer->where( array( 'phone' => $phone ) )->set_limit( 1 )->get_results_as_models();
114 if ( $customer ) {
115 $email = $customer->email;
116 if ( $customer->wordpress_user_id ) {
117 $wp_user_id = $customer->wordpress_user_id;
118 } else {
119 $wp_user_id = email_exists( $customer->email );
120 }
121 }
122 }
123 if ( $wp_user_id && ! empty( $email ) && ! empty( $password ) ) {
124 $wp_user = wp_signon(
125 [
126 'user_login' => $email,
127 'user_password' => $password,
128 ]
129 );
130 if ( ! is_wp_error( $wp_user ) ) {
131 // successfully logged into wp user
132 // check if latepoint customer exists in db for this wp user
133 wp_set_current_user( $wp_user->ID );
134 $customer = OsCustomerHelper::get_customer_for_wp_user( $wp_user );
135 if ( $customer->id ) {
136 self::clear_login_rate_limits( $contact_value, $contact_type );
137 return $customer;
138 } else {
139 OsDebugHelper::log( 'Can not login because can not create LatePoint Customer from WP User', 'customer_login_error', $customer->get_error_messages() );
140
141 return false;
142 }
143 } else {
144 self::record_failed_login_attempt( $contact_value, $contact_type );
145 return false;
146 }
147 } else {
148 return false;
149 }
150 } else {
151 $customer = new OsCustomerModel();
152 if ( $contact_type == 'email' ) {
153 $email = sanitize_email( $contact_value );
154 $customer = $customer->where( array( 'email' => $email ) )->set_limit( 1 )->get_results_as_models();
155 } elseif ( $contact_type == 'phone' ) {
156 $phone = OsUtilHelper::sanitize_phone_number( $contact_value );
157 $customer = $customer->where( array( 'phone' => $phone ) )->set_limit( 1 )->get_results_as_models();
158 }
159 if ( $customer && OsAuthHelper::verify_password( $password, $customer->password ) ) {
160 OsAuthHelper::authorize_customer( $customer->id );
161 self::clear_login_rate_limits( $contact_value, $contact_type );
162
163 return $customer;
164 } else {
165 self::record_failed_login_attempt( $contact_value, $contact_type );
166 return false;
167 }
168 }
169 }
170
171 private static function check_login_rate_limit( string $contact_value, string $contact_type ): bool {
172 // Per-contact check
173 $contact_key = self::get_login_rate_limit_key( 'contact', $contact_value . '::' . $contact_type );
174 $attempts = absint( get_transient( $contact_key ) );
175 if ( $attempts >= self::$max_login_attempts_per_contact ) {
176 OsDebugHelper::log(
177 'Too many login attempts for contact',
178 'login_rate_limit',
179 [
180 'contact' => $contact_value,
181 'type' => $contact_type,
182 ]
183 );
184 return false;
185 }
186
187 // Per-IP check
188 $client_ip = OsUtilHelper::get_user_ip();
189 if ( ! empty( $client_ip ) && $client_ip !== 'n/a' ) {
190 $ip_key = self::get_login_rate_limit_key( 'ip', $client_ip );
191 $ip_attempts = absint( get_transient( $ip_key ) );
192 if ( $ip_attempts >= self::$max_login_attempts_per_ip ) {
193 OsDebugHelper::log( 'Too many login attempts from IP', 'login_rate_limit_ip', [ 'ip' => $client_ip ] );
194 return false;
195 }
196 }
197
198 return true;
199 }
200
201 private static function record_failed_login_attempt( string $contact_value, string $contact_type ): void {
202 $lockout_duration = self::$login_lockout_minutes * MINUTE_IN_SECONDS;
203 $client_ip = OsUtilHelper::get_user_ip();
204
205 $contact_key = self::get_login_rate_limit_key( 'contact', $contact_value . '::' . $contact_type );
206 $attempts = absint( get_transient( $contact_key ) );
207 set_transient( $contact_key, $attempts + 1, $lockout_duration );
208
209 if ( ! empty( $client_ip ) && $client_ip !== 'n/a' ) {
210 $ip_key = self::get_login_rate_limit_key( 'ip', $client_ip );
211 $ip_attempts = absint( get_transient( $ip_key ) );
212 set_transient( $ip_key, $ip_attempts + 1, $lockout_duration );
213 }
214 }
215
216 private static function clear_login_rate_limits( string $contact_value, string $contact_type ): void {
217 delete_transient( self::get_login_rate_limit_key( 'contact', $contact_value . '::' . $contact_type ) );
218
219 $client_ip = OsUtilHelper::get_user_ip();
220 if ( ! empty( $client_ip ) && $client_ip !== 'n/a' ) {
221 delete_transient( self::get_login_rate_limit_key( 'ip', $client_ip ) );
222 }
223 }
224
225 private static function get_login_rate_limit_key( string $type, string $identifier ): string {
226 return 'latepoint_login_' . $type . '_' . substr( wp_hash( $identifier ), 0, 20 );
227 }
228
229 public static function can_wp_users_login_as_customers(): bool {
230 return OsSettingsHelper::is_on( 'wp_users_as_customers', false );
231 }
232
233
234 // CUSTOMERS
235 // ---------------
236
237 public static function logout_customer() {
238 if ( self::can_wp_users_login_as_customers() ) {
239 wp_logout();
240 } else {
241 OsSessionsHelper::destroy_customer_session_cookie();
242 }
243 }
244
245 public static function authorize_customer( $customer_id ): bool {
246 $customer = new OsCustomerModel();
247 $customer = $customer->where( [ 'id' => $customer_id ] )->set_limit( 1 )->get_results_as_models();
248
249 if ( empty( $customer ) ) {
250 OsDebugHelper::log(
251 'Tried to authorize customer with invalid ID',
252 'customer_authorization',
253 [ 'customer_id' => $customer_id ]
254 );
255
256 return false;
257 }
258
259 if ( self::can_wp_users_login_as_customers() ) {
260
261 if ( $customer->wordpress_user_id ) {
262 $wp_user = get_user_by( 'id', $customer->wordpress_user_id );
263 // check if WP User exists, if not - create new one and get ID, otherwise get ID from customer record, since its valid
264 $wordpress_user_id = ( $wp_user ) ? $customer->wordpress_user_id : OsCustomerHelper::create_wp_user_for_customer( $customer );
265 } else {
266 $wordpress_user_id = OsCustomerHelper::create_wp_user_for_customer( $customer );
267 }
268
269 if ( $wordpress_user_id ) {
270 $wp_user = get_user_by( 'id', $wordpress_user_id );
271 if ( $wp_user ) {
272 // Do not log a requester into a privileged WP account via the customer
273 // auth flow (OTP or social login). Mirror the guard used on the password and
274 // account-linking paths to prevent privilege escalation if a customer record's
275 // email has been overwritten to an attacker-controlled address.
276 if ( ! OsCustomerHelper::is_wp_user_safe_for_customer_link( (int) $wp_user->ID ) ) {
277 OsDebugHelper::log(
278 'Blocked customer authorization into a privileged WP user',
279 'customer_authorization',
280 [
281 'customer_id' => $customer_id,
282 'wordpress_user_id' => $wp_user->ID,
283 ]
284 );
285
286 return false;
287 }
288 self::login_wp_user( $wp_user );
289 } else {
290 OsDebugHelper::log(
291 'WordPress user ID for customer is not found or can not be created.',
292 'customer_create_error',
293 [
294 'customer_id' => $customer_id,
295 'wordpress_user_id' => $wordpress_user_id,
296 ]
297 );
298
299 return false;
300 }
301 } else {
302 OsDebugHelper::log(
303 'WordPress user ID for customer is not found or can not be created.',
304 'customer_create_error',
305 [
306 'customer_id' => $customer_id,
307 'wordpress_user_id' => $wordpress_user_id,
308 ]
309 );
310
311 return false;
312 }
313 }
314 OsSessionsHelper::start_or_use_session_for_customer( $customer_id );
315 OsStepsHelper::$customer_object = $customer;
316
317 return true;
318 }
319
320 public static function get_logged_in_customer_uuid() {
321 $customer = self::get_logged_in_customer();
322 if ( $customer ) {
323 return $customer->get_uuid();
324 } else {
325 return false;
326 }
327 }
328
329 public static function get_logged_in_customer_id() {
330 if ( OsAuthHelper::is_customer_auth_disabled() ) {
331 return false;
332 }
333 if ( self::can_wp_users_login_as_customers() ) {
334 // using wp users as customers
335 if ( OsWpUserHelper::is_user_logged_in() ) {
336 $wp_user = wp_get_current_user();
337 // search connected latepoint customer
338 $customer = OsCustomerHelper::get_customer_for_wp_user( $wp_user );
339 if ( $customer->id ) {
340 return $customer->id;
341 } else {
342 OsDebugHelper::log( 'Can not create LatePoint Customer from WP User', 'customer_create_error', $customer->get_error_messages() );
343
344 return false;
345 }
346 } else {
347 return false;
348 }
349 } else {
350 if ( self::$logged_in_customer_id ) {
351 return self::$logged_in_customer_id;
352 }
353 $customer_id = OsSessionsHelper::get_customer_id_from_session();
354 // make sure customer with this ID exists in database
355 $customer = new OsCustomerModel( $customer_id );
356 if ( ! $customer->is_new_record() ) {
357 self::$logged_in_customer_id = $customer_id;
358
359 return self::$logged_in_customer_id;
360 } else {
361 // customer not found, destroy this invalid customer ID in session cookie
362 OsSessionsHelper::destroy_customer_session_cookie();
363
364 return false;
365 }
366 }
367 }
368
369 public static function is_customer_logged_in() {
370 return self::get_logged_in_customer_id();
371 }
372
373 public static function get_logged_in_customer() {
374 $customer = false;
375 if ( self::is_customer_logged_in() ) {
376 $customer = new OsCustomerModel( self::get_logged_in_customer_id() );
377 if ( $customer->is_new_record() ) {
378 $customer = false;
379 }
380 }
381
382 return $customer;
383 }
384
385
386 // AGENTS
387 // -------------
388
389 public static function get_logged_in_agent_id() {
390 $agent_id = false;
391 if ( self::is_agent_logged_in() ) {
392 if ( self::get_current_user()->agent && self::get_current_user()->agent->id ) {
393 $agent_id = self::get_current_user()->agent->id;
394 }
395 }
396
397 return $agent_id;
398 }
399
400 public static function is_agent_logged_in() {
401 return ( self::get_current_user()->backend_user_type == LATEPOINT_USER_TYPE_AGENT );
402 }
403
404 public static function get_logged_in_agent() {
405 $agent = false;
406 if ( self::is_agent_logged_in() ) {
407 $agent = new OsAgentModel();
408 $agent = $agent->where( [ 'wp_user_id' => self::get_logged_in_wp_user_id() ] )->set_limit( 1 )->get_results_as_models();
409 }
410
411 return $agent;
412 }
413
414
415 public static function is_custom_backend_user_logged_in() {
416 return ( self::get_current_user()->backend_user_type == LATEPOINT_USER_TYPE_CUSTOM );
417 }
418
419 public static function is_admin_logged_in() {
420 return ( self::get_current_user()->backend_user_type == LATEPOINT_USER_TYPE_ADMIN );
421 }
422
423 public static function get_logged_in_admin_user() {
424 $admin_user = false;
425 if ( self::is_admin_logged_in() ) {
426 $admin_user = self::get_logged_in_wp_user();
427 }
428
429 return $admin_user;
430 }
431
432 public static function get_logged_in_admin_user_id() {
433 $admin_id = false;
434 if ( self::is_admin_logged_in() ) {
435 $admin_id = self::get_logged_in_wp_user_id();
436 }
437
438 return $admin_id;
439 }
440
441 public static function get_logged_in_custom_user_id() {
442 $admin_id = false;
443 if ( self::is_custom_backend_user_logged_in() ) {
444 $admin_id = self::get_logged_in_wp_user_id();
445 }
446
447 return $admin_id;
448 }
449
450
451 public static function get_default_customer_authentication_method(): string {
452 $method = OsSettingsHelper::get_settings_value( 'default_customer_authentication_method', 'password' );
453
454 $enabled_auth_methods = self::get_enabled_customer_authentication_methods();
455 if ( empty( $enabled_auth_methods ) ) {
456 $method = '';
457 } elseif ( count( $enabled_auth_methods ) == 1 ) {
458 $method = $enabled_auth_methods[0];
459 } else {
460 $method = in_array( $method, $enabled_auth_methods ) ? $method : reset( $enabled_auth_methods );
461 }
462
463 /**
464 * Default auth method
465 *
466 * @param {string} $method default auth method value
467 * @returns {string} The filtered auth method value
468 *
469 * @since 5.2.0
470 * @hook latepoint_default_contact_type_for_customer_auth
471 *
472 */
473 return apply_filters( 'latepoint_default_contact_type_for_customer_auth', $method );
474 }
475
476 public static function get_available_customer_authentication_methods(): array {
477 $methods = [
478 'password' => __( 'Password', 'latepoint' ),
479 'otp' => __( 'One-time code', 'latepoint' ),
480 ];
481
482 /**
483 * Enabled auth method
484 *
485 * @param {array} $methods available auth methods
486 * @returns {array} The filtered array of available auth methods
487 *
488 * @since 5.2.0
489 * @hook latepoint_get_available_customer_authentication_methods
490 *
491 */
492 return apply_filters( 'latepoint_get_available_customer_authentication_methods', $methods );
493 }
494
495
496 public static function get_enabled_customer_authentication_methods(): array {
497 $selected_method = OsSettingsHelper::get_settings_value( 'selected_customer_authentication_method', 'password' );
498
499 switch ( $selected_method ) {
500 case 'password':
501 $auth_methods = [ 'password' ];
502 break;
503 case 'otp':
504 $auth_methods = [ 'otp' ];
505 break;
506 case 'password_or_otp':
507 $auth_methods = [ 'password', 'otp' ];
508 break;
509 default:
510 $auth_methods = [ 'password' ];
511 }
512
513 /**
514 * Enabled auth method
515 *
516 * @param {array} $auth_methods enabled auth methods
517 * @returns {array} The filtered array of enabled auth methods
518 *
519 * @since 5.2.0
520 * @hook latepoint_get_enabled_customer_authentication_methods
521 *
522 */
523 return apply_filters( 'latepoint_get_enabled_customer_authentication_methods', $auth_methods );
524 }
525
526
527 public static function get_selected_customer_authentication_method(): string {
528 $via = OsSettingsHelper::get_settings_value( 'selected_customer_authentication_method', 'password' );
529 if ( in_array( $via, self::get_enabled_customer_authentication_methods( true ) ) ) {
530 return $via;
531 } else {
532 OsSettingsHelper::save_setting_by_name( 'selected_customer_authentication_method', 'password' );
533
534 return 'password';
535 }
536 }
537
538 public static function get_customer_authentication_method_options( $keys_only = false ): array {
539 $options = [
540 'password' => __( 'Password', 'latepoint' ),
541 'otp' => __( 'One-time code', 'latepoint' ),
542 ];
543
544 return $keys_only ? array_keys( $options ) : $options;
545 }
546
547
548 public static function get_default_contact_type_for_customer_auth(): string {
549 $method = OsSettingsHelper::get_settings_value( 'default_contact_type_for_customer_auth', 'email' );
550
551 $enabled_contact_types = self::get_enabled_contact_types_for_customer_auth();
552 if ( empty( $enabled_contact_types ) ) {
553 $method = '';
554 } elseif ( count( $enabled_contact_types ) == 1 ) {
555 $method = $enabled_contact_types[0];
556 } else {
557 $method = in_array( $method, $enabled_contact_types ) ? $method : reset( $enabled_contact_types );
558 }
559
560 /**
561 * Default auth method
562 *
563 * @param {string} $method default auth method value
564 * @returns {string} The filtered auth method value
565 *
566 * @since 5.2.0
567 * @hook latepoint_default_contact_type_for_customer_auth
568 *
569 */
570 return apply_filters( 'latepoint_default_contact_type_for_customer_auth', $method );
571 }
572
573 public static function get_available_contact_types_for_customer_auth(): array {
574 $contact_types = [
575 'email' => __( 'Email Address', 'latepoint' ),
576 'phone' => __( 'Phone Number', 'latepoint' ),
577 ];
578
579 /**
580 * Enabled auth method
581 *
582 * @param {array} $contact_types available contact types
583 * @returns {array} The filtered array of available contact types
584 *
585 * @since 5.2.0
586 * @hook latepoint_get_available_contact_types_for_customer_auth
587 *
588 */
589 return apply_filters( 'latepoint_get_available_contact_types_for_customer_auth', $contact_types );
590 }
591
592 public static function get_enabled_contact_types_for_customer_auth(): array {
593 $customer_authentication_field_type = OsSettingsHelper::get_settings_value( 'selected_customer_authentication_field_type', 'email' );
594
595 switch ( $customer_authentication_field_type ) {
596 case 'email':
597 $contact_types = [ 'email' ];
598 break;
599 case 'phone':
600 $contact_types = [ 'phone' ];
601 break;
602 case 'email_or_phone':
603 $contact_types = [ 'email', 'phone' ];
604 break;
605 case 'disabled':
606 $contact_types = [];
607 break;
608 default:
609 $contact_types = [ 'email' ];
610 }
611
612 /**
613 * Enabled auth method
614 *
615 * @param {array} $contact_types enabled contact types
616 * @returns {array} The filtered array of enabled contact types
617 *
618 * @since 5.2.0
619 * @hook latepoint_get_enabled_contact_types_for_customer_auth
620 *
621 */
622 return apply_filters( 'latepoint_get_enabled_contact_types_for_customer_auth', $contact_types );
623 }
624
625 public static function is_customer_auth_enabled(): bool {
626 return ( self::get_selected_customer_authentication_field_type() != 'disabled' );
627 }
628
629 public static function is_customer_auth_disabled(): bool {
630 return ! self::is_customer_auth_enabled();
631 }
632
633 public static function count_total_customers_violating_auth_rules(): array {
634
635 $violators = [
636 'field' => '',
637 'values' => [],
638 ];
639 $field = '';
640
641 if ( OsAuthHelper::is_customer_auth_enabled() ) {
642 // auth enabled
643 $auth_field = OsAuthHelper::get_selected_customer_authentication_field_type();
644 if ( $auth_field == 'email' ) {
645 $field = 'email';
646 }
647 if ( $auth_field == 'phone' ) {
648 $field = 'phone';
649 }
650 } else {
651 // auth disabled
652 $merge_data = OsSettingsHelper::get_settings_value( 'default_contact_merge_behavior', 'email' );
653 if ( $merge_data == 'email' ) {
654 $field = 'email';
655 }
656 if ( $merge_data == 'phone' ) {
657 $field = 'phone';
658 }
659 }
660
661 if ( $field ) {
662 $customers_model = new OsCustomerModel();
663 $result = $customers_model->select( $field )
664 ->where( [ $field . ' !=' => '' ] )
665 ->group_by( $field )
666 ->having( 'COUNT(*) > 1' )
667 ->get_results( ARRAY_A );
668 $violators['values'] = ( array_column( $result, $field ) );
669 $violators['field'] = $field;
670 }
671
672 return $violators;
673 }
674
675 public static function get_selected_customer_authentication_field_type(): string {
676 $via = OsSettingsHelper::get_settings_value( 'selected_customer_authentication_field_type' );
677 if ( empty( $via ) ) {
678 // load from legacy setting
679 if ( OsSettingsHelper::is_on( 'steps_hide_login_register_tabs' ) ) {
680 OsSettingsHelper::save_setting_by_name( 'selected_customer_authentication_field_type', 'disabled' );
681
682 return 'disabled';
683 } else {
684 OsSettingsHelper::save_setting_by_name( 'selected_customer_authentication_field_type', 'email' );
685
686 return 'email';
687 }
688 }
689 if ( in_array( $via, self::get_customer_authentication_field_type_options( true ) ) ) {
690 return $via;
691 } else {
692 OsSettingsHelper::save_setting_by_name( 'selected_customer_authentication_field_type', 'email' );
693
694 return 'email';
695 }
696 }
697
698 public static function get_customer_authentication_field_type_options( $keys_only = false ): array {
699 $options = [
700 'email' => __( 'Email Address', 'latepoint' ),
701 'phone' => __( 'Phone Number', 'latepoint' ),
702 'disabled' => __( 'Disable ability to login', 'latepoint' ),
703 ];
704
705 return $keys_only ? array_keys( $options ) : $options;
706 }
707
708
709 public static function is_classic_auth_flow(): bool {
710 return ( OsSettingsHelper::get_settings_value( 'modern_auth_flow_for_customers', LATEPOINT_VALUE_OFF ) == LATEPOINT_VALUE_OFF );
711 }
712
713 public static function is_otp_auth_enabled(): bool {
714 return in_array( 'otp', self::get_enabled_customer_authentication_methods() );
715 }
716
717
718 public static function get_default_delivery_method_for_customer_auth_contact_type( string $contact_type ) {
719 $delivery_method = 'email';
720 switch ( $contact_type ) {
721 case 'email':
722 $delivery_method = 'email';
723 break;
724 case 'phone':
725 $delivery_method = 'sms';
726 break;
727 }
728
729 /**
730 * Get default delivery method for auth contact type
731 *
732 * @param {string} $delivery_method delivery method (email, sms, whatsapp)
733 * @param {string} $contact_type contact type selected for auth (email, phone)
734 *
735 * @returns {string} Filtered delivery method
736 * @since 5.2.0
737 * @hook latepoint_get_default_delivery_method_for_customer_auth
738 *
739 */
740 return apply_filters( 'latepoint_get_default_delivery_method_for_customer_auth', $delivery_method, $contact_type );
741 }
742
743 public static function auth_form_html( $is_classic, OsCustomerModel $customer, string $selected_contact_type_for_auth = '', $selected_delivery_method = '' ): string {
744 if ( empty( $selected_contact_type_for_auth ) ) {
745 $selected_contact_type_for_auth = OsAuthHelper::get_default_contact_type_for_customer_auth();
746 }
747 if ( empty( $selected_delivery_method ) ) {
748 $selected_delivery_method = self::get_default_delivery_method_for_customer_auth_contact_type( $selected_contact_type_for_auth );
749 }
750 $enabled_contact_types_for_customer_auth = OsAuthHelper::get_enabled_contact_types_for_customer_auth();
751 $otp_allowed = self::is_otp_auth_enabled();
752 $multiple_auth_methods_enabled = ( count( self::get_enabled_customer_authentication_methods() ) > 1 );
753 $html = '';
754 if ( ! $is_classic ) {
755 $html .= '<div class="latepoint-customer-auth-wrapper">';
756 $html .= '<div class="latepoint-customer-otp-request-wrapper hide-when-entering-otp">';
757 // NEW STYLE SHOWING JUST THE AUTH OPTIONS FIRST
758 if ( in_array( 'email', $enabled_contact_types_for_customer_auth ) ) {
759 $html .= '<div data-login-method="email" class="customer-login-method-wrapper ' . ( $selected_contact_type_for_auth == 'email' ? '' : 'os-hidden' ) . '">';
760 $html .= OsFormHelper::text_field(
761 'auth[email]',
762 __( 'Your Email Address', 'latepoint' ),
763 $customer->email,
764 array(
765 'validate' => $customer->get_validations_for_property( 'email' ),
766 'class' => 'required',
767 'autocomplete' => 'email',
768 )
769 );
770 $html .= '</div>';
771 }
772 if ( in_array( 'phone', $enabled_contact_types_for_customer_auth ) ) {
773 $html .= '<div data-login-method="phone" class="customer-login-method-wrapper ' . ( $selected_contact_type_for_auth == 'phone' ? '' : 'os-hidden' ) . '">';
774 $html .= OsFormHelper::phone_number_field(
775 'auth[phone]',
776 __( 'Your Phone Number', 'latepoint' ),
777 $customer->phone,
778 array(
779 'validate' => $customer->get_validations_for_property( 'phone' ),
780 'class' => 'required',
781 'theme' => 'simple',
782 'autocomplete' => 'tel',
783 )
784 );
785 $html .= '</div>';
786 }
787 $html .= '<a tabindex="0" class="latepoint-btn latepoint-btn-block latepoint-btn-primary latepoint-request-otp-button" data-otp-request-route="' . OsRouterHelper::build_route_name( 'auth', 'request_otp' ) . '"><span>' . __( 'Continue', 'latepoint' ) . '</span></a>';
788 $html .= '</div>';
789 $html .= '</div>';
790 } else {
791 ob_start();
792 ?>
793 <div class="os-customer-login-w os-customer-wrapped-box os-unwrapped">
794 <?php if ( count( $enabled_contact_types_for_customer_auth ) > 1 ) { ?>
795 <div class="login-options-wrapper">
796 <div class="latepoint-customer-box-title"><?php _e( 'Sign in', 'latepoint' ); ?></div>
797 <?php if ( in_array( 'email', $enabled_contact_types_for_customer_auth ) && in_array( 'phone', $enabled_contact_types_for_customer_auth ) ) { ?>
798 <div class="login-options-col login-options-via">
799 <div class="login-options-via-wrapper">
800 <div data-login-method="email" data-is-otp-enabled="<?php echo OsOTPHelper::is_otp_enabled_for_contact_type( 'email', 'email' ) ? 'yes' : 'no'; ?>"
801 data-otp-delivery-method="email"
802 class="login-option <?php echo $selected_contact_type_for_auth == 'email' ? 'os-selected os-default' : ''; ?>"><?php _e( 'Email', 'latepoint' ); ?></div>
803 <div data-login-method="phone" data-is-otp-enabled="<?php echo OsOTPHelper::is_otp_enabled_for_contact_type( 'phone', 'sms' ) ? 'yes' : 'no'; ?>"
804 data-otp-delivery-method="sms"
805 class="login-option <?php echo $selected_contact_type_for_auth == 'phone' ? 'os-selected os-default' : ''; ?>"><?php _e( 'Phone', 'latepoint' ); ?></div>
806 </div>
807 </div>
808 <?php } ?>
809 </div>
810 <?php } ?>
811 <?php
812 if ( in_array( 'email', $enabled_contact_types_for_customer_auth ) ) {
813 echo '<div data-login-method="email" class="customer-login-method-wrapper ' . ( $selected_contact_type_for_auth == 'email' ? '' : 'os-hidden' ) . '">';
814 echo OsFormHelper::text_field( 'auth[email]', __( 'Your Email Address', 'latepoint' ), '', [ 'autocomplete' => 'email' ] );
815 echo '</div>';
816 }
817 ?>
818 <?php
819 if ( in_array( 'phone', $enabled_contact_types_for_customer_auth ) ) {
820 echo '<div data-login-method="phone" class="customer-login-method-wrapper ' . ( $selected_contact_type_for_auth == 'phone' ? '' : 'os-hidden' ) . '">';
821 echo OsFormHelper::phone_number_field( 'auth[phone]', __( 'Your Phone Number', 'latepoint' ), '', [ 'autocomplete' => 'tel' ] );
822 echo '</div>';
823 }
824 ?>
825 <div class="os-customer-login-password-fields-w" <?php echo ( OsAuthHelper::get_default_customer_authentication_method() == 'otp' ) ? 'style="display:none;"' : ''; ?>>
826 <?php echo OsFormHelper::password_field(
827 'auth[password]',
828 __( 'Your Password', 'latepoint' ),
829 '',
830 array(
831 'class' => 'required',
832 'autocomplete' => 'current-password',
833 )
834 ); ?>
835 <a href="#" class="latepoint-btn latepoint-btn-primary latepoint-btn-link step-forgot-password-btn"
836 data-os-action="<?php echo esc_attr( OsRouterHelper::build_route_name( 'customer_cabinet', 'request_password_reset_token' ) ); ?>"
837 data-os-output-target=".os-password-reset-form-holder"
838 data-os-after-call="latepoint_reset_password_from_booking_init"
839 data-os-params="<?php echo esc_attr( OsUtilHelper::build_os_params( [ 'from_booking' => true ] ) ); ?>"><span><?php esc_html_e( 'Forgot?', 'latepoint' ); ?></span></a>
840 </div>
841
842 <?php if ( $otp_allowed ) { ?>
843 <div class="os-customer-otp-notice" <?php echo ( OsAuthHelper::get_default_customer_authentication_method() == 'otp' ) ? '' : 'style="display:none;"'; ?>>
844 <?php _e( 'You will receive a 6-digit code to log in.', 'latepoint' ); ?>
845 </div>
846 <?php } ?>
847 <div class="os-customer-login-buttons">
848 <?php if ( $multiple_auth_methods_enabled ) { ?>
849 <div class="login-options-col">
850 <div class="latepoint-customer-otp-option">
851 <?php if ( OsAuthHelper::get_default_customer_authentication_method() == 'otp' ) { ?>
852 <label><input type="checkbox" name="auth[via]" value="password"
853 class="login-with-password-toggle os-opposite"><span><?php _e( 'Use password instead', 'latepoint' ); ?></span></label>
854 <?php } else { ?>
855 <label><input type="checkbox" name="auth[via]" value="otp"
856 class="login-with-password-toggle"><span><?php _e( 'Send me a sign-in code', 'latepoint' ); ?></span></label>
857 <?php } ?>
858 </div>
859 </div>
860 <?php } else {
861 echo OsFormHelper::hidden_field( 'auth[via]', OsAuthHelper::get_default_customer_authentication_method() );
862 } ?>
863 <a data-otp-request-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'auth', 'request_otp' ) ); ?>"
864 data-password-login-route="<?php echo esc_attr( OsRouterHelper::build_route_name( 'auth', 'login_customer' ) ); ?>" href="#"
865 class="latepoint-btn latepoint-btn-primary step-login-existing-customer-btn <?php echo $multiple_auth_methods_enabled ? '' : 'latepoint-btn-block'; ?>"><span><?php esc_html_e( 'Continue', 'latepoint' ); ?></span></a>
866 </div>
867 </div>
868 <?php
869 $html = ob_get_clean();
870 }
871 $html .= OsFormHelper::hidden_field( 'auth[contact_type]', $selected_contact_type_for_auth );
872 $html .= OsFormHelper::hidden_field( 'auth[delivery_method]', $selected_delivery_method );
873 $html .= OsFormHelper::hidden_field( 'auth[action]', 'register' );
874 $html .= wp_nonce_field( 'auth_nonce', 'auth[nonce]', true, false );
875
876
877 return $html;
878 }
879
880
881 // WP USER
882 public static function get_logged_in_wp_user_id() {
883 return OsWpUserHelper::get_current_user_id();
884 }
885
886 public static function get_logged_in_wp_user() {
887 return OsWpUserHelper::get_current_user();
888 }
889
890
891 // UTILS
892
893 public static function hash_password( $password ) {
894 return wp_hash_password( $password, PASSWORD_DEFAULT );
895 }
896
897 public static function verify_password( $password, $hash ) {
898 return wp_check_password( $password, $hash );
899 }
900 }
901