PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.10 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 All 34 releases
desktop-mode / apps / user-edit / parts / account.php

account.php in OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin 1.1.10, at apps/user-edit/parts/account.php

247 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * User Edit app — the account routes and the personal-options meta.
4 *
5 * - `register_meta()` for the personal options and the contact
6 * methods, with `show_in_rest`, so the profile form saves them
7 * through core's `PUT /wp/v2/users/<id>` `meta` field.
8 * - `POST /desktop-mode/v1/users/<id>/destroy-sessions` — log the
9 * user out elsewhere (or everywhere).
10 * - `GET|POST /desktop-mode/v1/users/<id>/application-passwords`
11 * and `DELETE …/application-passwords/<uuid>` — thin wrappers over
12 * `WP_Application_Passwords`.
13 *
14 * Every route re-checks `edit_user` on the target
15 * ({@see openstation_user_edit_window_can_edit()}).
16 *
17 * @package OpenStation
18 */
19
20 defined( 'ABSPATH' ) || exit;
21
22 /**
23 * Register the personal-options user-meta keys and the contact
24 * methods with `show_in_rest`. Without this the keys exist (core uses
25 * them on the classic profile.php save) but the REST controller
26 * silently drops `meta.rich_editing`, `meta.admin_color`, … on update.
27 *
28 * Runs on `rest_api_init`: this file loads DURING `init` @10, where
29 * an `init` callback of the same priority can never fire (`WP_Hook`
30 * snapshots the running priority), and REST is the only consumer.
31 */
32 function openstation_user_edit_window_register_meta() {
33 $keys = array(
34 'rich_editing' => 'string',
35 'syntax_highlighting' => 'string',
36 'admin_color' => 'string',
37 'comment_shortcuts' => 'string',
38 'show_admin_bar_front' => 'string',
39 );
40 foreach ( array_keys( wp_get_user_contact_methods() ) as $method ) {
41 $keys[ (string) $method ] = 'string';
42 }
43 foreach ( $keys as $meta_key => $type ) {
44 register_meta(
45 'user',
46 $meta_key,
47 array(
48 'type' => $type,
49 'single' => true,
50 'show_in_rest' => array(
51 'schema' => array(
52 'type' => $type,
53 'context' => array( 'view', 'edit' ),
54 ),
55 ),
56 'auth_callback' => static function ( $allowed, $meta_key2, $user_id ) {
57 unset( $meta_key2 );
58 return current_user_can( 'edit_user', (int) $user_id );
59 },
60 'sanitize_callback' => 'sanitize_text_field',
61 )
62 );
63 }
64 }
65 add_action( 'rest_api_init', 'openstation_user_edit_window_register_meta', 5 );
66
67 /**
68 * The `permission_callback` every account route shares.
69 *
70 * @param WP_REST_Request $req Request with `id`.
71 * @return bool
72 */
73 function openstation_user_edit_window_rest_permission( $req ) {
74 return openstation_user_edit_window_can_edit( (int) get_current_user_id(), (int) $req->get_param( 'id' ) );
75 }
76
77 /**
78 * Register the sessions and application-password routes.
79 */
80 function openstation_user_edit_window_account_routes() {
81 register_rest_route(
82 'desktop-mode/v1',
83 '/users/(?P<id>\d+)/destroy-sessions',
84 array(
85 'methods' => WP_REST_Server::CREATABLE,
86 'callback' => 'openstation_user_edit_window_rest_destroy_sessions',
87 'permission_callback' => 'openstation_user_edit_window_rest_permission',
88 'args' => array(
89 'id' => array(
90 'required' => true,
91 'type' => 'integer',
92 ),
93 'scope' => array(
94 'type' => 'string',
95 'default' => 'others',
96 ),
97 ),
98 )
99 );
100 register_rest_route(
101 'desktop-mode/v1',
102 '/users/(?P<id>\d+)/application-passwords',
103 array(
104 array(
105 'methods' => WP_REST_Server::READABLE,
106 'callback' => 'openstation_user_edit_window_rest_app_pw_list',
107 'permission_callback' => 'openstation_user_edit_window_rest_permission',
108 ),
109 array(
110 'methods' => WP_REST_Server::CREATABLE,
111 'callback' => 'openstation_user_edit_window_rest_app_pw_create',
112 'permission_callback' => 'openstation_user_edit_window_rest_permission',
113 'args' => array(
114 'name' => array(
115 'required' => true,
116 'type' => 'string',
117 ),
118 ),
119 ),
120 )
121 );
122 register_rest_route(
123 'desktop-mode/v1',
124 '/users/(?P<id>\d+)/application-passwords/(?P<uuid>[a-f0-9-]+)',
125 array(
126 'methods' => WP_REST_Server::DELETABLE,
127 'callback' => 'openstation_user_edit_window_rest_app_pw_revoke',
128 'permission_callback' => 'openstation_user_edit_window_rest_permission',
129 )
130 );
131 }
132 add_action( 'rest_api_init', 'openstation_user_edit_window_account_routes' );
133
134 /**
135 * `POST /users/<id>/destroy-sessions`. Editing another user, or self
136 * with `scope=all`, destroys every session (the latter logs the
137 * requester out); self with the default scope spares this device.
138 *
139 * @param WP_REST_Request $req Request with `id` and optional `scope`.
140 * @return WP_REST_Response|WP_Error
141 */
142 function openstation_user_edit_window_rest_destroy_sessions( $req ) {
143 $id = (int) $req->get_param( 'id' );
144 $scope = (string) $req->get_param( 'scope' );
145 if ( ! class_exists( 'WP_Session_Tokens' ) ) {
146 return new WP_Error( 'openstation_users_no_sessions', __( 'Session manager unavailable.', 'desktop-mode' ), array( 'status' => 500 ) );
147 }
148 $manager = WP_Session_Tokens::get_instance( $id );
149 if ( 'all' === $scope || (int) get_current_user_id() !== $id ) {
150 $manager->destroy_all();
151 } else {
152 $manager->destroy_others( wp_get_session_token() );
153 }
154 // The sessions count in the insights payload is stale now.
155 delete_transient( 'dm_user_insights_' . $id );
156 return rest_ensure_response( array( 'ok' => true ) );
157 }
158
159 /**
160 * Core's application-password availability policy for a target user
161 * — site-wide and per user, both filterable by security plugins.
162 *
163 * @param int $user_id Target user id.
164 * @return WP_Error|null Error when unavailable, null when allowed.
165 */
166 function openstation_user_edit_window_app_pw_unavailable( $user_id ) {
167 if (
168 ! class_exists( 'WP_Application_Passwords' )
169 || ! function_exists( 'wp_is_application_passwords_available' )
170 || ! wp_is_application_passwords_available()
171 || ! wp_is_application_passwords_available_for_user( (int) $user_id )
172 ) {
173 return new WP_Error(
174 'openstation_users_app_pw_unavailable',
175 __( 'Application passwords are not available for this user.', 'desktop-mode' ),
176 array( 'status' => 501 )
177 );
178 }
179 return null;
180 }
181
182 /**
183 * `GET /users/<id>/application-passwords`.
184 *
185 * @param WP_REST_Request $req Request with `id`.
186 * @return WP_REST_Response|WP_Error
187 */
188 function openstation_user_edit_window_rest_app_pw_list( $req ) {
189 $id = (int) $req->get_param( 'id' );
190 $unavailable = openstation_user_edit_window_app_pw_unavailable( $id );
191 if ( is_wp_error( $unavailable ) ) {
192 return $unavailable;
193 }
194 return rest_ensure_response( array( 'items' => (array) WP_Application_Passwords::get_user_application_passwords( $id ) ) );
195 }
196
197 /**
198 * `POST /users/<id>/application-passwords`.
199 *
200 * @param WP_REST_Request $req Request with `id` and `name`.
201 * @return WP_REST_Response|WP_Error
202 */
203 function openstation_user_edit_window_rest_app_pw_create( $req ) {
204 $id = (int) $req->get_param( 'id' );
205 $unavailable = openstation_user_edit_window_app_pw_unavailable( $id );
206 if ( is_wp_error( $unavailable ) ) {
207 return $unavailable;
208 }
209 $name = sanitize_text_field( (string) $req->get_param( 'name' ) );
210 if ( '' === $name ) {
211 return new WP_Error( 'openstation_users_app_pw_name_required', __( 'Application password name is required.', 'desktop-mode' ), array( 'status' => 400 ) );
212 }
213 $created = WP_Application_Passwords::create_new_application_password( $id, array( 'name' => $name ) );
214 if ( is_wp_error( $created ) ) {
215 return $created;
216 }
217 list( $unhashed_password, $item ) = $created;
218 delete_transient( 'dm_user_insights_' . $id );
219 return rest_ensure_response(
220 array(
221 'ok' => true,
222 'password' => $unhashed_password,
223 'item' => $item,
224 )
225 );
226 }
227
228 /**
229 * `DELETE /users/<id>/application-passwords/<uuid>`.
230 *
231 * @param WP_REST_Request $req Request with `id` and `uuid`.
232 * @return WP_REST_Response|WP_Error
233 */
234 function openstation_user_edit_window_rest_app_pw_revoke( $req ) {
235 $id = (int) $req->get_param( 'id' );
236 $unavailable = openstation_user_edit_window_app_pw_unavailable( $id );
237 if ( is_wp_error( $unavailable ) ) {
238 return $unavailable;
239 }
240 $ok = WP_Application_Passwords::delete_application_password( $id, (string) $req->get_param( 'uuid' ) );
241 if ( is_wp_error( $ok ) ) {
242 return $ok;
243 }
244 delete_transient( 'dm_user_insights_' . $id );
245 return rest_ensure_response( array( 'ok' => true ) );
246 }
247