Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
MailPoet
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 3
110
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_querystring_valid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 get_wp_user_array
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * Autologin integration for MailPoet.
4 *
5 * If a request has `mailpoet_router` and `data` in the URL, check against MailPoet and try to find the user
6 * account or user data.
7 *
8 * @package brianhenryie/bh-wp-autologin-urls
9 */
10
11namespace BrianHenryIE\WP_Autologin_URLs\API\Integrations;
12
13use BrianHenryIE\WP_Autologin_URLs\API\User_Finder_Interface;
14use MailPoet\Models\Subscriber;
15use MailPoet\Router\Router;
16use Psr\Log\LoggerAwareInterface;
17use Psr\Log\LoggerAwareTrait;
18use Psr\Log\LoggerInterface;
19use WP_User;
20
21/**
22 *
23 * Since all querystring parameters are coming from links in emails, they will never have nonces.
24 *
25 * phpcs:disable WordPress.Security.NonceVerification.Recommended
26 */
27class MailPoet implements User_Finder_Interface, LoggerAwareInterface {
28    use LoggerAwareTrait;
29
30    /**
31     * Constructor.
32     *
33     * @param LoggerInterface $logger A PSR logger.
34     */
35    public function __construct( LoggerInterface $logger ) {
36        $this->setLogger( $logger );
37    }
38
39    /**
40     * Determine is the querystring needed for this integration present.
41     */
42    public function is_querystring_valid(): bool {
43        return isset( $_GET['mailpoet_router'] ) && isset( $_GET['data'] );
44    }
45
46    /**
47     * Check is the URL a tracking URL for MailPoet plugin and if so, log in the user being tracked.
48     *
49     * Uses MailPoet's verification process as the autologin code.
50     *
51     * @see LinkTokens::verifyToken()
52     *
53     * TODO: The time since the newsletter was sent should be respected for the expiry time.
54     *
55     * @hooked plugins_loaded
56     *
57     * @see https://wordpress.org/plugins/mailpoet/
58     *
59     * @return array{source:string, wp_user:\WP_User|null, user_data?:array<string,string>}
60     */
61    public function get_wp_user_array(): array {
62
63        $result              = array();
64        $result['source']    = 'MailPoet';
65        $result['wp_user']   = null;
66        $result['user_data'] = array();
67
68        if ( ! isset( $_GET['mailpoet_router'] ) ) {
69            return $result;
70        }
71
72        if ( ! isset( $_GET['data'] ) ) {
73            return $result;
74        }
75
76        if ( ! class_exists( Router::class ) ) {
77            return $result;
78        }
79
80        // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
81        $data = Router::decodeRequestData( filter_var( wp_unslash( $_GET['data'] ), FILTER_SANITIZE_STRIPPED ) );
82
83        /**
84         * The required data from the MailPoet querystring.
85         *
86         * @see Links::transformUrlDataObject()
87         */
88        $subscriber_id = $data[0];
89        $request_token = $data[1];
90
91        /**
92         * The MailPoet subscriber object, false if none found.
93         *
94         * @var \MailPoet\Models\Subscriber $subscriber
95         */
96        $subscriber = Subscriber::where( 'id', $subscriber_id )->findOne();
97
98        // @phpstan-ignore-next-line
99        if ( empty( $subscriber ) ) {
100            return $result;
101        }
102
103        // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
104        $database_token = $subscriber->linkToken;
105        $request_token  = substr( $request_token, 0, strlen( $database_token ) );
106        $valid          = hash_equals( $database_token, $request_token );
107
108        if ( ! $valid ) {
109            return $result;
110        }
111
112        $user_email_address = $subscriber->email;
113
114        $wp_user = get_user_by( 'email', $user_email_address );
115
116        if ( $wp_user instanceof WP_User ) {
117
118            $this->logger->info( "User wp_user:{$wp_user->ID} found via mailpoet_user:{$subscriber_id} from MailPoet URL." );
119
120            $result['wp_user'] = $wp_user;
121
122        } else {
123
124            // We have their email address but they have no account,
125            // if WooCommerce is installed, record the email address for
126            // UX and abandoned cart.
127            // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
128            $user_info = array(
129                'first_name' => $subscriber->firstName,
130                'last_name'  => $subscriber->lastName,
131            );
132
133            $result['user_data'] = $user_info;
134
135        }
136
137        return $result;
138    }
139}