diff --git a/includes/class-mastodon-api.php b/includes/class-mastodon-api.php index 7e786c70..9f112301 100644 --- a/includes/class-mastodon-api.php +++ b/includes/class-mastodon-api.php @@ -228,7 +228,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => array( $this, 'api_announcements' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:announcements' ), ) ); register_rest_route( @@ -274,7 +274,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:follows,follow' ), ) ); register_rest_route( @@ -283,7 +283,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:follows' ), ) ); register_rest_route( @@ -292,7 +292,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:bookmarks' ), ) ); register_rest_route( @@ -301,7 +301,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses' ), ) ); @@ -311,7 +311,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:favourites' ), ) ); register_rest_route( @@ -320,7 +320,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:filters' ), ) ); register_rest_route( @@ -329,7 +329,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:lists' ), ) ); @@ -339,7 +339,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses' ), ) ); @@ -349,7 +349,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:mutes' ), ) ); @@ -359,7 +359,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_notification_clear' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:notifications' ), ) ); @@ -369,7 +369,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_notification_dismiss' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:notifications' ), ) ); @@ -379,7 +379,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_notification_get' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:notifications' ), ) ); @@ -389,7 +389,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => array( $this, 'api_notifications' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:notifications' ), ) ); @@ -399,7 +399,7 @@ public function add_rest_routes() { array( 'methods' => 'GET', 'callback' => array( $this, 'api_preferences' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:accounts' ), ) ); @@ -420,13 +420,15 @@ public function add_rest_routes() { 'callback' => '__return_empty_array', 'permission_callback' => array( $this, 'public_api_permission' ), ) - ); register_rest_route( + ); + + register_rest_route( self::PREFIX, 'api/v1/accounts/familiar_followers', array( 'methods' => 'GET', 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:follows' ), ) ); @@ -436,7 +438,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_verify_credentials' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'follow:accounts' ), ) ); @@ -446,7 +448,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_accounts_search' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:accounts' ), ) ); @@ -456,7 +458,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_post_media' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:media' ), ) ); @@ -466,7 +468,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_get_media' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:media' ), ) ); @@ -476,7 +478,7 @@ public function add_rest_routes() { array( 'methods' => array( 'PUT', 'OPTIONS' ), 'callback' => array( $this, 'api_update_media' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:media' ), ) ); @@ -486,7 +488,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_submit_post' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:statuses' ), ) ); @@ -496,7 +498,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_get_post_context' ), - 'permission_callback' => array( $this, 'logged_in_for_private_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses', true ), ) ); @@ -506,7 +508,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses', true ), ) ); @@ -516,7 +518,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_favourite_post' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:favourites' ), ) ); @@ -526,7 +528,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_unfavourite_post' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:favourites' ), ) ); @@ -536,7 +538,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_reblog_post' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:statuses' ), ) ); @@ -546,7 +548,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_unreblog_post' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:statuses' ), ) ); @@ -556,7 +558,7 @@ public function add_rest_routes() { array( 'methods' => array( 'DELETE', 'OPTIONS' ), 'callback' => array( $this, 'api_delete_post' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:statuses' ), ) ); @@ -566,7 +568,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_get_post' ), - 'permission_callback' => array( $this, 'logged_in_for_private_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses', true ), ) ); @@ -576,7 +578,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_timelines' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses' ), ) ); @@ -586,7 +588,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_tag_timelines' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses' ), ) ); @@ -596,7 +598,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_public_timeline' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => array( $this, 'public_api_permission' ), ) ); @@ -606,7 +608,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses' ), ) ); @@ -616,7 +618,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_push_subscription' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'push' ), ) ); @@ -636,7 +638,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_account_relationships' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:follows' ), ) ); @@ -646,7 +648,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => '__return_empty_array', - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => array( $this, 'public_api_permission' ), ) ); @@ -656,7 +658,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_account_followers' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => array( $this, 'public_api_permission' ), ) ); @@ -666,7 +668,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_account_follow' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:follows' ), ) ); @@ -676,7 +678,7 @@ public function add_rest_routes() { array( 'methods' => array( 'POST', 'OPTIONS' ), 'callback' => array( $this, 'api_account_unfollow' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'write:follows' ), ) ); @@ -686,7 +688,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_account_statuses' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => $this->required_scope( 'read:statuses', true ), ) ); @@ -696,7 +698,7 @@ public function add_rest_routes() { array( 'methods' => array( 'GET', 'OPTIONS' ), 'callback' => array( $this, 'api_account' ), - 'permission_callback' => array( $this, 'logged_in_permission' ), + 'permission_callback' => array( $this, 'public_api_permission' ), ) ); } @@ -706,6 +708,38 @@ public function query_vars( $query_vars ) { return $query_vars; } + public function ensure_required_scope( $request, $scopes, $also_public ) { + if ( $also_public ) { + if ( ! $this->logged_in_for_private_permission( $request ) ) { + return new \WP_Error( 'token-required', 'Token required', array( 'status' => 401 ) ); + } + } elseif ( ! $this->logged_in_permission( $request ) ) { + return new \WP_Error( 'token-required', 'Token required', array( 'status' => 401 ) ); + } + + $has_scope = false; + $token = $this->oauth->get_token(); + if ( $token && isset( $token['scope'] ) ) { + foreach ( explode( ',', $scopes ) as $scope ) { + if ( OAuth2\Scope_Util::checkSingleScope( $scope, $token['scope'] ) ) { + $has_scope = true; + } + } + } + + if ( ! $has_scope && ! $also_public ) { + return new \WP_Error( 'insufficient-permissions', 'Insufficient permissions', array( 'status' => 401 ) ); + } + + return true; + } + + public function required_scope( $scopes, $also_public = false ) { + return function ( $request ) use ( $scopes, $also_public ) { + return $this->ensure_required_scope( $request, $scopes, $also_public ); + }; + } + public function public_api_permission( $request ) { $this->allow_cors(); // Optionally log in. @@ -1018,7 +1052,7 @@ private function get_posts( $args, $min_id = null, $max_id = null ) { private function get_comment_status_array( \WP_Comment $comment ) { if ( ! $comment ) { - return new \WP_Error( 'record-not-found', 'Record not found', array( 'status' => 404 ) ); + return new \WP_Error( 'mastodon_' . __FUNCTION__, 'Record not found', array( 'status' => 404 ) ); } $post = (object) array( @@ -1304,12 +1338,12 @@ private function get_notification_array( $type, $date, $account, $status = array public function api_submit_post( $request ) { if ( ! current_user_can( 'edit_posts' ) ) { - return new \WP_Error( 'mastodon_api_submit_post', 'The access token is invalid', array( 'status' => 401 ) ); + return new \WP_Error( 'mastodon_' . __FUNCTION__, 'Insufficient permissions', array( 'status' => 401 ) ); } $status = $request->get_param( 'status' ); if ( empty( $status ) ) { - return new \WP_Error( 'mastodon_api_submit_post', 'Validation failed: Text can\'t be blank', array( 'status' => 422 ) ); + return new \WP_Error( 'mastodon_' . __FUNCTION__, 'Validation failed: Text can\'t be blank', array( 'status' => 422 ) ); } $status = make_clickable( $status ); @@ -1396,10 +1430,10 @@ public function api_submit_post( $request ) { foreach ( $media_ids as $media_id ) { $media = get_post( $media_id ); if ( ! $media ) { - return new \WP_Error( 'mastodon_api_submit_post', 'Media not found', array( 'status' => 400 ) ); + return new \WP_Error( 'mastodon_' . __FUNCTION__, 'Media not found', array( 'status' => 400 ) ); } if ( 'attachment' !== $media->post_type ) { - return new \WP_Error( 'mastodon_api_submit_post', 'Media not found', array( 'status' => 400 ) ); + return new \WP_Error( 'mastodon_' . __FUNCTION__, 'Media not found', array( 'status' => 400 ) ); } $attachment = \wp_get_attachment_metadata( $media_id ); $post_data['post_content'] .= PHP_EOL; @@ -1841,6 +1875,10 @@ public function api_get_post( $request ) { return new \WP_REST_Response( array( 'error' => 'Record not found' ), 404 ); } + if ( get_post_status( $post_id ) !== 'publish' && ! current_user_can( 'edit_post', $post_id ) ) { + return new \WP_REST_Response( array( 'error' => 'Record not found' ), 404 ); + } + $comment_id = $this->get_remapped_comment_id( $post_id ); if ( $comment_id ) { return $this->get_comment_status_array( get_comment( $comment_id ) ); @@ -2445,6 +2483,9 @@ private function get_friend_account_data( $user_id, $meta = array(), $full_metad preg_match( '/^@?' . self::ACTIVITYPUB_USERNAME_REGEXP . '$/i', $user_id ) || $url ) { + if ( ! is_user_logged_in() ) { + return new \WP_Error( 'not-logged-in', 'Not logged in', array( 'status' => 401 ) ); + } $account = $this->get_acct( $user_id ); if ( $account ) { diff --git a/includes/class-mastodon-app.php b/includes/class-mastodon-app.php index caee4707..7fe4c93e 100644 --- a/includes/class-mastodon-app.php +++ b/includes/class-mastodon-app.php @@ -118,23 +118,6 @@ public function check_redirect_uri( $redirect_uri ) { return false; } - public function check_scopes( $requested_scopes ) { - $allowed_scopes = explode( ' ', $this->get_scopes() ); - - foreach ( explode( ' ', $requested_scopes ) as $s ) { - if ( false !== strpos( $s, ':' ) ) { - list( $scope, $subscope ) = explode( ':', $s, 2 ); - } else { - $scope = $s; - } - if ( ! in_array( $scope, $allowed_scopes, true ) ) { - return false; - } - } - - return false; - } - public function delete_last_requests() { return delete_metadata( 'term', $this->term->term_id, 'request' ); } @@ -512,6 +495,10 @@ function ( $post_format ) { return $args; } + public function has_scope( $requested_scope ) { + return OAuth2\Scope_Util::checkSingleScope( $requested_scope, $this->get_scopes() ); + } + /** * Get an app via client_id. * diff --git a/includes/class-mastodon-oauth.php b/includes/class-mastodon-oauth.php index d3a7084e..75b9b5ce 100644 --- a/includes/class-mastodon-oauth.php +++ b/includes/class-mastodon-oauth.php @@ -44,6 +44,7 @@ public function __construct() { $this->server = new Server( new Oauth2\Authorization_Code_Storage(), $config ); $this->server->addStorage( new Oauth2\Mastodon_App_Storage(), 'client_credentials' ); $this->server->addStorage( new Oauth2\Access_Token_Storage(), 'access_token' ); + $this->server->setScopeUtil( new Oauth2\Scope_Util() ); if ( '/oauth/token' === strtok( $_SERVER['REQUEST_URI'], '?' ) ) { // Avoid interference with private site plugins. diff --git a/includes/oauth2/class-access-token-storage.php b/includes/oauth2/class-access-token-storage.php index 7ebd6b52..7b0dc42b 100644 --- a/includes/oauth2/class-access-token-storage.php +++ b/includes/oauth2/class-access-token-storage.php @@ -167,14 +167,8 @@ public function getAccessToken( $oauth_token ) { $access_token = array( 'access_token' => $oauth_token, ); - foreach ( array( - 'client_id' => 'client_id', - 'user_id' => 'user_id', - 'expires' => 'expires', - 'redirect_uri' => 'redirect_uri', - 'scope' => 'scope', - ) as $key => $meta_key ) { - $access_token[ $key ] = get_term_meta( $term->term_id, $meta_key, true ); + foreach ( array_keys( self::$access_token_data ) as $meta_key ) { + $access_token[ $meta_key ] = get_term_meta( $term->term_id, $meta_key, true ); } $access_token['created_at'] = $access_token['expires'] - YEAR_IN_SECONDS * 2; diff --git a/includes/oauth2/class-authenticate-handler.php b/includes/oauth2/class-authenticate-handler.php index 1b6aafee..da6f97ed 100644 --- a/includes/oauth2/class-authenticate-handler.php +++ b/includes/oauth2/class-authenticate-handler.php @@ -35,14 +35,21 @@ public function handle( Request $request, Response $response ) { return $redirect_uri; } - $scopes = $app->check_scopes( $_GET['scope'] ); - if ( is_wp_error( $scopes ) ) { - return $scopes; + $scopes = array(); + foreach ( explode( ' ', $_GET['scope'] ) as $scope ) { + if ( $app->has_scope( $scope ) ) { + $scopes[] = $scope; + } + } + if ( empty( $scopes ) ) { + $response->setError( 403, 'invalid_scopes', 'Invalid scope was requested.' ); + return $response; } $data = array( 'user' => wp_get_current_user(), 'client_name' => $client_name, + 'scopes' => implode( ' ', $scopes ), 'body_class_attr' => implode( ' ', array_diff( get_body_class(), array( 'error404' ) ) ), 'cancel_url' => $this->get_cancel_url( $request ), 'form_url' => home_url( '/oauth/authorize' ), @@ -96,6 +103,27 @@ private function render_no_permission_screen( $data ) { } private function render_consent_screen( $data ) { + $scope_explanations = array( + 'read' => __( 'Read information from your account, for example read your statuses.', 'enable-mastodon-apps' ), + 'write' => __( 'Write information to your account, for example post a status on your behalf.', 'enable-mastodon-apps' ), + 'follow' => __( 'Follow other accounts using your account.', 'enable-mastodon-apps' ), + 'push' => __( 'Subscribe to push events for your account.', 'enable-mastodon-apps' ), + ); + + $requested_scopes = array(); + foreach ( explode( ' ', $data['scopes'] ) as $scope ) { + $p = strpos( $scope, ':' ); + if ( false === $p ) { + $requested_scopes[ $scope ] = array( 'all' => true ); + } else { + $main_scope = substr( $scope, 0, $p ); + if ( ! isset( $requested_scopes[ $main_scope ] ) ) { + $requested_scopes[ $main_scope ] = array(); + } + $requested_scopes[ $main_scope ][ substr( $scope, $p + 1 ) ] = true; + } + } + ?>
@@ -113,23 +141,52 @@ private function render_consent_screen( $data ) {

- + __( 'Do you want to log in to %1$s with your %2$s account?', 'enable-mastodon-apps' ), + $data['client_name'], + get_bloginfo( 'name' ) + ), + array( + 'strong' => array(), + ) + ); + ?>


+

+ +

+ +
$value ) : ?> diff --git a/includes/oauth2/class-scope-util.php b/includes/oauth2/class-scope-util.php new file mode 100644 index 00000000..74c6e83d --- /dev/null +++ b/includes/oauth2/class-scope-util.php @@ -0,0 +1,36 @@ +post, 'status' ); + + $this->private_post = wp_insert_post( + array( + 'post_author' => $this->administrator, + 'post_content' => 'Private post', + 'post_title' => '', + 'post_status' => 'private', + 'post_type' => 'post', + 'post_date' => '2023-01-03 00:00:00', + ) + ); + set_post_format( $this->post, 'status' ); $args = array( 'supports' => array( 'title', 'editor', 'author', 'revisions', 'thumbnail', 'excerpt', 'comments', 'post-formats' ), 'taxonomies' => array( 'post_tag', 'post_format' ), diff --git a/tests/test-mastodon-app.php b/tests/test-mastodon-app.php new file mode 100644 index 00000000..9de27828 --- /dev/null +++ b/tests/test-mastodon-app.php @@ -0,0 +1,52 @@ +assertInstanceOf( Mastodon_App::class, $app ); + } + + public function test_create_app_with_empty_scope() { + $this->expectException( \Exception::class ); + $app = Mastodon_App::save( 'test', array( Mastodon_OAuth::OOB_REDIRECT_URI ), '', '' ); + } + + /** + * Scopes to test + * + * @param string $app_scopes The application scopes. + * @param string $scope_to_test The scope to test. + * @param bool $has_scope Indicates if the test should assume the scope to be existent. + * @dataProvider scopes + */ + public function test_scope_given( $app_scopes, $scope_to_test, $has_scope ) { + $app = Mastodon_App::save( 'test', array( Mastodon_OAuth::OOB_REDIRECT_URI ), $app_scopes, '' ); + $this->assertEquals( $has_scope, $app->has_scope( $scope_to_test ) ); + } + + public function scopes() { + return array( + array( 'read', 'read', true ), + array( 'read', 'read:accounts', true ), + array( 'read:accounts', 'read:accounts', true ), + array( 'read:accounts', 'read', false ), + array( 'write', 'read', false ), + array( 'read', 'write', false ), + array( 'read write', 'write', true ), + array( 'read write push', 'write', true ), + array( 'read', 'write:accounts', false ), + ); + } +} diff --git a/tests/test-statuses-endpoint.php b/tests/test-statuses-endpoint.php index 42f16e5c..9a82dfd8 100644 --- a/tests/test-statuses-endpoint.php +++ b/tests/test-statuses-endpoint.php @@ -47,6 +47,18 @@ public function test_statuses_id() { $this->assertIsInt( $data['favourites_count'] ); } + public function test_statuses_private_id() { + global $wp_rest_server; + + $request = new \WP_REST_Request( 'GET', '/' . Mastodon_API::PREFIX . '/api/v1/statuses/' . $this->private_post ); + $response = $wp_rest_server->dispatch( $request ); + $this->assertEquals( 401, $response->get_status() ); + + $_SERVER['HTTP_AUTHORIZATION'] = 'Bearer ' . $this->token; + $response = $wp_rest_server->dispatch( $request ); + $this->assertEquals( 200, $response->get_status() ); + } + public function test_statuses_delete() { global $wp_rest_server; $request = new \WP_REST_Request( 'DELETE', '/' . Mastodon_API::PREFIX . '/api/v1/statuses/' . $this->post );