Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.09% covered (warning)
82.09%
55 / 67
57.14% covered (warning)
57.14%
4 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Utils
82.09% covered (warning)
82.09%
55 / 67
57.14% covered (warning)
57.14%
4 / 7
22.30
0.00% covered (danger)
0.00%
0 / 1
 update_user_token
n/a
0 / 0
n/a
0 / 0
1
 jetpack_api_constant_filter
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 init_default_constants
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 filter_register_request_body
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 generate_user
88.00% covered (warning)
88.00%
22 / 25
0.00% covered (danger)
0.00%
0 / 1
7.08
 get_wpcom_user_id
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 set_wpcom_user_id
83.33% covered (warning)
83.33%
15 / 18
0.00% covered (danger)
0.00%
0 / 1
4.07
 delete_wpcom_user_id
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * The Jetpack Connection package Utils class file.
4 *
5 * @package automattic/jetpack-connection
6 */
7
8namespace Automattic\Jetpack\Connection;
9
10use Automattic\Jetpack\Tracking;
11
12/**
13 * Provides utility methods for the Connection package.
14 */
15class Utils {
16
17    const DEFAULT_JETPACK__API_VERSION         = 1;
18    const DEFAULT_JETPACK__API_BASE            = 'https://jetpack.wordpress.com/jetpack.';
19    const DEFAULT_JETPACK__WPCOM_JSON_API_BASE = 'https://public-api.wordpress.com';
20
21    /**
22     * Enters a user token into the user_tokens option
23     *
24     * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Tokens->update_user_token() instead.
25     *
26     * @param int    $user_id The user id.
27     * @param string $token The user token.
28     * @param bool   $is_master_user Whether the user is the master user.
29     * @return bool
30     */
31    public static function update_user_token( $user_id, $token, $is_master_user ) {
32        _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Tokens->update_user_token' );
33        return ( new Tokens() )->update_user_token( $user_id, $token, $is_master_user );
34    }
35
36    /**
37     * Filters the value of the api constant.
38     *
39     * @param String $constant_value The constant value.
40     * @param String $constant_name The constant name.
41     * @return mixed | null
42     */
43    public static function jetpack_api_constant_filter( $constant_value, $constant_name ) {
44        if ( $constant_value !== null ) {
45            // If the constant value was already set elsewhere, use that value.
46            return $constant_value;
47        }
48
49        if ( defined( "self::DEFAULT_$constant_name" ) ) {
50            return constant( "self::DEFAULT_$constant_name" );
51        }
52
53        return null;
54    }
55
56    /**
57     * Add a filter to initialize default values of the constants.
58     */
59    public static function init_default_constants() {
60        add_filter(
61            'jetpack_constant_default_value',
62            array( __CLASS__, 'jetpack_api_constant_filter' ),
63            10,
64            2
65        );
66    }
67
68    /**
69     * Filters the registration request body to include tracking properties.
70     *
71     * @param array $properties Already prepared tracking properties.
72     * @return array amended properties.
73     */
74    public static function filter_register_request_body( $properties ) {
75        $tracking        = new Tracking();
76        $tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
77
78        return array_merge(
79            $properties,
80            array(
81                '_ui' => $tracks_identity['_ui'],
82                '_ut' => $tracks_identity['_ut'],
83            )
84        );
85    }
86
87    /**
88     * Generate a new user from a SSO attempt.
89     *
90     * @param object $user_data WordPress.com user information.
91     */
92    public static function generate_user( $user_data ) {
93        $username = $user_data->login;
94        /**
95         * Determines how many times the SSO module can attempt to randomly generate a user.
96         *
97         * @module sso
98         *
99         * @since jetpack-4.3.2
100         *
101         * @param int 5 By default, SSO will attempt to random generate a user up to 5 times.
102         */
103        $num_tries = (int) apply_filters( 'jetpack_sso_allowed_username_generate_retries', 5 );
104
105        $exists = username_exists( $username );
106        $tries  = 0;
107        while ( $exists && $tries++ < $num_tries ) {
108            $username = $user_data->login . '_' . $user_data->ID . '_' . wp_rand();
109            $exists   = username_exists( $username );
110        }
111
112        if ( $exists ) {
113            return false;
114        }
115
116        $user               = (object) array();
117        $user->user_pass    = wp_generate_password( 20 );
118        $user->user_login   = wp_slash( $username );
119        $user->user_email   = wp_slash( $user_data->email );
120        $user->display_name = $user_data->display_name;
121        $user->first_name   = $user_data->first_name;
122        $user->last_name    = $user_data->last_name;
123        $user->url          = $user_data->url;
124        $user->description  = $user_data->description;
125
126        if ( isset( $user_data->role ) && $user_data->role ) {
127            $user->role = $user_data->role;
128        }
129
130        $created_user_id = wp_insert_user( $user );
131
132        // `clean_user_cache()` calls `exists()` on anything non-numeric, which a WP_Error does not have.
133        if ( is_wp_error( $created_user_id ) ) {
134            return false;
135        }
136
137        self::set_wpcom_user_id( $created_user_id, (int) $user_data->ID );
138        return get_userdata( $created_user_id );
139    }
140
141    /**
142     * Get the WordPress.com user ID bound to a local user.
143     *
144     * The `wpcom_user_id` meta is a durable, one-to-one, WordPress.com-asserted identity binding,
145     * not a cache of any one token: SSO and Premium Content read and write it too, and it outlives
146     * the connection that first established it. Do not drop it because a token went away.
147     *
148     * @since 9.2.0
149     *
150     * @param int $user_id The local WordPress user ID.
151     * @return int The WordPress.com user ID, or 0 when none is bound.
152     */
153    public static function get_wpcom_user_id( $user_id ) {
154        return (int) get_user_meta( absint( $user_id ), 'wpcom_user_id', true );
155    }
156
157    /**
158     * Bind a WordPress.com user ID to a local user, removing it from any other user first.
159     *
160     * Two local users answering to the same WordPress.com identity would make owner resolution
161     * ambiguous, so the previous holder is cleared. That only holds for writes routed through here:
162     * Premium Content writes the same key directly, so uniqueness is not guaranteed site-wide. On
163     * multisite the lookup is scoped to the current site.
164     *
165     * @since 9.2.0
166     *
167     * @param int $user_id       The local WordPress user ID.
168     * @param int $wpcom_user_id The WordPress.com user ID.
169     */
170    public static function set_wpcom_user_id( $user_id, $wpcom_user_id ) {
171        $user_id       = absint( $user_id );
172        $wpcom_user_id = absint( $wpcom_user_id );
173
174        // 0 is what `get_wpcom_user_id()` returns for "nothing bound", so it is not storable.
175        if ( ! $user_id || ! $wpcom_user_id ) {
176            return;
177        }
178
179        $existing = new \WP_User_Query(
180            array(
181                'meta_key'    => 'wpcom_user_id',
182                'meta_value'  => $wpcom_user_id,
183                'exclude'     => array( $user_id ),
184                'fields'      => 'ID',
185                'count_total' => false,
186            )
187        );
188
189        foreach ( $existing->get_results() as $stale_user_id ) {
190            delete_user_meta( $stale_user_id, 'wpcom_user_id' );
191            clean_user_cache( $stale_user_id );
192        }
193
194        update_user_meta( $user_id, 'wpcom_user_id', $wpcom_user_id );
195        clean_user_cache( $user_id );
196    }
197
198    /**
199     * Drop the WordPress.com user ID bound to a local user.
200     *
201     * Only for when the binding itself is known to be wrong — a token replaced with a different
202     * WordPress.com account. A disconnect does not make it wrong, and other subsystems store their
203     * own meaning in this key.
204     *
205     * @since 9.2.0
206     *
207     * @param int $user_id The local WordPress user ID.
208     */
209    public static function delete_wpcom_user_id( $user_id ) {
210        $user_id = absint( $user_id );
211
212        // `clean_user_cache()` bumps the site-wide users cache salt, so skip it on the common
213        // no-op path where there was nothing bound.
214        if ( delete_user_meta( $user_id, 'wpcom_user_id' ) ) {
215            clean_user_cache( $user_id );
216        }
217    }
218}