فهرست منبع

Merge pull request #4843 from pixelfed/staging

Add Roles & Parental Controls
daniel 1 سال پیش
والد
کامیت
5b4214cb80

+ 21 - 0
app/Http/Controllers/Api/ApiV1Controller.php

@@ -98,6 +98,7 @@ use App\Jobs\MediaPipeline\MediaSyncLicensePipeline;
 use App\Services\DiscoverService;
 use App\Services\DiscoverService;
 use App\Services\CustomEmojiService;
 use App\Services\CustomEmojiService;
 use App\Services\MarkerService;
 use App\Services\MarkerService;
+use App\Services\UserRoleService;
 use App\Models\Conversation;
 use App\Models\Conversation;
 use App\Jobs\FollowPipeline\FollowAcceptPipeline;
 use App\Jobs\FollowPipeline\FollowAcceptPipeline;
 use App\Jobs\FollowPipeline\FollowRejectPipeline;
 use App\Jobs\FollowPipeline\FollowRejectPipeline;
@@ -1244,6 +1245,7 @@ class ApiV1Controller extends Controller
         abort_if(!$request->user(), 403);
         abort_if(!$request->user(), 403);
 
 
         $user = $request->user();
         $user = $request->user();
+        abort_if($user->has_roles && !UserRoleService::can('can-like', $user->id), 403, 'Invalid permissions for this action');
 
 
         AccountService::setLastActive($user->id);
         AccountService::setLastActive($user->id);
 
 
@@ -1305,6 +1307,7 @@ class ApiV1Controller extends Controller
         abort_if(!$request->user(), 403);
         abort_if(!$request->user(), 403);
 
 
         $user = $request->user();
         $user = $request->user();
+        abort_if($user->has_roles && !UserRoleService::can('can-like', $user->id), 403, 'Invalid permissions for this action');
 
 
         AccountService::setLastActive($user->id);
         AccountService::setLastActive($user->id);
 
 
@@ -1623,6 +1626,8 @@ class ApiV1Controller extends Controller
         ]);
         ]);
 
 
         $user = $request->user();
         $user = $request->user();
+        abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action');
+
         AccountService::setLastActive($user->id);
         AccountService::setLastActive($user->id);
 
 
         if($user->last_active_at == null) {
         if($user->last_active_at == null) {
@@ -1792,6 +1797,7 @@ class ApiV1Controller extends Controller
         abort_if(!$request->user(), 403);
         abort_if(!$request->user(), 403);
 
 
         $user = $request->user();
         $user = $request->user();
+        abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action');
         AccountService::setLastActive($user->id);
         AccountService::setLastActive($user->id);
 
 
         $media = Media::whereUserId($user->id)
         $media = Media::whereUserId($user->id)
@@ -1831,6 +1837,7 @@ class ApiV1Controller extends Controller
         ]);
         ]);
 
 
         $user = $request->user();
         $user = $request->user();
+        abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action');
 
 
         if($user->last_active_at == null) {
         if($user->last_active_at == null) {
             return [];
             return [];
@@ -2419,8 +2426,13 @@ class ApiV1Controller extends Controller
         $max = $request->input('max_id');
         $max = $request->input('max_id');
         $limit = $request->input('limit') ?? 20;
         $limit = $request->input('limit') ?? 20;
         $user = $request->user();
         $user = $request->user();
+
         $remote = $request->has('remote');
         $remote = $request->has('remote');
         $local = $request->has('local');
         $local = $request->has('local');
+        $userRoleKey = $remote ? 'can-view-network-feed' : 'can-view-public-feed';
+        if($user->has_roles && !UserRoleService::can($userRoleKey, $user->id)) {
+            return [];
+        }
         $filtered = $user ? UserFilterService::filters($user->profile_id) : [];
         $filtered = $user ? UserFilterService::filters($user->profile_id) : [];
         AccountService::setLastActive($user->id);
         AccountService::setLastActive($user->id);
         $domainBlocks = UserFilterService::domainBlocks($user->profile_id);
         $domainBlocks = UserFilterService::domainBlocks($user->profile_id);
@@ -3165,6 +3177,7 @@ class ApiV1Controller extends Controller
         abort_if(!$request->user(), 403);
         abort_if(!$request->user(), 403);
 
 
         $user = $request->user();
         $user = $request->user();
+        abort_if($user->has_roles && !UserRoleService::can('can-share', $user->id), 403, 'Invalid permissions for this action');
         AccountService::setLastActive($user->id);
         AccountService::setLastActive($user->id);
         $status = Status::whereScope('public')->findOrFail($id);
         $status = Status::whereScope('public')->findOrFail($id);
 
 
@@ -3212,6 +3225,7 @@ class ApiV1Controller extends Controller
         abort_if(!$request->user(), 403);
         abort_if(!$request->user(), 403);
 
 
         $user = $request->user();
         $user = $request->user();
+        abort_if($user->has_roles && !UserRoleService::can('can-share', $user->id), 403, 'Invalid permissions for this action');
         AccountService::setLastActive($user->id);
         AccountService::setLastActive($user->id);
         $status = Status::whereScope('public')->findOrFail($id);
         $status = Status::whereScope('public')->findOrFail($id);
 
 
@@ -3262,6 +3276,13 @@ class ApiV1Controller extends Controller
           '_pe'         => 'sometimes'
           '_pe'         => 'sometimes'
         ]);
         ]);
 
 
+        $user = $request->user();
+        abort_if(
+            $user->has_roles && !UserRoleService::can('can-view-hashtag-feed', $user->id),
+            403,
+            'Invalid permissions for this action'
+        );
+
         if(config('database.default') === 'pgsql') {
         if(config('database.default') === 'pgsql') {
             $tag = Hashtag::where('name', 'ilike', $hashtag)
             $tag = Hashtag::where('name', 'ilike', $hashtag)
                 ->orWhere('slug', 'ilike', $hashtag)
                 ->orWhere('slug', 'ilike', $hashtag)

+ 3 - 0
app/Http/Controllers/ComposeController.php

@@ -54,6 +54,7 @@ use App\Util\Lexer\Autolink;
 use App\Util\Lexer\Extractor;
 use App\Util\Lexer\Extractor;
 use App\Util\Media\License;
 use App\Util\Media\License;
 use Image;
 use Image;
+use App\Services\UserRoleService;
 
 
 class ComposeController extends Controller
 class ComposeController extends Controller
 {
 {
@@ -92,6 +93,7 @@ class ComposeController extends Controller
 
 
 		$user = Auth::user();
 		$user = Auth::user();
 		$profile = $user->profile;
 		$profile = $user->profile;
+		abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action');
 
 
 		$limitKey = 'compose:rate-limit:media-upload:' . $user->id;
 		$limitKey = 'compose:rate-limit:media-upload:' . $user->id;
 		$limitTtl = now()->addMinutes(15);
 		$limitTtl = now()->addMinutes(15);
@@ -184,6 +186,7 @@ class ComposeController extends Controller
 		]);
 		]);
 
 
 		$user = Auth::user();
 		$user = Auth::user();
+		abort_if($user->has_roles && !UserRoleService::can('can-post', $user->id), 403, 'Invalid permissions for this action');
 
 
 		$limitKey = 'compose:rate-limit:media-updates:' . $user->id;
 		$limitKey = 'compose:rate-limit:media-updates:' . $user->id;
 		$limitTtl = now()->addMinutes(15);
 		$limitTtl = now()->addMinutes(15);

+ 23 - 0
app/Http/Controllers/UserRolesController.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Http\Controllers;
+
+use Illuminate\Http\Request;
+use App\Services\UserRoleService;
+
+class UserRolesController extends Controller
+{
+    public function __construct()
+    {
+        $this->middleware('auth');
+    }
+
+    public function getRoles(Request $request)
+    {
+        $this->validate($request, [
+            'id' => 'required'
+        ]);
+
+        return UserRoleService::getRoles($request->user()->id);
+    }
+}

+ 23 - 0
app/Models/UserRoles.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Factories\HasFactory;
+use Illuminate\Database\Eloquent\Model;
+use App\User;
+
+class UserRoles extends Model
+{
+    use HasFactory;
+
+    protected $guarded = [];
+
+    protected $casts = [
+        'roles' => 'array'
+    ];
+
+    public function user()
+    {
+        return $this->belongsTo(User::class);
+    }
+}

+ 1 - 1
app/Providers/AuthServiceProvider.php

@@ -14,7 +14,7 @@ class AuthServiceProvider extends ServiceProvider
      * @var array
      * @var array
      */
      */
     protected $policies = [
     protected $policies = [
-        'App\Model' => 'App\Policies\ModelPolicy',
+        // 'App\Model' => 'App\Policies\ModelPolicy',
     ];
     ];
 
 
     /**
     /**

+ 119 - 0
app/Services/UserRoleService.php

@@ -0,0 +1,119 @@
+<?php
+
+namespace App\Services;
+
+use App\Models\UserRoles;
+
+class UserRoleService
+{
+    public static function can($action, $id, $useDefaultFallback = true)
+    {
+        $default = self::defaultRoles();
+        $roles = self::get($id);
+        return
+            in_array($action, array_keys($roles)) ?
+                $roles[$action] :
+                (
+                    $useDefaultFallback ?
+                        $default[$action] :
+                        false
+                );
+        }
+
+    public static function get($id)
+    {
+        if($roles = UserRoles::whereUserId($id)->first()) {
+            return $roles->roles;
+        }
+
+        return self::defaultRoles();
+    }
+
+    public static function roleKeys()
+    {
+        return array_keys(self::defaultRoles());
+    }
+
+    public static function defaultRoles()
+    {
+        return [
+            'account-force-private' => true,
+            'account-ignore-follow-requests' => true,
+
+            'can-view-public-feed' => true,
+            'can-view-network-feed' => true,
+            'can-view-discover' => true,
+            'can-view-hashtag-feed' => false,
+
+            'can-post' => true,
+            'can-comment' => true,
+            'can-like' => true,
+            'can-share' => true,
+
+            'can-follow' => false,
+            'can-make-public' => false,
+        ];
+    }
+
+    public static function getRoles($id)
+    {
+        $myRoles = self::get($id);
+        $roleData = collect(self::roleData())
+            ->map(function($role, $k) use($myRoles) {
+                $role['value'] = $myRoles[$k];
+                return $role;
+            })
+            ->toArray();
+        return $roleData;
+    }
+
+    public static function roleData()
+    {
+        return [
+            'account-force-private' => [
+                'title' => 'Force Private Account',
+                'action' => 'Prevent changing account from private'
+            ],
+            'account-ignore-follow-requests' => [
+                'title' => 'Ignore Follow Requests',
+                'action' => 'Hide follow requests and associated notifications'
+            ],
+            'can-view-public-feed' => [
+                'title' => 'Hide Public Feed',
+                'action' => 'Hide the public feed timeline'
+            ],
+            'can-view-network-feed' => [
+                'title' => 'Hide Network Feed',
+                'action' => 'Hide the network feed timeline'
+            ],
+            'can-view-discover' => [
+                'title' => 'Hide Discover',
+                'action' => 'Hide the discover feature'
+            ],
+            'can-post' => [
+                'title' => 'Can post',
+                'action' => 'Allows new posts to be shared'
+            ],
+            'can-comment' => [
+                'title' => 'Can comment',
+                'action' => 'Allows new comments to be posted'
+            ],
+            'can-like' => [
+                'title' => 'Can Like',
+                'action' => 'Allows the ability to like posts and comments'
+            ],
+            'can-share' => [
+                'title' => 'Can Share',
+                'action' => 'Allows the ability to share posts and comments'
+            ],
+            'can-follow' => [
+                'title' => 'Can Follow',
+                'action' => 'Allows the ability to follow accounts'
+            ],
+            'can-make-public' => [
+                'title' => 'Can make account public',
+                'action' => 'Allows the ability to make account public'
+            ],
+        ];
+    }
+}

+ 31 - 0
database/migrations/2023_12_27_081801_create_user_roles_table.php

@@ -0,0 +1,31 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('user_roles', function (Blueprint $table) {
+            $table->id();
+            $table->unsignedBigInteger('profile_id')->unique()->index();
+            $table->unsignedInteger('user_id')->unique()->index();
+            $table->json('roles')->nullable();
+            $table->json('meta')->nullable();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('user_roles');
+    }
+};

+ 32 - 0
database/migrations/2023_12_27_082024_add_has_roles_to_users_table.php

@@ -0,0 +1,32 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->boolean('has_roles')->default(false);
+            $table->unsignedInteger('parent_id')->nullable();
+            $table->tinyInteger('role_id')->unsigned()->nullable()->index();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('users', function (Blueprint $table) {
+            $table->dropColumn('has_roles');
+            $table->dropColumn('parent_id');
+            $table->dropColumn('role_id');
+        });
+    }
+};