Skip to content

Commit a22859e

Browse files
fix: permission issues for move_image and PHPStan types
1 parent 721ac28 commit a22859e

File tree

2 files changed

+138
-77
lines changed

2 files changed

+138
-77
lines changed

inc/rest.php

Lines changed: 132 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -28,53 +28,67 @@ class Optml_Rest {
2828
/**
2929
* Upload conflicts api.
3030
*
31-
* @var array upload_conflicts_api.
31+
* @var array{
32+
* service_routes: RestRouteMap,
33+
* image_routes: RestRouteMap,
34+
* media_cloud_routes: RestRouteMap,
35+
* conflict_routes: SimpleRouteMap,
36+
* cache_routes: SimpleRouteMap,
37+
* dam_routes: RestRouteMap,
38+
* notification_dismiss_routes: RestRouteMap,
39+
* optimization_routes: RestRouteMap
40+
* }
3241
*/
3342
public static $rest_routes = [
3443
'service_routes' => [
35-
'update_option' => 'POST', 'request_update' => 'GET', 'check_redirects' => 'POST_PUT_PATCH',
36-
'connect' => [
37-
'POST', 'args' => [
38-
'api_key' => [
39-
'type' => 'string',
40-
'required' => true,
41-
],
42-
],
44+
'update_option' => 'POST',
45+
'request_update' => 'GET',
46+
'check_redirects' => 'POST_PUT_PATCH',
47+
'connect' => [
48+
'POST',
49+
'args' => [
50+
'api_key' => [
51+
'type' => 'string',
52+
'required' => true,
4353
],
44-
'select_application' => [
45-
'POST', 'args' => [
46-
'api_key' => [
47-
'type' => 'string',
48-
'required' => true,
49-
],
50-
'application' => [
51-
'type' => 'string',
52-
'required' => true,
53-
],
5454
],
55+
],
56+
'select_application' => [
57+
'POST',
58+
'args' => [
59+
'api_key' => [
60+
'type' => 'string',
61+
'required' => true,
62+
],
63+
'application' => [
64+
'type' => 'string',
65+
'required' => true,
66+
],
5567
],
56-
'register_service' => [
57-
'POST', 'args' => [
58-
'email' => [
59-
'type' => 'string',
60-
'required' => true,
61-
],
68+
],
69+
'register_service' => [
70+
'POST',
71+
'args' => [
72+
'email' => [
73+
'type' => 'string',
74+
'required' => true,
6275
],
63-
6476
],
65-
'disconnect' => 'GET',
77+
],
78+
'disconnect' => 'GET',
6679
],
6780
'image_routes' => [
6881
'poll_optimized_images' => 'GET',
6982
'get_sample_rate' => 'POST',
7083
'upload_onboard_images' => [
71-
'POST', 'args' => [
72-
'offset' => [
73-
'type' => 'number',
74-
'required' => false,
75-
'default' => 0,
76-
],
84+
'POST',
85+
'args' => [
86+
'offset' => [
87+
'type' => 'number',
88+
'required' => false,
89+
'default' => 0,
7790
],
91+
],
7892
],
7993
],
8094
'media_cloud_routes' => [
@@ -93,7 +107,7 @@ class Optml_Rest {
93107
'required' => true,
94108
],
95109
],
96-
'permission_callback' => 'upload_files',
110+
'permission_callback' => [ __CLASS__, 'can_move_image' ],
97111
],
98112
],
99113
'conflict_routes' => [
@@ -158,43 +172,63 @@ public function __construct() {
158172
}
159173

160174
/**
161-
* Method to register a specific rest route.
175+
* Method to register a specific REST route.
176+
*
177+
* @param string $route The route name.
178+
* @param string $method The route access method: GET, POST, POST_PUT_PATCH.
179+
* @param array $args Optional arguments for route parameters.
180+
* @param string|callable $permission_callback Permission callback function or capability.
181+
*
182+
* @phpstan-param RestArgs $args Optional arguments for route parameters.
183+
*
184+
* @throws \InvalidArgumentException If method is invalid.
162185
*
163-
* @param string $route The route name.
164-
* @param string $method The route access method GET, POST, POST_PUT_PATCH.
165-
* @param array $args Optional argument to include required args.
166-
* @param string $permission_callback Optional permission callback.
186+
* @return void
167187
*/
168-
private function reqister_route( $route, $method = 'GET', $args = [], $permission_callback = 'manage_options' ) {
169-
$wp_method_constant = false;
170-
if ( $method === 'GET' ) {
171-
$wp_method_constant = \WP_REST_Server::READABLE;
172-
}
173-
if ( $method === 'POST' ) {
174-
$wp_method_constant = \WP_REST_Server::CREATABLE;
175-
}
176-
if ( $method === 'POST_PUT_PATCH' ) {
177-
$wp_method_constant = \WP_REST_Server::EDITABLE;
188+
private function register_route( $route, $method = 'GET', $args = [], $permission_callback = 'manage_options' ) {
189+
if ( empty( $route ) ) {
190+
return;
178191
}
179-
if ( $wp_method_constant !== false ) {
180-
$params = [
181-
'methods' => $wp_method_constant,
182-
'permission_callback' => function_exists( $permission_callback ) ? $permission_callback : function () use ( $permission_callback ) {
183-
return current_user_can( $permission_callback );
184-
},
185-
'callback' => [ $this, $route ],
186-
];
187-
if ( ! empty( $args ) ) {
188-
$params['args'] = $args;
189-
}
190-
register_rest_route(
191-
$this->namespace,
192-
'/' . $route,
193-
[
194-
$params,
195-
]
192+
193+
$method_map = [
194+
'GET' => \WP_REST_Server::READABLE,
195+
'POST' => \WP_REST_Server::CREATABLE,
196+
'POST_PUT_PATCH' => \WP_REST_Server::EDITABLE,
197+
];
198+
199+
if ( ! isset( $method_map[ $method ] ) ) {
200+
_doing_it_wrong(
201+
__METHOD__,
202+
sprintf( 'Invalid REST method: %s', esc_html( $method ) ),
203+
'1.0.0'
196204
);
205+
return;
197206
}
207+
208+
$permission = is_callable( $permission_callback )
209+
? $permission_callback
210+
: function () use ( $permission_callback ) {
211+
if ( ! is_string( $permission_callback ) ) {
212+
return false;
213+
}
214+
return current_user_can( $permission_callback );
215+
};
216+
217+
$params = [
218+
'methods' => $method_map[ $method ],
219+
'permission_callback' => $permission,
220+
'callback' => [ $this, $route ],
221+
];
222+
223+
if ( ! empty( $args ) ) {
224+
$params['args'] = $args;
225+
}
226+
227+
register_rest_route(
228+
$this->namespace,
229+
'/' . $route,
230+
[ $params ]
231+
);
198232
}
199233

200234
/**
@@ -219,9 +253,9 @@ public function register() {
219253
public function register_service_routes() {
220254
foreach ( self::$rest_routes['service_routes'] as $route => $details ) {
221255
if ( is_array( $details ) ) {
222-
$this->reqister_route( $route, $details[0], $details['args'] );
256+
$this->register_route( $route, $details[0], $details['args'] );
223257
} else {
224-
$this->reqister_route( $route, $details );
258+
$this->register_route( $route, $details );
225259
}
226260
}
227261
}
@@ -232,9 +266,9 @@ public function register_service_routes() {
232266
public function register_image_routes() {
233267
foreach ( self::$rest_routes['image_routes'] as $route => $details ) {
234268
if ( is_array( $details ) ) {
235-
$this->reqister_route( $route, $details[0], $details['args'] );
269+
$this->register_route( $route, $details[0], $details['args'] );
236270
} else {
237-
$this->reqister_route( $route, $details );
271+
$this->register_route( $route, $details );
238272
}
239273
}
240274
}
@@ -246,7 +280,7 @@ public function register_media_offload_routes() {
246280

247281
$permission = isset( $details['permission_callback'] ) ? $details['permission_callback'] : 'manage_options';
248282
$args = isset( $details['args'] ) ? $details['args'] : [];
249-
$this->reqister_route( $route, is_array( $details ) ? $details[0] : $details, $args, $permission );
283+
$this->register_route( $route, is_array( $details ) ? $details[0] : $details, $args, $permission );
250284
}
251285
}
252286

@@ -256,7 +290,7 @@ public function register_media_offload_routes() {
256290
*/
257291
public function register_conflict_routes() {
258292
foreach ( self::$rest_routes['conflict_routes'] as $route => $details ) {
259-
$this->reqister_route( $route, $details );
293+
$this->register_route( $route, $details );
260294
}
261295
}
262296

@@ -265,7 +299,7 @@ public function register_conflict_routes() {
265299
*/
266300
public function register_cache_routes() {
267301
foreach ( self::$rest_routes['cache_routes'] as $route => $details ) {
268-
$this->reqister_route( $route, $details );
302+
$this->register_route( $route, $details );
269303
}
270304
}
271305

@@ -278,7 +312,7 @@ public function register_dam_routes() {
278312
foreach ( self::$rest_routes['dam_routes'] as $route => $details ) {
279313
$permission = isset( $details['permission_callback'] ) ? $details['permission_callback'] : 'manage_options';
280314
$args = isset( $details['args'] ) ? $details['args'] : [];
281-
$this->reqister_route( $route, $details[0], $args, $permission );
315+
$this->register_route( $route, $details[0], $args, $permission );
282316
}
283317
}
284318

@@ -289,7 +323,7 @@ public function register_dam_routes() {
289323
*/
290324
public function register_notification_routes() {
291325
foreach ( self::$rest_routes['notification_dismiss_routes'] as $route => $details ) {
292-
$this->reqister_route( $route, $details[0], isset( $details['args'] ) ? $details['args'] : [] );
326+
$this->register_route( $route, $details[0], isset( $details['args'] ) ? $details['args'] : [] );
293327
}
294328
}
295329

@@ -1051,7 +1085,7 @@ function ( $url ) {
10511085
*/
10521086
public function register_optimization_routes() {
10531087
foreach ( self::$rest_routes['optimization_routes'] as $route => $details ) {
1054-
$this->reqister_route( $route, $details[0], $details['args'], $details['permission_callback'] );
1088+
$this->register_route( $route, $details[0], $details['args'], $details['permission_callback'] );
10551089
}
10561090
}
10571091

@@ -1063,7 +1097,7 @@ public function register_optimization_routes() {
10631097
* @return WP_REST_Response
10641098
*/
10651099
public function move_image( WP_REST_Request $request ) {
1066-
$id = $request->get_param( 'id' );
1100+
$id = $request->get_param( 'id' );
10671101
$action = $request->get_param( 'action' );
10681102

10691103
if ( $request->get_param( 'status' ) === 'start' ) {
@@ -1087,4 +1121,25 @@ public function move_image( WP_REST_Request $request ) {
10871121

10881122
return $this->response( [ $id,$action ], $result );
10891123
}
1124+
1125+
/**
1126+
* Check if user can move image to/from cloud.
1127+
*
1128+
* @param WP_REST_Request $request Rest request.
1129+
* @phpstan-param WP_REST_Request<array{id: int, action: string}> $request Rest request.
1130+
* @return bool True if user can move image, false otherwise.
1131+
*/
1132+
public static function can_move_image( WP_REST_Request $request ) {
1133+
1134+
if ( ! current_user_can( 'upload_files' ) ) {
1135+
return false;
1136+
}
1137+
1138+
$id = $request->get_param( 'id' );
1139+
if ( ! current_user_can( 'edit_post', $id ) ) {
1140+
return false;
1141+
}
1142+
1143+
return true;
1144+
}
10901145
}

phpstan.neon

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ parameters:
1818
- OPTML_DEBUG_MEDIA
1919
- ABSPATH
2020
- WPINC
21+
typeAliases:
22+
RestArgConfig: 'array{type: string, required: bool, default?: mixed}'
23+
RestArgs: 'array<string, RestArgConfig>'
24+
RestRouteConfig: 'array{0: string, args?: RestArgs, permission_callback?: callable|string}'
25+
RestRouteMap: 'array<string, string|RestRouteConfig>'
26+
SimpleRouteMap: 'array<string, string>'
2127
includes:
2228
- %currentWorkingDirectory%/vendor/szepeviktor/phpstan-wordpress/extension.neon
2329
- %currentWorkingDirectory%/phpstan-baseline.neon

0 commit comments

Comments
 (0)