Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
50.88% covered (warning)
50.88%
29 / 57
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_JSON_API_Endpoint
53.70% covered (warning)
53.70%
29 / 54
0.00% covered (danger)
0.00%
0 / 4
105.80
0.00% covered (danger)
0.00%
0 / 1
 callback
63.64% covered (warning)
63.64%
7 / 11
0.00% covered (danger)
0.00%
0 / 1
6.20
 result
n/a
0 / 0
n/a
0 / 0
0
 validate_input
50.00% covered (danger)
50.00%
6 / 12
0.00% covered (danger)
0.00%
0 / 1
13.12
 validate_call
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
6.03
 check_capability
30.00% covered (danger)
30.00%
6 / 20
0.00% covered (danger)
0.00%
0 / 1
44.30
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3if ( ! defined( 'ABSPATH' ) ) {
4    exit( 0 );
5}
6
7require JETPACK__PLUGIN_DIR . '/modules/module-info.php';
8
9/**
10 * Base class for Jetpack Endpoints, has the validate_call helper function.
11 */
12abstract class Jetpack_JSON_API_Endpoint extends WPCOM_JSON_API_Endpoint {
13
14    /**
15     * Needed capabilities.
16     *
17     * @var string
18     */
19    protected $needed_capabilities;
20
21    /**
22     * Expected actions.
23     *
24     * @var array
25     */
26    protected $expected_actions = array();
27
28    /**
29     * The action.
30     *
31     * @var string
32     */
33    protected $action;
34
35    /**
36     * Callback function.
37     *
38     * @param string $path - the path.
39     * @param int    $blog_id - the blog ID.
40     * @param object $object - parameter is for making the method signature compatible with its parent class method.
41     */
42    public function callback( $path = '', $blog_id = 0, $object = null ) {
43        $error = $this->validate_call( $blog_id, $this->needed_capabilities );
44        if ( is_wp_error( $error ) ) {
45            return $error;
46        }
47
48        $error = $this->validate_input( $object );
49        if ( is_wp_error( $error ) ) {
50            return $error;
51        }
52
53        if ( ! empty( $this->action ) ) {
54            $error = call_user_func( array( $this, $this->action ) );
55            if ( is_wp_error( $error ) ) {
56                return $error;
57            }
58        }
59
60        return $this->result();
61    }
62
63    /**
64     * The result function.
65     */
66    abstract protected function result();
67
68    /**
69     * Validate input.
70     *
71     * @param object $object - unused, for parent class compatability.
72     *
73     * @return bool
74     */
75    protected function validate_input( $object ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
76        $args = $this->input();
77
78        if ( isset( $args['action'] ) && $args['action'] === 'update' ) {
79            $this->action = 'update';
80        }
81
82        if ( preg_match( '!/update/?$!', $this->path ) ) {
83            $this->action = 'update';
84
85        } elseif ( preg_match( '/\/install\/?$/', $this->path ) ) {
86            $this->action = 'install';
87
88        } elseif ( ! empty( $args['action'] ) ) {
89            if ( ! in_array( $args['action'], $this->expected_actions, true ) ) {
90                return new WP_Error( 'invalid_action', __( 'You must specify a valid action', 'jetpack' ) );
91            }
92            $this->action = $args['action'];
93        }
94        return true;
95    }
96
97    /**
98     * Switches to the blog and checks current user capabilities.
99     *
100     * @param int   $_blog_id - the blog ID.
101     * @param array $capability - the capabilities of the user.
102     * @param bool  $check_validation - if we're checking the validation.
103     *
104     * @return bool|WP_Error a WP_Error object or true if things are good.
105     */
106    protected function validate_call( $_blog_id, $capability, $check_validation = true ) {
107        $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $_blog_id ) );
108        if ( is_wp_error( $blog_id ) ) {
109            return $blog_id;
110        }
111
112        $error = $this->check_capability( $capability );
113        if ( is_wp_error( $error ) ) {
114            return $error;
115        }
116
117        if (
118            $check_validation &&
119            'GET' !== $this->method &&
120            /**
121             * Filter to disallow JSON API requests to the site.
122             * Setting to false disallows you to manage your site remotely from WordPress.com
123             * and disallows plugin auto-updates.
124             *
125             * @since 7.3.0
126             *
127             * @param bool $check_validation Whether to allow API requests to manage the site
128             */
129            ! apply_filters( 'jetpack_json_manage_api_enabled', $check_validation )
130        ) {
131            return new WP_Error( 'unauthorized_full_access', __( 'Full management mode is off for this site.', 'jetpack' ), 403 );
132        }
133
134        return true;
135    }
136
137    /**
138     * Check capability.
139     *
140     * @param array $capability - the compatability.
141     *
142     * @return bool|WP_Error
143     */
144    protected function check_capability( $capability ) {
145        // If this endpoint accepts site based authentication, skip capabilities check.
146        if ( $this->accepts_site_based_authentication() ) {
147            return true;
148        }
149        if ( is_array( $capability ) ) {
150            // the idea is that the we can pass in an array of capabilitie that the user needs to have before we allowing them to do something
151            $capabilities = ( isset( $capability['capabilities'] ) ? $capability['capabilities'] : $capability );
152
153            // We can pass in the number of conditions we must pass by default it is all.
154            $must_pass = ( isset( $capability['must_pass'] ) && is_int( $capability['must_pass'] ) ? $capability['must_pass'] : count( $capabilities ) );
155
156            $failed = array(); // store the failed capabilities
157            $passed = 0;
158            foreach ( $capabilities as $cap ) {
159                if ( current_user_can( $cap ) ) {
160                    ++$passed;
161                } else {
162                    $failed[] = $cap;
163                }
164            }
165            // Check if all conditions have passed.
166            if ( $passed < $must_pass ) {
167                return new WP_Error(
168                    'unauthorized',
169                    /* translators: %s: comma-separated list of capabilities */
170                    sprintf( __( 'This user is not authorized to %s on this blog.', 'jetpack' ), implode( ', ', $failed ) ),
171                    403
172                );
173            }
174        } elseif ( ! current_user_can( $capability ) ) {
175            // Translators: the capability that the user is not authorized for.
176            return new WP_Error( 'unauthorized', sprintf( __( 'This user is not authorized to %s on this blog.', 'jetpack' ), $capability ), 403 );
177        }
178
179        return true;
180    }
181}