PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-connection / src / class-protected-owner.php

class-protected-owner.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.5, at jetpack_vendor/automattic/jetpack-connection/src/class-protected-owner.php

156 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * The Jetpack Connection Protected Owner class file.
4 *
5 * @package automattic/jetpack-connection
6 */
7
8 namespace Automattic\Jetpack\Connection;
9
10 use Jetpack_Options;
11
12 /**
13 * The local anchor naming the connection's protected owner.
14 *
15 * WordPress.com is authoritative on who the owner is; this records who the site was told to
16 * expect, so ownership stops following whoever connected first. Identity is always matched on
17 * `wpcom_user_id` — `local_user_id` is a re-pointable cache, never the match key.
18 *
19 * @since 9.3.0
20 */
21 class Protected_Owner {
22
23 const OPTION = 'protected_owner';
24
25 /**
26 * Get the anchor.
27 *
28 * @since 9.3.0
29 *
30 * @return array|null The anchor, or null when none is usable.
31 */
32 public static function get() {
33 $anchor = Jetpack_Options::get_option( self::OPTION );
34
35 if ( ! is_array( $anchor ) || empty( $anchor['wpcom_user_id'] ) ) {
36 return null;
37 }
38
39 return $anchor;
40 }
41
42 /**
43 * Record the owner WordPress.com has confirmed for this site.
44 *
45 * @since 9.3.0
46 * @since 9.6.0 No longer records how the owner was confirmed.
47 *
48 * @param int $wpcom_user_id The owner's WordPress.com user ID, as confirmed by WordPress.com.
49 * @param int $local_user_id The owner's local WordPress user ID. Required here, though the
50 * anchor treats it as a re-pointable cache rather than the match
51 * key, so a caller that legitimately does not know it yet would
52 * need this relaxed.
53 * @return bool Whether the anchor is now stored as requested.
54 */
55 public static function set( $wpcom_user_id, $local_user_id ) {
56 $wpcom_user_id = absint( $wpcom_user_id );
57 $local_user_id = absint( $local_user_id );
58
59 // A zero ID would store an anchor `get()` rejects.
60 if ( ! $wpcom_user_id || ! $local_user_id ) {
61 return false;
62 }
63
64 $anchor = array(
65 'wpcom_user_id' => $wpcom_user_id,
66 'local_user_id' => $local_user_id,
67 'confirmed_at' => gmdate( 'Y-m-d\TH:i:s\Z' ),
68 );
69
70 if ( Jetpack_Options::update_option( self::OPTION, $anchor ) ) {
71 return true;
72 }
73
74 // `update_option()` reports false for an unchanged value as well as for a failed write.
75 return Jetpack_Options::get_option( self::OPTION ) === $anchor;
76 }
77
78 /**
79 * Point the anchor's cached local user ID at a different local user.
80 *
81 * The match key is `wpcom_user_id`; `local_user_id` is a cache of where that identity lives on
82 * this site, and it legitimately moves when the owner reconnects under another local account.
83 * Deliberately narrow: `confirmed_at` records when the owner was originally confirmed, and
84 * re-pointing a cache is not a new confirmation.
85 *
86 * @since 9.5.0
87 *
88 * @param int $local_user_id The local user the anchored identity now holds.
89 * @return bool Whether the anchor now names that local user.
90 */
91 public static function repoint( $local_user_id ) {
92 $local_user_id = absint( $local_user_id );
93 $anchor = self::get();
94
95 if ( ! $anchor || ! $local_user_id ) {
96 return false;
97 }
98
99 // Defaulted: `get()` only requires `wpcom_user_id`, so a partial anchor reaches here.
100 if ( (int) ( $anchor['local_user_id'] ?? 0 ) === $local_user_id ) {
101 return true;
102 }
103
104 $anchor['local_user_id'] = $local_user_id;
105
106 if ( Jetpack_Options::update_option( self::OPTION, $anchor ) ) {
107 return true;
108 }
109
110 return Jetpack_Options::get_option( self::OPTION ) === $anchor;
111 }
112
113 /**
114 * Drop the anchor, unlocking ownership.
115 *
116 * Leaves `master_user` alone: clearing the lock does not change who the owner is.
117 *
118 * @internal Recovery and support flows only. Consumers must not call this.
119 * @since 9.3.0
120 *
121 * @return bool Whether the anchor was deleted.
122 */
123 public static function clear() {
124 return Jetpack_Options::delete_option( self::OPTION );
125 }
126
127 /**
128 * Get the anchor, but only while it protects somebody.
129 *
130 * The anchor is dropped the moment WordPress.com stops confirming it, so holding one and
131 * being protected by it are the same thing. Kept as the name gates read by, which says what
132 * the call site means rather than what the storage happens to be.
133 *
134 * @since 9.3.0
135 *
136 * @return array|null The anchor, or null when there is none.
137 */
138 public static function get_locked() {
139 return self::get();
140 }
141
142 /**
143 * Whether an anchor is set and locked.
144 *
145 * Deliberately independent of whether the current owner matches it: a mismatch is when
146 * ownership most needs to stay locked.
147 *
148 * @since 9.3.0
149 *
150 * @return bool
151 */
152 public static function is_locked() {
153 return null !== self::get_locked();
154 }
155 }
156