PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 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 All 507 releases
jetpack / jetpack_vendor / automattic / jetpack-publicize / src / class-keyring-helper.php

class-keyring-helper.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-beta, at jetpack_vendor/automattic/jetpack-publicize/src/class-keyring-helper.php

258 lines 7.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Keyring helper.
4 *
5 * @package automattic/jetpack-publicize
6 */
7
8 namespace Automattic\Jetpack\Publicize;
9
10 use Automattic\Jetpack\Connection\Secrets;
11 use Automattic\Jetpack\Paths;
12 use Jetpack_IXR_Client;
13 use Jetpack_Options;
14
15 /**
16 * Starts Keyring connection requests and removes Publicize connections, both
17 * through public-api.
18 */
19 class Keyring_Helper {
20 /**
21 * Class instance
22 *
23 * @var Keyring_Helper
24 */
25 private static $instance = null;
26
27 /**
28 * Initialize instance.
29 */
30 public static function init() {
31 if ( null === self::$instance ) {
32 self::$instance = new Keyring_Helper();
33 }
34
35 return self::$instance;
36 }
37
38 /**
39 * Services whose connection request starts from the site. Jetpack Social's
40 * networks start theirs from WordPress.com instead.
41 */
42 const SERVICES = array(
43 'google_site_verification' => array(
44 'for' => 'other',
45 ),
46 );
47
48 /**
49 * Constructor
50 */
51 private function __construct() {
52 add_action( 'admin_init', array( __CLASS__, 'intercept_request' ) );
53 }
54
55 /**
56 * Gets a URL to the public-api actions. Works like WP's admin_url.
57 * On WordPress.com this is/calls Keyring::admin_url.
58 *
59 * @param string $service Shortname of a specific service.
60 * @param array $params Parameters to append to an API connection URL.
61 *
62 * @return string URL to specific public-api process
63 */
64 private static function api_url( $service = false, $params = array() ) {
65 /**
66 * Filters the API URL used to interact with WordPress.com.
67 *
68 * @since 0.1.0
69 * @since-jetpack 2.0.0
70 *
71 * @param string https://public-api.wordpress.com/connect/?jetpack=publicize Default Publicize API URL.
72 */
73 $url = apply_filters( 'publicize_api_url', 'https://public-api.wordpress.com/connect/?jetpack=publicize' );
74
75 if ( $service ) {
76 $url = add_query_arg( array( 'service' => $service ), $url );
77 }
78
79 if ( array() !== $params ) {
80 $url = add_query_arg( $params, $url );
81 }
82
83 return $url;
84 }
85
86 /**
87 * Build a connection URL (admin URL with unique query args to create a connection).
88 *
89 * @param string $service_name Service name.
90 * @param string $for Feature name.
91 */
92 public static function connect_url( $service_name, $for ) {
93 return add_query_arg(
94 array(
95 'action' => 'request',
96 'service' => $service_name,
97 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
98 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
99 'for' => $for,
100 'publicize_action' => 1,
101 ),
102 admin_url()
103 );
104 }
105
106 /**
107 * Build a URL to refresh a connection (admin URL with unique query args to refresh a connection).
108 * Similar to connect_url, but with a refresh parameter.
109 *
110 * @param string $service_name Service name.
111 * @param string $for Feature name.
112 */
113 public static function refresh_url( $service_name, $for ) {
114 return add_query_arg(
115 array(
116 'action' => 'request',
117 'service' => $service_name,
118 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
119 'refresh' => 1,
120 'for' => $for,
121 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
122 'publicize_action' => 1,
123 ),
124 admin_url()
125 );
126 }
127
128 /**
129 * Build a URL to delete a connection (admin URL with unique query args to delete a connection).
130 *
131 * @param string $service_name Service name.
132 * @param string $id Connection ID.
133 */
134 public static function disconnect_url( $service_name, $id ) {
135 return add_query_arg(
136 array(
137 'action' => 'delete',
138 'service' => $service_name,
139 'id' => $id,
140 'kr_nonce' => wp_create_nonce( 'keyring-request' ),
141 'nonce' => wp_create_nonce( "keyring-request-$service_name" ),
142 'publicize_action' => 1,
143 ),
144 admin_url()
145 );
146 }
147
148 /**
149 * Handle a Keyring connection request or deletion started from connect_url(), refresh_url() or disconnect_url().
150 */
151 public static function intercept_request() {
152 if ( ! empty( $_GET['publicize_action'] ) && isset( $_GET['action'] ) ) {
153 $service_name = null;
154
155 if ( isset( $_GET['service'] ) ) {
156 $service_name = filter_var( wp_unslash( $_GET['service'] ) );
157 }
158
159 switch ( $_GET['action'] ) {
160
161 case 'request':
162 check_admin_referer( 'keyring-request', 'kr_nonce' );
163 check_admin_referer( "keyring-request-$service_name", 'nonce' );
164
165 $verification = ( new Secrets() )->generate( 'publicize' );
166 if ( ! $verification ) {
167 $url = ( new Paths() )->admin_url( 'page=jetpack#/settings' );
168 wp_die(
169 sprintf(
170 wp_kses(
171 /* Translators: placeholder is a URL to a Settings page. */
172 __( "Jetpack is not connected. Please connect Jetpack by visiting <a href='%s'>Settings</a>.", 'jetpack-publicize-pkg' ),
173 array(
174 'a' => array(
175 'href' => array(),
176 ),
177 )
178 ),
179 esc_url( $url )
180 )
181 );
182
183 }
184 $stats_options = get_option( 'stats_options' );
185 $wpcom_blog_id = Jetpack_Options::get_option( 'id' );
186 $wpcom_blog_id = ! empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id'];
187
188 $for = isset( $_GET['for'] ) ? sanitize_text_field( wp_unslash( $_GET['for'] ) ) : 'publicize';
189
190 $custom_inputs = array();
191
192 // For Bluesky.
193 if ( isset( $_GET['handle'] ) && isset( $_GET['app_password'] ) ) {
194 $custom_inputs['handle'] = sanitize_text_field( wp_unslash( $_GET['handle'] ) );
195
196 $custom_inputs['app_password'] = sanitize_text_field( wp_unslash( $_GET['app_password'] ) );
197 }
198
199 // For Mastodon.
200 if ( isset( $_GET['instance'] ) ) {
201 $custom_inputs['instance'] = sanitize_text_field( wp_unslash( $_GET['instance'] ) );
202 }
203
204 $user = wp_get_current_user();
205 $redirect = self::api_url(
206 $service_name,
207 urlencode_deep(
208 $custom_inputs +
209 array(
210 'action' => 'request',
211 'for' => $for,
212 'siteurl' => site_url(),
213 'state' => $user->ID,
214 'blog_id' => $wpcom_blog_id,
215 'secret_1' => $verification['secret_1'],
216 'secret_2' => $verification['secret_2'],
217 'eol' => $verification['exp'],
218 )
219 )
220 );
221 wp_redirect( $redirect ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect -- The API URL is an external URL and is filterable.
222 exit( 0 );
223
224 case 'delete':
225 $id = isset( $_GET['id'] ) ? filter_var( wp_unslash( $_GET['id'] ) ) : null;
226
227 check_admin_referer( 'keyring-request', 'kr_nonce' );
228 check_admin_referer( "keyring-request-$service_name", 'nonce' );
229
230 self::disconnect( $service_name, $id );
231
232 do_action( 'connection_disconnected', $service_name );
233 break;
234 }
235 }
236 }
237
238 /**
239 * Remove a Publicize connection
240 *
241 * @param string $service_name Service name.
242 * @param string $connection_id Connection ID.
243 * @param int|bool $_blog_id Blog ID.
244 * @param int|bool $_user_id User ID.
245 * @param bool $force_delete Force delete the connection.
246 */
247 public static function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
248 $xml = new Jetpack_IXR_Client();
249 $xml->query( 'jetpack.deletePublicizeConnection', $connection_id );
250
251 if ( ! $xml->isError() ) {
252 Jetpack_Options::update_option( 'publicize_connections', $xml->getResponse() );
253 } else {
254 return false;
255 }
256 }
257 }
258