diff --git a/CHANGELOG.md b/CHANGELOG.md index d939ea1e0..de5f73b61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ # Release Notes -## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.9...dev) +## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.11...dev) + +## [v0.11.11 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.10...v0.11.11) + +### Fixes +- Fix api endpoints ([fd7f5dbb](https://github.com/pixelfed/pixelfed/commit/fd7f5dbb)) + +## [v0.11.10 (2024-02-09)](https://github.com/pixelfed/pixelfed/compare/v0.11.9...v0.11.10) ### Added - Resilient Media Storage ([#4665](https://github.com/pixelfed/pixelfed/pull/4665)) ([fb1deb6](https://github.com/pixelfed/pixelfed/commit/fb1deb6)) diff --git a/app/Auth/BearerTokenResponse.php b/app/Auth/BearerTokenResponse.php index 79cd97c85..0e1aa8a19 100644 --- a/app/Auth/BearerTokenResponse.php +++ b/app/Auth/BearerTokenResponse.php @@ -18,8 +18,7 @@ class BearerTokenResponse extends \League\OAuth2\Server\ResponseTypes\BearerToke protected function getExtraParams(AccessTokenEntityInterface $accessToken) { return [ - 'created_at' => time(), - 'scope' => 'read write follow push' + 'created_at' => time(), ]; } } diff --git a/app/Http/Controllers/Api/AdminApiController.php b/app/Http/Controllers/Api/AdminApiController.php index 76f73e720..69ba54cee 100644 --- a/app/Http/Controllers/Api/AdminApiController.php +++ b/app/Http/Controllers/Api/AdminApiController.php @@ -40,16 +40,20 @@ class AdminApiController extends Controller { public function supported(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); return response()->json(['supported' => true]); } public function getStats(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); $res = AdminStatsService::summary(); $res['autospam_count'] = AccountInterstitial::whereType('post.autospam') @@ -60,8 +64,10 @@ class AdminApiController extends Controller public function autospam(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); $appeals = AccountInterstitial::whereType('post.autospam') ->whereNull('appeal_handled_at') @@ -95,8 +101,10 @@ class AdminApiController extends Controller public function autospamHandle(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:write'), 404); $this->validate($request, [ 'action' => 'required|in:dismiss,approve,dismiss-all,approve-all,delete-post,delete-account', @@ -239,8 +247,10 @@ class AdminApiController extends Controller public function modReports(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); $reports = Report::whereNull('admin_seen') ->orderBy('created_at','desc') @@ -285,8 +295,10 @@ class AdminApiController extends Controller public function modReportHandle(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:write'), 404); $this->validate($request, [ 'action' => 'required|string', @@ -343,8 +355,11 @@ class AdminApiController extends Controller public function getConfiguration(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); + abort_unless(config('instance.enable_cc'), 400); return collect([ @@ -386,8 +401,11 @@ class AdminApiController extends Controller public function updateConfiguration(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:write'), 404); + abort_unless(config('instance.enable_cc'), 400); $this->validate($request, [ @@ -448,8 +466,11 @@ class AdminApiController extends Controller public function getUsers(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); + $this->validate($request, [ 'sort' => 'sometimes|in:asc,desc', ]); @@ -466,8 +487,10 @@ class AdminApiController extends Controller public function getUser(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); $id = $request->input('user_id'); $key = 'pf-admin-api:getUser:byId:' . $id; @@ -497,8 +520,10 @@ class AdminApiController extends Controller public function userAdminAction(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:write'), 404); $this->validate($request, [ 'id' => 'required', @@ -669,8 +694,10 @@ class AdminApiController extends Controller public function instances(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:write'), 404); $this->validate($request, [ 'q' => 'sometimes', @@ -707,8 +734,10 @@ class AdminApiController extends Controller public function getInstance(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); $id = $request->input('id'); $res = Instance::findOrFail($id); @@ -718,8 +747,10 @@ class AdminApiController extends Controller public function moderateInstance(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:write'), 404); $this->validate($request, [ 'id' => 'required', @@ -742,8 +773,10 @@ class AdminApiController extends Controller public function refreshInstanceStats(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin == 1, 404); + abort_unless($request->user()->tokenCan('admin:write'), 404); $this->validate($request, [ 'id' => 'required', @@ -760,8 +793,10 @@ class AdminApiController extends Controller public function getAllStats(Request $request) { - abort_if(!$request->user(), 404); + abort_if(!$request->user() || !$request->user()->token(), 404); + abort_unless($request->user()->is_admin === 1, 404); + abort_unless($request->user()->tokenCan('admin:read'), 404); if($request->has('refresh')) { Cache::forget('admin-api:instance-all-stats-v1'); diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index b94a11c92..d1bd9cac2 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/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(); @@ -965,7 +985,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 +1029,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 +1067,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 +1161,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 +1213,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 +1228,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 +1243,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 +1301,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 +1369,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 +1413,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 +1428,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 +1460,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 +1519,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 +1557,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 +1659,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 +1674,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 +1688,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 +1825,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 +1879,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 +1903,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 +2045,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 +2081,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 +2140,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 +2177,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 +2254,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 +2659,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 +2738,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 +2787,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 +2861,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 +2877,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 +2974,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 +3072,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 +3288,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 +3316,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 +3369,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 +3413,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 +3515,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 +3583,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 +3624,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 +3657,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,26 +3668,26 @@ 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); @@ -3707,11 +3779,11 @@ 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); @@ -3724,14 +3796,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 +3839,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 +3862,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 +3912,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]'); diff --git a/app/Http/Controllers/Api/ApiV1Dot1Controller.php b/app/Http/Controllers/Api/ApiV1Dot1Controller.php index 75d0fe984..c34b9d968 100644 --- a/app/Http/Controllers/Api/ApiV1Dot1Controller.php +++ b/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); @@ -757,8 +774,9 @@ class ApiV1Dot1Controller extends Controller public function moderatePost(Request $request, $id) { - abort_if(!$request->user(), 403); + abort_if(!$request->user() || !$request->user()->token(), 403); abort_if($request->user()->is_admin != true, 403); + abort_unless($request->user()->tokenCan('admin:write'), 403); if(config('pixelfed.bouncer.cloud_ips.ban_signups')) { abort_if(BouncerService::checkIp($request->ip()), 404); @@ -864,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 @@ -877,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' @@ -901,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)) diff --git a/app/Http/Controllers/Api/ApiV2Controller.php b/app/Http/Controllers/Api/ApiV2Controller.php index ce15a8a49..2380588cf 100644 --- a/app/Http/Controllers/Api/ApiV2Controller.php +++ b/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.*' => [ diff --git a/app/Http/Controllers/Api/BaseApiController.php b/app/Http/Controllers/Api/BaseApiController.php index 2e1fb5678..7ac73b4d0 100644 --- a/app/Http/Controllers/Api/BaseApiController.php +++ b/app/Http/Controllers/Api/BaseApiController.php @@ -99,6 +99,7 @@ class BaseApiController extends Controller public function avatarUpdate(Request $request) { abort_if(!$request->user(), 403); + $this->validate($request, [ 'upload' => 'required|mimetypes:image/jpeg,image/jpg,image/png|max:'.config('pixelfed.max_avatar_size'), ]); @@ -134,9 +135,10 @@ class BaseApiController extends Controller public function verifyCredentials(Request $request) { + abort_if(!$request->user(), 403); + $user = $request->user(); - abort_if(!$user, 403); - if($user->status != null) { + if ($user->status != null) { Auth::logout(); abort(403); } @@ -147,6 +149,7 @@ class BaseApiController extends Controller public function accountLikes(Request $request) { abort_if(!$request->user(), 403); + $this->validate($request, [ 'page' => 'sometimes|int|min:1|max:20', 'limit' => 'sometimes|int|min:1|max:10' diff --git a/app/Http/Controllers/Api/V1/DomainBlockController.php b/app/Http/Controllers/Api/V1/DomainBlockController.php index 2186c0936..5a2698361 100644 --- a/app/Http/Controllers/Api/V1/DomainBlockController.php +++ b/app/Http/Controllers/Api/V1/DomainBlockController.php @@ -23,7 +23,8 @@ class DomainBlockController extends Controller public function index(Request $request) { - abort_unless($request->user(), 403); + abort_if(!$request->user(), 403); + $this->validate($request, [ 'limit' => 'sometimes|integer|min:1|max:200' ]); @@ -52,7 +53,7 @@ class DomainBlockController extends Controller public function store(Request $request) { - abort_unless($request->user(), 403); + abort_if(!$request->user(), 403); $this->validate($request, [ 'domain' => 'required|active_url|min:1|max:120' @@ -99,7 +100,7 @@ class DomainBlockController extends Controller public function delete(Request $request) { - abort_unless($request->user(), 403); + abort_if(!$request->user(), 403); $this->validate($request, [ 'domain' => 'required|min:1|max:120' diff --git a/app/Http/Controllers/Api/V1/TagsController.php b/app/Http/Controllers/Api/V1/TagsController.php index 7314ce09d..2f7acf4a0 100644 --- a/app/Http/Controllers/Api/V1/TagsController.php +++ b/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); } diff --git a/app/Http/Controllers/ComposeController.php b/app/Http/Controllers/ComposeController.php index e17a37fd7..36bd5a66c 100644 --- a/app/Http/Controllers/ComposeController.php +++ b/app/Http/Controllers/ComposeController.php @@ -260,6 +260,8 @@ class ComposeController extends Controller $q = mb_substr($q, 1); } + $user = $request->user(); + abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action'); $blocked = UserFilter::whereFilterableType('App\Profile') diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 8e1a6a98d..52e992ce0 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -31,17 +31,20 @@ class AuthServiceProvider extends ServiceProvider if(config('instance.oauth.pat.enabled')) { Passport::personalAccessClientId(config('instance.oauth.pat.id')); } - Passport::setDefaultScope([ - 'read', - 'write', - 'follow', - ]); Passport::tokensCan([ 'read' => 'Full read access to your account', 'write' => 'Full write access to your account', 'follow' => 'Ability to follow other profiles', - 'push' => '' + 'admin:read' => 'Read all data on the server', + 'admin:write' => 'Modify all data on the server', + 'push' => 'Receive your push notifications' + ]); + + Passport::setDefaultScope([ + 'read', + 'write', + 'follow', ]); } diff --git a/config/pixelfed.php b/config/pixelfed.php index fc7da598a..9ed7fc616 100644 --- a/config/pixelfed.php +++ b/config/pixelfed.php @@ -23,7 +23,7 @@ return [ | This value is the version of your Pixelfed instance. | */ - 'version' => '0.11.9', + 'version' => '0.11.11', /* |--------------------------------------------------------------------------