Răsfoiți Sursa

Implement proper OAuth authorization on API endpoints

Emelia Smith 1 an în urmă
părinte
comite
0f8e45fe75

+ 188 - 107
app/Http/Controllers/Api/ApiV1Controller.php

@@ -125,11 +125,15 @@ class ApiV1Controller extends Controller
         return response()->json($res, $code, $headers, JSON_UNESCAPED_SLASHES);
     }
 
+    /**
+     * GET /api/v1/apps/verify_credentials
+     */
     public function getApp(Request $request)
     {
-        if(!$request->user()) {
-            return response('', 403);
-        }
+        # FIXME: /api/v1/apps/verify_credentials should be accessible with any
+        # valid Access Token, not just a user's access token (i.e., client
+        # credentails grant flow access tokens)
+        abort_if(!$request->user() || !$request->user()->token(), 403);
 
         $client = $request->user()->token()->client;
         $res = [
@@ -141,6 +145,9 @@ class ApiV1Controller extends Controller
         return $this->json($res);
     }
 
+    /**
+     * POST /api/v1/apps
+     */
     public function apps(Request $request)
     {
         abort_if(!config_cache('pixelfed.oauth_enabled'), 404);
@@ -187,9 +194,11 @@ class ApiV1Controller extends Controller
      */
     public function verifyCredentials(Request $request)
     {
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $user = $request->user();
 
-        abort_if(!$user, 403);
         abort_if($user->status != null, 403);
         AccountService::setLastActive($user->id);
 
@@ -215,6 +224,9 @@ class ApiV1Controller extends Controller
      */
     public function accountById(Request $request, $id)
     {
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $res = $request->has(self::PF_API_ENTITY_KEY) ? AccountService::get($id, true) : AccountService::getMastodon($id, true);
         if(!$res) {
             return response()->json(['error' => 'Record not found'], 404);
@@ -233,7 +245,8 @@ class ApiV1Controller extends Controller
      */
     public function accountUpdateCredentials(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         if(config('pixelfed.bouncer.cloud_ips.ban_api')) {
             abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -476,7 +489,8 @@ class ApiV1Controller extends Controller
      */
     public function accountFollowersById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $account = AccountService::get($id);
         abort_if(!$account, 404);
@@ -573,7 +587,8 @@ class ApiV1Controller extends Controller
      */
     public function accountFollowingById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $account = AccountService::get($id);
         abort_if(!$account, 404);
@@ -670,6 +685,9 @@ class ApiV1Controller extends Controller
      */
     public function accountStatusesById(Request $request, $id)
     {
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $user = $request->user();
 
         $this->validate($request, [
@@ -774,7 +792,8 @@ class ApiV1Controller extends Controller
      */
     public function accountFollowById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('follow'), 403);
 
         $user = $request->user();
         abort_if($user->has_roles && !UserRoleService::can('can-follow', $user->id), 403, 'Invalid permissions for this action');
@@ -866,7 +885,8 @@ class ApiV1Controller extends Controller
      */
     public function accountUnfollowById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('follow'), 403);
 
         $user = $request->user();
 
@@ -936,7 +956,8 @@ class ApiV1Controller extends Controller
      */
     public function accountRelationshipsById(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'id'    => 'required|array|min:1|max:20',
@@ -965,7 +986,8 @@ class ApiV1Controller extends Controller
      */
     public function accountSearch(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'q'         => 'required|string|min:1|max:255',
@@ -1008,7 +1030,8 @@ class ApiV1Controller extends Controller
      */
     public function accountBlocks(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit'     => 'nullable|integer|min:1|max:40',
@@ -1045,7 +1068,8 @@ class ApiV1Controller extends Controller
      */
     public function accountBlockById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         $pid = $user->profile_id ?? $user->profile->id;
@@ -1138,7 +1162,8 @@ class ApiV1Controller extends Controller
      */
     public function accountUnblockById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         $pid = $user->profile_id ?? $user->profile->id;
@@ -1189,7 +1214,9 @@ class ApiV1Controller extends Controller
      */
     public function accountDomainBlocks(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         return response()->json([]);
     }
 
@@ -1202,7 +1229,9 @@ class ApiV1Controller extends Controller
      */
     public function accountEndorsements(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         return response()->json([]);
     }
 
@@ -1215,7 +1244,9 @@ class ApiV1Controller extends Controller
      */
     public function accountFavourites(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $this->validate($request, [
             'limit' => 'sometimes|integer|min:1|max:40'
         ]);
@@ -1271,7 +1302,8 @@ class ApiV1Controller extends Controller
      */
     public function statusFavouriteById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         abort_if($user->has_roles && !UserRoleService::can('can-like', $user->id), 403, 'Invalid permissions for this action');
@@ -1338,7 +1370,8 @@ class ApiV1Controller extends Controller
      */
     public function statusUnfavouriteById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         abort_if($user->has_roles && !UserRoleService::can('can-like', $user->id), 403, 'Invalid permissions for this action');
@@ -1381,7 +1414,8 @@ class ApiV1Controller extends Controller
      */
     public function accountFilters(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         return response()->json([]);
     }
@@ -1395,7 +1429,9 @@ class ApiV1Controller extends Controller
      */
     public function accountFollowRequests(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $this->validate($request, [
             'limit' => 'sometimes|integer|min:1|max:100'
         ]);
@@ -1425,7 +1461,9 @@ class ApiV1Controller extends Controller
      */
     public function accountFollowRequestAccept(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('follow'), 403);
+
         $pid = $request->user()->profile_id;
         $target = AccountService::getMastodon($id);
 
@@ -1482,7 +1520,9 @@ class ApiV1Controller extends Controller
      */
     public function accountFollowRequestReject(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('follow'), 403);
+
         $pid = $request->user()->profile_id;
         $target = AccountService::getMastodon($id);
 
@@ -1518,7 +1558,8 @@ class ApiV1Controller extends Controller
      */
     public function accountSuggestions(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         // todo
 
@@ -1619,7 +1660,8 @@ class ApiV1Controller extends Controller
      */
     public function accountLists(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         return response()->json([]);
     }
@@ -1633,7 +1675,8 @@ class ApiV1Controller extends Controller
      */
     public function accountListsById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         return response()->json([]);
     }
@@ -1646,7 +1689,8 @@ class ApiV1Controller extends Controller
      */
     public function mediaUpload(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $this->validate($request, [
             'file.*' => [
@@ -1782,7 +1826,8 @@ class ApiV1Controller extends Controller
      */
     public function mediaUpdate(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $this->validate($request, [
           'description' => 'nullable|string|max:' . config_cache('pixelfed.max_altext_length')
@@ -1835,7 +1880,8 @@ class ApiV1Controller extends Controller
      */
     public function mediaGet(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $user = $request->user();
         abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action');
@@ -1858,7 +1904,8 @@ class ApiV1Controller extends Controller
      */
     public function mediaUploadV2(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $this->validate($request, [
             'file.*' => [
@@ -1999,7 +2046,8 @@ class ApiV1Controller extends Controller
      */
     public function accountMutes(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit' => 'nullable|integer|min:1|max:40'
@@ -2034,7 +2082,8 @@ class ApiV1Controller extends Controller
      */
     public function accountMuteById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         $pid = $user->profile_id;
@@ -2092,7 +2141,8 @@ class ApiV1Controller extends Controller
      */
     public function accountUnmuteById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         $pid = $user->profile_id;
@@ -2128,7 +2178,8 @@ class ApiV1Controller extends Controller
      */
     public function accountNotifications(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit' => 'nullable|integer|min:1|max:100',
@@ -2204,7 +2255,10 @@ class ApiV1Controller extends Controller
      */
     public function timelineHome(Request $request)
     {
-        $this->validate($request,[
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
+        $this->validate($request, [
             'page'        => 'sometimes|integer|max:40',
             'min_id'      => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
             'max_id'      => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
@@ -2606,7 +2660,9 @@ class ApiV1Controller extends Controller
      */
     public function conversations(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $this->validate($request, [
             'limit' => 'min:1|max:40',
             'scope' => 'nullable|in:inbox,sent,requests'
@@ -2683,7 +2739,9 @@ class ApiV1Controller extends Controller
      */
     public function statusById(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         AccountService::setLastActive($request->user()->id);
         $pid = $request->user()->profile_id;
 
@@ -2730,7 +2788,8 @@ class ApiV1Controller extends Controller
      */
     public function statusContext(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $user = $request->user();
         AccountService::setLastActive($user->id);
@@ -2803,7 +2862,9 @@ class ApiV1Controller extends Controller
      */
     public function statusCard(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $res = [];
         return response()->json($res);
     }
@@ -2817,7 +2878,8 @@ class ApiV1Controller extends Controller
      */
     public function statusRebloggedBy(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit' => 'sometimes|integer|min:1|max:80'
@@ -2913,7 +2975,8 @@ class ApiV1Controller extends Controller
      */
     public function statusFavouritedBy(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit' => 'nullable|integer|min:1|max:80'
@@ -3010,7 +3073,8 @@ class ApiV1Controller extends Controller
      */
     public function statusCreate(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $this->validate($request, [
             'status' => 'nullable|string',
@@ -3225,7 +3289,9 @@ class ApiV1Controller extends Controller
      */
     public function statusDelete(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
+
         AccountService::setLastActive($request->user()->id);
         $status = Status::whereProfileId($request->user()->profile->id)
         ->findOrFail($id);
@@ -3251,7 +3317,8 @@ class ApiV1Controller extends Controller
      */
     public function statusShare(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         abort_if($user->has_roles && !UserRoleService::can('can-share', $user->id), 403, 'Invalid permissions for this action');
@@ -3303,7 +3370,8 @@ class ApiV1Controller extends Controller
      */
     public function statusUnshare(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $user = $request->user();
         abort_if($user->has_roles && !UserRoleService::can('can-share', $user->id), 403, 'Invalid permissions for this action');
@@ -3346,7 +3414,8 @@ class ApiV1Controller extends Controller
      */
     public function timelineHashtag(Request $request, $hashtag)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request,[
           'page'        => 'nullable|integer|max:40',
@@ -3447,7 +3516,8 @@ class ApiV1Controller extends Controller
      */
     public function bookmarks(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit' => 'nullable|integer|min:1|max:40',
@@ -3514,7 +3584,8 @@ class ApiV1Controller extends Controller
      */
     public function bookmarkStatus(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $status = Status::findOrFail($id);
         $pid = $request->user()->profile_id;
@@ -3554,7 +3625,8 @@ class ApiV1Controller extends Controller
      */
     public function unbookmarkStatus(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $status = Status::findOrFail($id);
         $pid = $request->user()->profile_id;
@@ -3586,7 +3658,8 @@ class ApiV1Controller extends Controller
      */
     public function discoverPosts(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit' => 'integer|min:1|max:40'
@@ -3596,29 +3669,30 @@ class ApiV1Controller extends Controller
         $pid = $request->user()->profile_id;
         $filters = UserFilterService::filters($pid);
         $forYou = DiscoverService::getForYou();
-        $posts = $forYou->take(50)->map(function($post) {
+        $posts = $forYou->take(50)->map(function ($post) {
             return StatusService::getMastodon($post);
         })
-        ->filter(function($post) use($filters) {
-            return $post &&
-                isset($post['account']) &&
-                isset($post['account']['id']) &&
-                !in_array($post['account']['id'], $filters);
-        })
-        ->take(12)
-        ->values();
+            ->filter(function ($post) use ($filters) {
+                return $post &&
+                    isset($post['account']) &&
+                    isset($post['account']['id']) &&
+                    !in_array($post['account']['id'], $filters);
+            })
+            ->take(12)
+            ->values();
         return $this->json(compact('posts'));
     }
 
     /**
-    * GET /api/v2/statuses/{id}/replies
-    *
-    *
-    * @return array
-    */
+     * GET /api/v2/statuses/{id}/replies
+     *
+     *
+     * @return array
+     */
     public function statusReplies(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'limit' => 'int|min:1|max:10',
@@ -3707,14 +3781,15 @@ class ApiV1Controller extends Controller
     }
 
     /**
-    * GET /api/v2/statuses/{id}/state
-    *
-    *
-    * @return array
-    */
+     * GET /api/v2/statuses/{id}/state
+     *
+     *
+     * @return array
+     */
     public function statusState(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $status = Status::findOrFail($id);
         $pid = $request->user()->profile_id;
@@ -3724,14 +3799,15 @@ class ApiV1Controller extends Controller
     }
 
     /**
-    * GET /api/v1.1/discover/accounts/popular
-    *
-    *
-    * @return array
-    */
+     * GET /api/v1.1/discover/accounts/popular
+     *
+     *
+     * @return array
+     */
     public function discoverAccountsPopular(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $pid = $request->user()->profile_id;
 
@@ -3766,14 +3842,15 @@ class ApiV1Controller extends Controller
     }
 
     /**
-    * GET /api/v1/preferences
-    *
-    *
-    * @return array
-    */
+     * GET /api/v1/preferences
+     *
+     *
+     * @return array
+     */
     public function getPreferences(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $pid = $request->user()->profile_id;
         $account = AccountService::get($pid);
@@ -3788,40 +3865,43 @@ class ApiV1Controller extends Controller
     }
 
     /**
-    * GET /api/v1/trends
-    *
-    *
-    * @return array
-    */
+     * GET /api/v1/trends
+     *
+     *
+     * @return array
+     */
     public function getTrends(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         return $this->json([]);
     }
 
     /**
-    * GET /api/v1/announcements
-    *
-    *
-    * @return array
-    */
+     * GET /api/v1/announcements
+     *
+     *
+     * @return array
+     */
     public function getAnnouncements(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         return $this->json([]);
     }
 
     /**
-    * GET /api/v1/markers
-    *
-    *
-    * @return array
-    */
+     * GET /api/v1/markers
+     *
+     *
+     * @return array
+     */
     public function getMarkers(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $type = $request->input('timeline');
         if(is_array($type)) {
@@ -3835,14 +3915,15 @@ class ApiV1Controller extends Controller
     }
 
     /**
-    * POST /api/v1/markers
-    *
-    *
-    * @return array
-    */
+     * POST /api/v1/markers
+     *
+     *
+     * @return array
+     */
     public function setMarkers(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $pid = $request->user()->profile_id;
         $home = $request->input('home[last_read_id]');

+ 41 - 18
app/Http/Controllers/Api/ApiV1Dot1Controller.php

@@ -68,9 +68,10 @@ class ApiV1Dot1Controller extends Controller
 
 	public function report(Request $request)
 	{
-		$user = $request->user();
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('write'), 403);
 
-		abort_if(!$user, 403);
+		$user = $request->user();
 		abort_if($user->status != null, 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@@ -175,9 +176,10 @@ class ApiV1Dot1Controller extends Controller
 	 */
 	public function deleteAvatar(Request $request)
 	{
-		$user = $request->user();
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('write'), 403);
 
-		abort_if(!$user, 403);
+		$user = $request->user();
 		abort_if($user->status != null, 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@@ -215,9 +217,10 @@ class ApiV1Dot1Controller extends Controller
 	 */
 	public function accountPosts(Request $request, $id)
 	{
-		$user = $request->user();
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
 
-		abort_if(!$user, 403);
+		$user = $request->user();
 		abort_if($user->status != null, 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@@ -255,8 +258,10 @@ class ApiV1Dot1Controller extends Controller
 	 */
 	public function accountChangePassword(Request $request)
 	{
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('write'), 403);
+
 		$user = $request->user();
-		abort_if(!$user, 403);
 		abort_if($user->status != null, 403);
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
 			abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -296,8 +301,10 @@ class ApiV1Dot1Controller extends Controller
 	 */
 	public function accountLoginActivity(Request $request)
 	{
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
+
 		$user = $request->user();
-		abort_if(!$user, 403);
 		abort_if($user->status != null, 403);
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
 			abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -336,8 +343,10 @@ class ApiV1Dot1Controller extends Controller
 	 */
 	public function accountTwoFactor(Request $request)
 	{
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
+
 		$user = $request->user();
-		abort_if(!$user, 403);
 		abort_if($user->status != null, 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@@ -358,8 +367,10 @@ class ApiV1Dot1Controller extends Controller
 	 */
 	public function accountEmailsFromPixelfed(Request $request)
 	{
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
+
 		$user = $request->user();
-		abort_if(!$user, 403);
 		abort_if($user->status != null, 403);
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
 			abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -433,8 +444,10 @@ class ApiV1Dot1Controller extends Controller
 	 */
 	public function accountApps(Request $request)
 	{
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
+
 		$user = $request->user();
-		abort_if(!$user, 403);
 		abort_if($user->status != null, 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
@@ -640,7 +653,8 @@ class ApiV1Dot1Controller extends Controller
 
 	public function archive(Request $request, $id)
 	{
-		abort_if(!$request->user(), 403);
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('write'), 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
 			abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -672,7 +686,8 @@ class ApiV1Dot1Controller extends Controller
 
 	public function unarchive(Request $request, $id)
 	{
-		abort_if(!$request->user(), 403);
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('write'), 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
 			abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -703,7 +718,8 @@ class ApiV1Dot1Controller extends Controller
 
 	public function archivedPosts(Request $request)
 	{
-		abort_if(!$request->user(), 403);
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
 			abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -719,7 +735,8 @@ class ApiV1Dot1Controller extends Controller
 
 	public function placesById(Request $request, $id, $slug)
 	{
-		abort_if(!$request->user(), 403);
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
 
 		if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
 			abort_if(BouncerService::checkIp($request->ip()), 404);
@@ -865,7 +882,9 @@ class ApiV1Dot1Controller extends Controller
 
 	public function getWebSettings(Request $request)
 	{
-		abort_if(!$request->user(), 403);
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('read'), 403);
+
         $uid = $request->user()->id;
         $settings = UserSetting::firstOrCreate([
             'user_id' => $uid
@@ -878,7 +897,9 @@ class ApiV1Dot1Controller extends Controller
 
     public function setWebSettings(Request $request)
     {
-        abort_if(!$request->user(), 403);
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('write'), 403);
+
         $this->validate($request, [
             'field' => 'required|in:enable_reblogs,hide_reblog_banner',
             'value' => 'required'
@@ -902,7 +923,9 @@ class ApiV1Dot1Controller extends Controller
 
     public function getMutualAccounts(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+		abort_if(!$request->user() || !$request->user()->token(), 403);
+		abort_unless($request->user()->tokenCan('follows'), 403);
+
         $account = AccountService::get($id, true);
         if(!$account || !isset($account['id'])) { return []; }
         $res = collect(FollowerService::mutualAccounts($request->user()->profile_id, $id))

+ 4 - 2
app/Http/Controllers/Api/ApiV2Controller.php

@@ -148,7 +148,8 @@ class ApiV2Controller extends Controller
      */
     public function search(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $this->validate($request, [
             'q' => 'required|string|min:1|max:100',
@@ -199,7 +200,8 @@ class ApiV2Controller extends Controller
      */
     public function mediaUploadV2(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $this->validate($request, [
             'file.*' => [

+ 18 - 8
app/Http/Controllers/Api/BaseApiController.php

@@ -56,7 +56,8 @@ class BaseApiController extends Controller
 
     public function notifications(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
 		$pid = $request->user()->profile_id;
 		$limit = $request->input('limit', 20);
@@ -98,7 +99,9 @@ class BaseApiController extends Controller
 
     public function avatarUpdate(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
+
         $this->validate($request, [
             'upload'   => 'required|mimetypes:image/jpeg,image/jpg,image/png|max:'.config('pixelfed.max_avatar_size'),
         ]);
@@ -134,9 +137,11 @@ class BaseApiController extends Controller
 
     public function verifyCredentials(Request $request)
     {
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $user = $request->user();
-        abort_if(!$user, 403);
-        if($user->status != null) {
+        if ($user->status != null) {
             Auth::logout();
             abort(403);
         }
@@ -146,7 +151,9 @@ class BaseApiController extends Controller
 
     public function accountLikes(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $this->validate($request, [
         	'page' => 'sometimes|int|min:1|max:20',
         	'limit' => 'sometimes|int|min:1|max:10'
@@ -173,7 +180,8 @@ class BaseApiController extends Controller
 
     public function archive(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $status = Status::whereNull('in_reply_to_id')
             ->whereNull('reblog_of_id')
@@ -201,7 +209,8 @@ class BaseApiController extends Controller
 
     public function unarchive(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $status = Status::whereNull('in_reply_to_id')
             ->whereNull('reblog_of_id')
@@ -228,7 +237,8 @@ class BaseApiController extends Controller
 
     public function archivedPosts(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $statuses = Status::whereProfileId($request->user()->profile_id)
             ->whereScope('archived')

+ 7 - 3
app/Http/Controllers/Api/V1/DomainBlockController.php

@@ -23,7 +23,9 @@ class DomainBlockController extends Controller
 
     public function index(Request $request)
     {
-        abort_unless($request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $this->validate($request, [
             'limit' => 'sometimes|integer|min:1|max:200'
         ]);
@@ -52,7 +54,8 @@ class DomainBlockController extends Controller
 
     public function store(Request $request)
     {
-        abort_unless($request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $this->validate($request, [
             'domain' => 'required|active_url|min:1|max:120'
@@ -99,7 +102,8 @@ class DomainBlockController extends Controller
 
     public function delete(Request $request)
     {
-        abort_unless($request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('write'), 403);
 
         $this->validate($request, [
             'domain' => 'required|min:1|max:120'

+ 11 - 5
app/Http/Controllers/Api/V1/TagsController.php

@@ -32,7 +32,9 @@ class TagsController extends Controller
     */
     public function relatedTags(Request $request, $tag)
     {
-        abort_unless($request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
+
         $tag = Hashtag::whereSlug($tag)->firstOrFail();
         return HashtagRelatedService::get($tag->id);
     }
@@ -45,7 +47,8 @@ class TagsController extends Controller
     */
     public function followHashtag(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('follow'), 403);
 
         $pid = $request->user()->profile_id;
         $account = AccountService::get($pid);
@@ -87,7 +90,8 @@ class TagsController extends Controller
     */
     public function unfollowHashtag(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('follow'), 403);
 
         $pid = $request->user()->profile_id;
         $account = AccountService::get($pid);
@@ -132,7 +136,8 @@ class TagsController extends Controller
     */
     public function getHashtag(Request $request, $id)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $pid = $request->user()->profile_id;
         $account = AccountService::get($pid);
@@ -172,7 +177,8 @@ class TagsController extends Controller
     */
     public function getFollowedTags(Request $request)
     {
-        abort_if(!$request->user(), 403);
+        abort_if(!$request->user() || !$request->user()->token(), 403);
+        abort_unless($request->user()->tokenCan('read'), 403);
 
         $account = AccountService::get($request->user()->profile_id);