| @@ -18,8 +18,23 @@ | ||
| 18 | 18 | } |
| 19 | 19 | |
| 20 | 20 | public function checkout() { |
| 21 | 21 | $purchase_type = $this->get_param( 'purchase_type', 'template' ); |
| 22 | + | |
| 23 | + /** | |
| 24 | + * Buying a template is a content decision any contributor may make, but a | |
| 25 | + * subscription is bought *for the account* and changes what everyone on it | |
| 26 | + * pays. The base gate is `delete_posts`, so without this a Contributor | |
| 27 | + * could start a plan purchase against the site owner's account. | |
| 28 | + */ | |
| 29 | + if ( 'subscription' === $purchase_type && ! current_user_can( 'manage_options' ) ) { | |
| 30 | + return $this->error( | |
| 31 | + 'insufficient_permission', | |
| 32 | + __( 'Only administrators can change the subscription.', 'templately' ), | |
| 33 | + 'checkout', | |
| 34 | + rest_authorization_required_code() | |
| 35 | + ); | |
| 36 | + } | |
| 22 | 37 | $id = $this->get_param( 'id', 0, 'intval' ); |
| 23 | 38 | $return_url = $this->get_param( 'return_url', admin_url( 'admin.php?page=templately' ), 'esc_url_raw' ); |
| 24 | 39 | |
| 25 | 40 | if ( empty( $id ) ) { |
| @@ -49,9 +64,24 @@ | ||
| 49 | 64 | $data = ! empty( $response['data'] ) ? json_decode( $response['data'], true ) : []; |
| 50 | 65 | |
| 51 | 66 | // Frontend checkout URL created — the app redirects the buyer to it. |
| 52 | 67 | if ( ! empty( $data['url'] ) ) { |
| 53 | - return $this->success( [ 'url' => $data['url'] ] ); | |
| 68 | + /** | |
| 69 | + * The client assigns this straight to `window.location.href`, so verify | |
| 70 | + * it is an https URL on a Templately host before handing it over. The | |
| 71 | + * response is our own API's, but a redirect target that arrives over the | |
| 72 | + * wire and is followed unchecked is an open redirect waiting to happen. | |
| 73 | + */ | |
| 74 | + $url = esc_url_raw( $data['url'] ); | |
| 75 | + $host = wp_parse_url( $url, PHP_URL_HOST ); | |
| 76 | + | |
| 77 | + $allowed = $host && ( 'templately.com' === $host || 'templately.dev' === $host || str_ends_with( $host, '.templately.com' ) || str_ends_with( $host, '.templately.dev' ) ); | |
| 78 | + | |
| 79 | + if ( ! $allowed || 'https' !== wp_parse_url( $url, PHP_URL_SCHEME ) ) { | |
| 80 | + return $this->error( 'invalid_checkout_url', __( 'The checkout could not be verified. Please try again.', 'templately' ), 'checkout' ); | |
| 81 | + } | |
| 82 | + | |
| 83 | + return $this->success( [ 'url' => $url ] ); | |
| 54 | 84 | } |
| 55 | 85 | |
| 56 | 86 | $message = ! empty( $response['message'] ) ? $response['message'] : __( 'Could not start the checkout. Please try again.', 'templately' ); |
| 57 | 87 | |