PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / trunk
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin vtrunk
1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 0.8.6 All 33 releases
desktop-mode / includes / network / identity.php

identity.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin trunk, at includes/network/identity.php

168 lines 5.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * OpenStation — Network identity: what one install tells another about
4 * itself, and the request layer the two sides share.
5 *
6 * `GET /desktop-mode/v1/network/identity` is public: a site's name,
7 * its URL, its shell screen and its public key are what a hub pins
8 * when pairing and what a member pins about its hub. Nothing secret is
9 * in it, and nothing in it is trusted until it has been pinned.
10 *
11 * @package OpenStation
12 */
13
14 defined( 'ABSPATH' ) || exit;
15
16 /**
17 * This install's identity: the facts another install pins about it.
18 *
19 * @return array{url:string,name:string,shellUrl:string,publicKey:string,multisite:bool}
20 */
21 function openstation_network_identity() {
22 $screen = 'admin.php?page=' . OPENSTATION_SHELL_PAGE_SLUG;
23 if ( is_multisite() ) {
24 $network = get_network();
25 return array(
26 'url' => esc_url_raw( network_home_url( '/' ) ),
27 'name' => (string) ( $network ? $network->site_name : get_bloginfo( 'name' ) ),
28 'shellUrl' => esc_url_raw( network_admin_url( $screen ) ),
29 'publicKey' => openstation_network_public_key(),
30 'multisite' => true,
31 );
32 }
33 return array(
34 'url' => esc_url_raw( home_url( '/' ) ),
35 'name' => (string) get_bloginfo( 'name' ),
36 'shellUrl' => esc_url_raw( admin_url( $screen ) ),
37 'publicKey' => openstation_network_public_key(),
38 'multisite' => false,
39 );
40 }
41
42 /**
43 * Whether a remote install may be reached over this URL: HTTPS, unless
44 * the install itself runs in a local or development environment, where
45 * plain HTTP between two containers is the whole point.
46 *
47 * @param string $url URL.
48 * @return bool
49 */
50 function openstation_network_url_allowed( $url ) {
51 $scheme = wp_parse_url( (string) $url, PHP_URL_SCHEME );
52 if ( 'https' === $scheme ) {
53 return true;
54 }
55 return 'http' === $scheme && in_array( wp_get_environment_type(), array( 'local', 'development' ), true );
56 }
57
58 /**
59 * A GET to another install's REST route, with the URL it is actually
60 * reached by.
61 *
62 * @param string $base The other install's URL (its home).
63 * @param string $route REST route, `/desktop-mode/v1/network/identity`.
64 * @param array<string,string> $headers Extra headers (a signature).
65 * @return array|WP_Error Decoded JSON body, or the error.
66 */
67 function openstation_network_remote_get( $base, $route, array $headers = array() ) {
68 $base = untrailingslashit( (string) $base );
69 if ( ! openstation_network_url_allowed( $base ) ) {
70 return new WP_Error( 'openstation_network_insecure', __( 'Sites in a network talk over HTTPS.', 'desktop-mode' ) );
71 }
72 $url = $base . '/wp-json' . $route;
73
74 /**
75 * Filters the URL one install reaches another by.
76 *
77 * The address a site is known by is not always the address its
78 * server can be reached at: an internal hostname behind a proxy, a
79 * container beside another container. The identity and the pinned
80 * key stay keyed by the public URL; only the wire address changes.
81 *
82 * @param string $url The URL about to be requested.
83 * @param string $base The install's public URL.
84 */
85 $url = (string) apply_filters( 'openstation_network_request_url', $url, $base );
86
87 $response = wp_remote_get(
88 $url,
89 array(
90 'timeout' => 8,
91 // An identity or list request never moves; a redirect is a
92 // misconfigured host (a multisite sending an unknown Host to
93 // signup) and is reported as such rather than followed.
94 'redirection' => 0,
95 'headers' => array_merge( array( 'Accept' => 'application/json' ), $headers ),
96 )
97 );
98 if ( is_wp_error( $response ) ) {
99 return $response;
100 }
101 $code = (int) wp_remote_retrieve_response_code( $response );
102 $body = json_decode( (string) wp_remote_retrieve_body( $response ), true );
103 if ( 200 !== $code || ! is_array( $body ) ) {
104 $message = is_array( $body ) && ! empty( $body['message'] ) ? (string) $body['message'] : '';
105 return new WP_Error(
106 'openstation_network_http_' . $code,
107 '' !== $message
108 ? $message
109 /* translators: %d: HTTP status code. */
110 : sprintf( __( 'The site answered with HTTP %d.', 'desktop-mode' ), $code )
111 );
112 }
113 return $body;
114 }
115
116 /**
117 * Fetch and validate another install's identity.
118 *
119 * @param string $url The install's URL.
120 * @return array|WP_Error Identity, or the error.
121 */
122 function openstation_network_fetch_identity( $url ) {
123 $identity = openstation_network_remote_get( $url, '/desktop-mode/v1/network/identity' );
124 if ( is_wp_error( $identity ) ) {
125 return $identity;
126 }
127 foreach ( array( 'url', 'name', 'shellUrl', 'publicKey' ) as $key ) {
128 if ( empty( $identity[ $key ] ) || ! is_string( $identity[ $key ] ) ) {
129 return new WP_Error( 'openstation_network_no_identity', __( 'That site does not run OpenStation, or its network identity is unreachable.', 'desktop-mode' ) );
130 }
131 }
132 if ( ! openstation_network_is_public_key( $identity['publicKey'] ) ) {
133 return new WP_Error( 'openstation_network_bad_key', __( 'That site published a public key OpenStation cannot use.', 'desktop-mode' ) );
134 }
135 return array(
136 'url' => esc_url_raw( $identity['url'] ),
137 'name' => sanitize_text_field( $identity['name'] ),
138 'shellUrl' => esc_url_raw( $identity['shellUrl'] ),
139 'publicKey' => $identity['publicKey'],
140 'multisite' => ! empty( $identity['multisite'] ),
141 );
142 }
143
144 /**
145 * Register the identity route.
146 */
147 function openstation_network_register_identity_route() {
148 register_rest_route(
149 'desktop-mode/v1',
150 '/network/identity',
151 array(
152 'methods' => WP_REST_Server::READABLE,
153 'callback' => 'openstation_rest_network_identity',
154 'permission_callback' => '__return_true',
155 )
156 );
157 }
158 add_action( 'rest_api_init', 'openstation_network_register_identity_route' );
159
160 /**
161 * GET /desktop-mode/v1/network/identity
162 *
163 * @return WP_REST_Response
164 */
165 function openstation_rest_network_identity() {
166 return rest_ensure_response( openstation_network_identity() );
167 }
168