Completed
Push — add/jetpack-data-methods ( d7e5a6...2c93cb )
by
unknown
07:30
created

class.jetpack-data.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use Automattic\Jetpack\Constants\Manager as Constants_Manager;
4
5
class Jetpack_Data {
6
	/*
7
	 * Used internally when we want to look for the Normal Blog Token
8
	 * without knowing its token key ahead of time.
9
	 */
10
	const MAGIC_NORMAL_TOKEN_KEY = ';normal;';
11
12
	/**
13
	 * Gets the requested token.
14
	 *
15
	 * Tokens are one of two types:
16
	 * 1. Blog Tokens: These are the "main" tokens. Each site typically has one Blog Token,
17
	 *    though some sites can have multiple "Special" Blog Tokens (see below). These tokens
18
	 *    are not associated with a user account. They represent the site's connection with
19
	 *    the Jetpack servers.
20
	 * 2. User Tokens: These are "sub-"tokens. Each connected user account has one User Token.
21
	 *
22
	 * All tokens look like "{$token_key}.{$private}". $token_key is a public ID for the
23
	 * token, and $private is a secret that should never be displayed anywhere or sent
24
	 * over the network; it's used only for signing things.
25
	 *
26
	 * Blog Tokens can be "Normal" or "Special".
27
	 * * Normal: The result of a normal connection flow. They look like
28
	 *   "{$random_string_1}.{$random_string_2}"
29
	 *   That is, $token_key and $private are both random strings.
30
	 *   Sites only have one Normal Blog Token. Normal Tokens are found in either
31
	 *   Jetpack_Options::get_option( 'blog_token' ) (usual) or the JETPACK_BLOG_TOKEN
32
	 *   constant (rare).
33
	 * * Special: A connection token for sites that have gone through an alternative
34
	 *   connection flow. They look like:
35
	 *   ";{$special_id}{$special_version};{$wpcom_blog_id};.{$random_string}"
36
	 *   That is, $private is a random string and $token_key has a special structure with
37
	 *   lots of semicolons.
38
	 *   Most sites have zero Special Blog Tokens. Special tokens are only found in the
39
	 *   JETPACK_BLOG_TOKEN constant.
40
	 *
41
	 * In particular, note that Normal Blog Tokens never start with ";" and that
42
	 * Special Blog Tokens always do.
43
	 *
44
	 * When searching for a matching Blog Tokens, Blog Tokens are examined in the following
45
	 * order:
46
	 * 1. Defined Special Blog Tokens (via the JETPACK_BLOG_TOKEN constant)
47
	 * 2. Stored Normal Tokens (via Jetpack_Options::get_option( 'blog_token' ))
48
	 * 3. Defined Normal Tokens (via the JETPACK_BLOG_TOKEN constant)
49
	 *
50
	 * @param int|false    $user_id   false: Return the Blog Token. int: Return that user's User Token.
51
	 * @param string|false $token_key If provided, check that the token matches the provided input.
52
	 *                                false                                : Use first token. Default.
53
	 *                                Jetpack_Data::MAGIC_NORMAL_TOKEN_KEY : Use first Normal Token.
54
	 *                                non-empty string                     : Use matching token
55
	 * @return object|false
56
	 */
57
	public static function get_access_token( $user_id = false, $token_key = false ) {
58
		$possible_special_tokens = array();
59
		$possible_normal_tokens  = array();
60
61
		if ( $user_id ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $user_id of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
62
			if ( !$user_tokens = Jetpack_Options::get_option( 'user_tokens' ) ) {
63
				return false;
64
			}
65
			if ( $user_id === JETPACK_MASTER_USER ) {
66
				if ( !$user_id = Jetpack_Options::get_option( 'master_user' ) ) {
67
					return false;
68
				}
69
			}
70
			if ( !isset( $user_tokens[$user_id] ) || ! $user_tokens[$user_id] ) {
71
				return false;
72
			}
73
			$user_token_chunks = explode( '.', $user_tokens[$user_id] );
74
			if ( empty( $user_token_chunks[1] ) || empty( $user_token_chunks[2] ) ) {
75
				return false;
76
			}
77
			if ( $user_id != $user_token_chunks[2] ) {
78
				return false;
79
			}
80
			$possible_normal_tokens[] = "{$user_token_chunks[0]}.{$user_token_chunks[1]}";
81
		} else {
82
			$stored_blog_token = Jetpack_Options::get_option( 'blog_token' );
83
			if ( $stored_blog_token ) {
84
				$possible_normal_tokens[] = $stored_blog_token;
85
			}
86
87
			$defined_tokens = Constants_Manager::is_defined( 'JETPACK_BLOG_TOKEN' )
88
				? explode( ',', Constants_Manager::get_constant( 'JETPACK_BLOG_TOKEN' ) )
89
				: array();
90
91
			foreach ( $defined_tokens as $defined_token ) {
92
				if ( ';' === $defined_token[0] ) {
93
					$possible_special_tokens[] = $defined_token;
94
				} else {
95
					$possible_normal_tokens[] = $defined_token;
96
				}
97
			}
98
		}
99
100
		if ( self::MAGIC_NORMAL_TOKEN_KEY === $token_key ) {
101
			$possible_tokens = $possible_normal_tokens;
102
		} else {
103
			$possible_tokens = array_merge( $possible_special_tokens, $possible_normal_tokens );
104
		}
105
106
		if ( ! $possible_tokens ) {
107
			return false;
108
		}
109
110
		$valid_token = false;
111
112
		if ( false === $token_key ) {
113
			// Use first token.
114
			$valid_token = $possible_tokens[0];
115
		} elseif ( self::MAGIC_NORMAL_TOKEN_KEY === $token_key ) {
116
			// Use first normal token.
117
			$valid_token = $possible_tokens[0]; // $possible_tokens only contains normal tokens because of earlier check.
118
		} else {
119
			// Use the token matching $token_key or false if none.
120
			// Ensure we check the full key.
121
			$token_check = rtrim( $token_key, '.' ) . '.';
122
123
			foreach ( $possible_tokens as $possible_token ) {
124
				if ( hash_equals( substr( $possible_token, 0, strlen( $token_check ) ), $token_check ) ) {
125
					$valid_token = $possible_token;
126
					break;
127
				}
128
			}
129
		}
130
131
		if ( ! $valid_token ) {
132
			return false;
133
		}
134
135
		return (object) array(
136
			'secret' => $valid_token,
137
			'external_user_id' => (int) $user_id,
138
		);
139
	}
140
}
141