Procházet zdrojové kódy

simple starting permissions. only admin

1day2die před 2 roky
rodič
revize
0ffceb535d

+ 190 - 0
app/Http/Controllers/Admin/RoleController.php

@@ -0,0 +1,190 @@
+<?php
+
+namespace App\Http\Controllers\Admin;
+
+use App\Http\Controllers\Controller;
+use Exception;
+use Illuminate\Contracts\Foundation\Application;
+use Illuminate\Contracts\View\Factory;
+use Illuminate\Contracts\View\View;
+use Illuminate\Http\RedirectResponse;
+use Illuminate\Http\Request;
+use Spatie\Permission\Models\Permission;
+use Spatie\Permission\Models\Role;
+
+class RoleController extends Controller
+{
+
+    /**
+     * Display a listing of the resource.
+     *
+     * @param Request $request
+     * @return mixed
+     * @throws Exception
+     */
+    public function index(Request $request)
+    {
+
+
+        //datatables
+        if ($request->ajax()) {
+            return $this->dataTableQuery();
+        }
+
+        $html = $this->dataTable();
+        return view('admin.roles.index', compact('html'));
+    }
+
+    /**
+     * Show the form for creating a new resource.
+     *
+     * @return Application|Factory|View
+     */
+    public function create()
+    {
+
+        $permissions = Permission::all();
+
+        return view('admin.roles.edit', compact('permissions'));
+    }
+
+    /**
+     * Store a newly created resource in storage.
+     *
+     * @return RedirectResponse
+     */
+    public function store(Request $request): RedirectResponse
+    {
+        $role = Role::create([
+            'name' => $request->name,
+            'color' => $request->color
+        ]);
+
+        if ($request->permissions) {
+            $role->givePermissionTo($request->permissions);
+        }
+
+        return redirect()
+            ->route('admin.roles.index')
+            ->with('success', __('Role saved'));
+    }
+
+    /**
+     * Display the specified resource.
+     */
+    public function show()
+    {
+        abort(404);
+    }
+
+    /**
+     * Show the form for editing the specified resource.
+     *
+     * @param Role $role
+     * @return Application|Factory|View
+     */
+    public function edit(Role $role)
+    {
+
+        $permissions = Permission::all();
+
+        return view('admin.roles.edit', compact('role', 'permissions'));
+    }
+
+    /**
+     * Update the specified resource in storage.
+     *
+     * @param Role $role
+     * @return RedirectResponse
+     */
+    public function update(Request $request, Role $role)
+    {
+        if ($request->permissions) {
+            if($role->id != 1){ //disable admin permissions change
+                $role->syncPermissions($request->permissions);
+            }
+        }
+
+        if($role->id == 3 || $role->id == 1 || $role->id == 4){ //dont let the user change the names of these roles
+            $role->update([
+                'color' => $request->color
+            ]);
+        }else{
+            $role->update([
+                'name' => $request->name,
+                'color' => $request->color
+            ]);
+        }
+
+        if($role->id == 1){
+            return redirect()->route('admin.roles.index')->with('success', __('Role updated. Name and Permissions of this Role cannot be changed'));
+        }elseif($role->id == 4 || $role->id == 3){
+            return redirect()->route('admin.roles.index')->with('success', __('Role updated. Name of this Role cannot be changed'));
+        }else{
+            return redirect()
+                ->route('admin.roles.index')
+                ->with('success', __('Role saved'));
+        }
+    }
+
+    /**
+     * Remove the specified resource from storage.
+     *
+     * @return RedirectResponse
+     */
+    public function destroy(Role $role)
+    {
+
+        if($role->id == 3 || $role->id == 1 || $role->id == 2){ //cannot delete the hard coded roles
+            return back()->with("error","You cannot delete that role");
+        }
+
+        $users = User::role($role)->get();
+
+        foreach($users as $user){
+            $user->syncRoles(['Member']);
+        }
+
+        $role->delete();
+
+        return redirect()
+            ->route('admin.roles.index')
+            ->with('success', __('Role removed'));
+    }
+
+    /**
+     * @return mixed
+     * @throws Exception
+     */
+    public function dataTable()
+    {
+        $query = Role::query()->withCount(['users', 'permissions']);
+
+
+        return datatables($query)
+            ->addColumn('actions', function (Role $role) {
+                return '
+                            <a title="Edit" href="'.route("admin.roles.edit", $role).'" class="btn btn-sm btn-info"><i
+                                    class="fa fas fa-edit"></i></a>
+                            <form class="d-inline" method="post" action="'.route("admin.roles.destroy", $role).'">
+                            ' . csrf_field() . '
+                            ' . method_field("DELETE") . '
+                                <button title="Delete" type="submit" class="btn btn-sm btn-danger confirm"><i
+                                        class="fa fas fa-trash"></i></button>
+                            </form>
+                ';
+            })
+
+            ->editColumn('name', function (Role $role) {
+                return "<span style=\"color: $role->color\">$role->name</span>";
+            })
+            ->editColumn('usercount', function ($query) {
+                return $query->users_count;
+            })
+            ->editColumn('permissionscount', function ($query){
+                return $query->permissions_count;
+            })
+            ->rawColumns(['actions', 'name'])
+            ->make(true);
+    }
+}

+ 1 - 1
app/Http/Controllers/Admin/UserController.php

@@ -166,7 +166,7 @@ class UserController extends Controller
      */
     public function destroy(User $user)
     {
-        if ($user->role === 'admin' && User::query()->where('role', 'admin')->count() === 1) {
+        if ($user->hasRole("Admin") && User::query()->where('role', 'admin')->count() === 1) {
             return redirect()->back()->with('error', __('You can not delete the last admin!'));
         }
 

+ 32 - 0
app/Http/Controllers/Controller.php

@@ -2,12 +2,44 @@
 
 namespace App\Http\Controllers;
 
+use App\Models\User;
 use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
 use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Routing\Controller as BaseController;
+use Illuminate\Support\Facades\Auth;
 
 class Controller extends BaseController
 {
     use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+    /**
+     * Check if user has permissions
+     * Abort 403 if the user doesn't have the required permission
+     *
+     * @param string $permission
+     * @return void
+     */
+    public function checkPermission(string $permission)
+    {
+        /** @var User $user */
+        $user = Auth::user();
+
+        if (!$user->can($permission)) {
+            abort(403, __('User does not have the right permissions.'));
+        }
+    }
+
+    /**
+     * Check if user has permissions
+     *
+     * @param string $permission
+     * @return bool
+     */
+    public function can(string $permission): bool
+    {
+        /** @var User $user */
+        $user = Auth::user();
+
+        return $user->can($permission);
+    }
 }

+ 1 - 1
app/Http/Controllers/ProfileController.php

@@ -57,7 +57,7 @@ class ProfileController extends Controller
     public function selfDestroyUser()
     {
         $user = Auth::user();
-        if ($user->role == "admin") return back()->with("error", "You cannot delete yourself as an admin!");
+        if ($user->hasRole("Admin")) return back()->with("error", "You cannot delete yourself as an admin!");
 
         $user->delete();
 

+ 5 - 0
app/Http/Kernel.php

@@ -27,6 +27,7 @@ class Kernel extends HttpKernel
         \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
         \App\Http\Middleware\TrimStrings::class,
         \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
+
     ];
 
     /**
@@ -76,5 +77,9 @@ class Kernel extends HttpKernel
         'moderator' => isMod::class,
         'api.token' => ApiAuthToken::class,
         'checkSuspended' => CheckSuspended::class,
+        'role' => \Spatie\Permission\Middlewares\RoleMiddleware::class,
+        'permission' => \Spatie\Permission\Middlewares\PermissionMiddleware::class,
+        'role_or_permission' => \Spatie\Permission\Middlewares\RoleOrPermissionMiddleware::class,
     ];
+
 }

+ 1 - 1
app/Http/Middleware/isAdmin.php

@@ -18,7 +18,7 @@ class isAdmin
      */
     public function handle(Request $request, Closure $next)
     {
-        if (Auth::user() && Auth::user()->role == 'admin') {
+        if (Auth::user() && Auth::user()->hasRole("Admin")) {
             return $next($request);
         }
 

+ 1 - 1
app/Http/Middleware/isMod.php

@@ -18,7 +18,7 @@ class isMod
      */
     public function handle(Request $request, Closure $next)
     {
-        if (Auth::user() && Auth::user()->role == 'moderator' || Auth::user() && Auth::user()->role == 'admin') {
+        if (Auth::user() && Auth::user()->role == 'moderator' || Auth::user() && Auth::user()->hasRole("Admin")) {
             return $next($request);
         }
 

+ 2 - 1
app/Models/User.php

@@ -18,13 +18,14 @@ use Illuminate\Notifications\Notifiable;
 use Spatie\Activitylog\LogOptions;
 use Spatie\Activitylog\Traits\CausesActivity;
 use Spatie\Activitylog\Traits\LogsActivity;
+use Spatie\Permission\Traits\HasRoles;
 
 /**
  * Class User
  */
 class User extends Authenticatable implements MustVerifyEmail
 {
-    use HasFactory, Notifiable, LogsActivity, CausesActivity;
+    use HasFactory, Notifiable, LogsActivity, CausesActivity, HasRoles;
 
     private PterodactylClient $pterodactyl;
 

+ 1 - 0
composer.json

@@ -26,6 +26,7 @@
         "qirolab/laravel-themer": "^2.0.2",
         "socialiteproviders/discord": "^4.1.2",
         "spatie/laravel-activitylog": "^4.7.3",
+        "spatie/laravel-permission": "^5.10",
         "spatie/laravel-query-builder": "^5.1.2",
         "spatie/laravel-settings": "^2.7",
         "spatie/laravel-validation-rules": "^3.2.2",

+ 83 - 1
composer.lock

@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "0d007fe2e018692a9ff3d50fcbebabc5",
+    "content-hash": "8a9b4a3cda2a919fa33f41527b679dce",
     "packages": [
         {
             "name": "aws/aws-crt-php",
@@ -5160,6 +5160,88 @@
             ],
             "time": "2023-04-27T08:09:01+00:00"
         },
+        {
+            "name": "spatie/laravel-permission",
+            "version": "5.10.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/spatie/laravel-permission.git",
+                "reference": "d08b3ffc5870cce4a47a39f22174947b33c191ae"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/spatie/laravel-permission/zipball/d08b3ffc5870cce4a47a39f22174947b33c191ae",
+                "reference": "d08b3ffc5870cce4a47a39f22174947b33c191ae",
+                "shasum": ""
+            },
+            "require": {
+                "illuminate/auth": "^7.0|^8.0|^9.0|^10.0",
+                "illuminate/container": "^7.0|^8.0|^9.0|^10.0",
+                "illuminate/contracts": "^7.0|^8.0|^9.0|^10.0",
+                "illuminate/database": "^7.0|^8.0|^9.0|^10.0",
+                "php": "^7.3|^8.0"
+            },
+            "require-dev": {
+                "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0",
+                "phpunit/phpunit": "^9.4",
+                "predis/predis": "^1.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "5.x-dev",
+                    "dev-master": "5.x-dev"
+                },
+                "laravel": {
+                    "providers": [
+                        "Spatie\\Permission\\PermissionServiceProvider"
+                    ]
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/helpers.php"
+                ],
+                "psr-4": {
+                    "Spatie\\Permission\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Freek Van der Herten",
+                    "email": "freek@spatie.be",
+                    "homepage": "https://spatie.be",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Permission handling for Laravel 6.0 and up",
+            "homepage": "https://github.com/spatie/laravel-permission",
+            "keywords": [
+                "acl",
+                "laravel",
+                "permission",
+                "permissions",
+                "rbac",
+                "roles",
+                "security",
+                "spatie"
+            ],
+            "support": {
+                "issues": "https://github.com/spatie/laravel-permission/issues",
+                "source": "https://github.com/spatie/laravel-permission/tree/5.10.1"
+            },
+            "funding": [
+                {
+                    "url": "https://github.com/spatie",
+                    "type": "github"
+                }
+            ],
+            "time": "2023-04-12T17:08:32+00:00"
+        },
         {
             "name": "spatie/laravel-query-builder",
             "version": "5.2.0",

+ 161 - 0
config/permission.php

@@ -0,0 +1,161 @@
+<?php
+
+return [
+
+    'models' => [
+
+        /*
+         * When using the "HasPermissions" trait from this package, we need to know which
+         * Eloquent model should be used to retrieve your permissions. Of course, it
+         * is often just the "Permission" model but you may use whatever you like.
+         *
+         * The model you want to use as a Permission model needs to implement the
+         * `Spatie\Permission\Contracts\Permission` contract.
+         */
+
+        'permission' => Spatie\Permission\Models\Permission::class,
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * Eloquent model should be used to retrieve your roles. Of course, it
+         * is often just the "Role" model but you may use whatever you like.
+         *
+         * The model you want to use as a Role model needs to implement the
+         * `Spatie\Permission\Contracts\Role` contract.
+         */
+
+        'role' => Spatie\Permission\Models\Role::class,
+
+    ],
+
+    'table_names' => [
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * table should be used to retrieve your roles. We have chosen a basic
+         * default value but you may easily change it to any table you like.
+         */
+
+        'roles' => 'roles',
+
+        /*
+         * When using the "HasPermissions" trait from this package, we need to know which
+         * table should be used to retrieve your permissions. We have chosen a basic
+         * default value but you may easily change it to any table you like.
+         */
+
+        'permissions' => 'permissions',
+
+        /*
+         * When using the "HasPermissions" trait from this package, we need to know which
+         * table should be used to retrieve your models permissions. We have chosen a
+         * basic default value but you may easily change it to any table you like.
+         */
+
+        'model_has_permissions' => 'model_has_permissions',
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * table should be used to retrieve your models roles. We have chosen a
+         * basic default value but you may easily change it to any table you like.
+         */
+
+        'model_has_roles' => 'model_has_roles',
+
+        /*
+         * When using the "HasRoles" trait from this package, we need to know which
+         * table should be used to retrieve your roles permissions. We have chosen a
+         * basic default value but you may easily change it to any table you like.
+         */
+
+        'role_has_permissions' => 'role_has_permissions',
+    ],
+
+    'column_names' => [
+        /*
+         * Change this if you want to name the related pivots other than defaults
+         */
+        'role_pivot_key' => null, //default 'role_id',
+        'permission_pivot_key' => null, //default 'permission_id',
+
+        /*
+         * Change this if you want to name the related model primary key other than
+         * `model_id`.
+         *
+         * For example, this would be nice if your primary keys are all UUIDs. In
+         * that case, name this `model_uuid`.
+         */
+
+        'model_morph_key' => 'model_id',
+
+        /*
+         * Change this if you want to use the teams feature and your related model's
+         * foreign key is other than `team_id`.
+         */
+
+        'team_foreign_key' => 'team_id',
+    ],
+
+    /*
+     * When set to true, the method for checking permissions will be registered on the gate.
+     * Set this to false, if you want to implement custom logic for checking permissions.
+     */
+
+    'register_permission_check_method' => true,
+
+    /*
+     * When set to true the package implements teams using the 'team_foreign_key'. If you want
+     * the migrations to register the 'team_foreign_key', you must set this to true
+     * before doing the migration. If you already did the migration then you must make a new
+     * migration to also add 'team_foreign_key' to 'roles', 'model_has_roles', and
+     * 'model_has_permissions'(view the latest version of package's migration file)
+     */
+
+    'teams' => false,
+
+    /*
+     * When set to true, the required permission names are added to the exception
+     * message. This could be considered an information leak in some contexts, so
+     * the default setting is false here for optimum safety.
+     */
+
+    'display_permission_in_exception' => false,
+
+    /*
+     * When set to true, the required role names are added to the exception
+     * message. This could be considered an information leak in some contexts, so
+     * the default setting is false here for optimum safety.
+     */
+
+    'display_role_in_exception' => false,
+
+    /*
+     * By default wildcard permission lookups are disabled.
+     */
+
+    'enable_wildcard_permission' => false,
+
+    'cache' => [
+
+        /*
+         * By default all permissions are cached for 24 hours to speed up performance.
+         * When permissions or roles are updated the cache is flushed automatically.
+         */
+
+        'expiration_time' => \DateInterval::createFromDateString('24 hours'),
+
+        /*
+         * The cache key used to store all permissions.
+         */
+
+        'key' => 'spatie.permission.cache',
+
+        /*
+         * You may optionally indicate a specific cache driver to use for permission and
+         * role caching using any of the `store` drivers listed in the cache.php config
+         * file. Using 'default' here means to use the `default` set in cache.php.
+         */
+
+        'store' => 'default',
+    ],
+];

+ 92 - 0
config/permissions_web.php

@@ -0,0 +1,92 @@
+<?php
+
+return [
+    '*',
+
+    /*
+        * Permissions for admin
+        */
+    'admin.sidebar.read',
+
+    'admin.roles.read',
+    'admin.roles.write',
+
+
+    'admin.ticket.read',
+
+    'admin.ticket_blacklist.read',
+    'admin.ticket_blacklist.write',
+
+    'admin.overview.read',
+    'admin.overview.sync',
+
+    'admin.api.read',
+    'admin.api.write',
+
+    'admin.users.read',
+    'admin.users.write',
+    'admin.users.suspend',
+    'admin.users.write.credits',
+    'admin.users.write.username',
+    'admin.users.write.password',
+    'admin.users.write.role',
+    'admin.users.write.referal',
+    'admin.users.write.pterodactyl',
+
+    'admin.servers.read',
+    'admin.servers.write',
+    'admin.servers.suspend',
+    'admin.server.write.owner',
+    'admin.server.write.identifier',
+    'admin.server.delete',
+
+    'admin.products.read',
+    'admin.products.create',
+    'admin.products.edit',
+    'admin.products.delete',
+
+    'admin.store.read',
+    'admin.store.write',
+    'admin.store.disable',
+
+    'admin.voucher.read',
+    'admin.voucher.write',
+
+    'admin.useful_links.read',
+    'admin.useful_links.write',
+
+    'admin.legal.read',
+    'admin.legal.write',
+
+    'admin.logs.read',
+
+    /*
+     * Permissions for settings
+     */
+    'settings.sidebar.read',
+
+    'settings.invoices.read',
+    'settings.invoices.write',
+
+    'settings.language.read',
+    'settings.language.write',
+
+    'settings.misc.read',
+    'settings.misc.write',
+
+    'settings.payment.read',
+    'settings.payment.write',
+
+    'settings.system.read',
+    'settings.system.write',
+
+    /*
+    * Permissions for users
+    */
+    'user.server.create',
+    'user.server.upgrade',
+    'user.shop.buy',
+    'user.ticket.read',
+    'user.ticket.write',
+    'user.referral',
+];

+ 142 - 0
database/migrations/2023_04_29_232942_create_permission_tables.php

@@ -0,0 +1,142 @@
+<?php
+
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Database\Migrations\Migration;
+use Spatie\Permission\PermissionRegistrar;
+
+class CreatePermissionTables extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        $tableNames = config('permission.table_names');
+        $columnNames = config('permission.column_names');
+        $teams = config('permission.teams');
+
+        if (empty($tableNames)) {
+            throw new \Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.');
+        }
+        if ($teams && empty($columnNames['team_foreign_key'] ?? null)) {
+            throw new \Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.');
+        }
+
+        Schema::create($tableNames['permissions'], function (Blueprint $table) {
+            $table->bigIncrements('id'); // permission id
+            $table->string('name');       // For MySQL 8.0 use string('name', 125);
+            $table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
+            $table->timestamps();
+
+            $table->unique(['name', 'guard_name']);
+        });
+
+        Schema::create($tableNames['roles'], function (Blueprint $table) use ($teams, $columnNames) {
+            $table->bigIncrements('id'); // role id
+            if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
+                $table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
+                $table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
+            }
+            $table->string('name');       // For MySQL 8.0 use string('name', 125);
+            $table->string('color')->nullable()->default('#485460');       // For MySQL 8.0 use string('name', 125);
+            $table->string('guard_name'); // For MySQL 8.0 use string('guard_name', 125);
+            $table->timestamps();
+            if ($teams || config('permission.testing')) {
+                $table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
+            } else {
+                $table->unique(['name', 'guard_name']);
+            }
+        });
+
+        Schema::create($tableNames['model_has_permissions'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
+            $table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
+
+            $table->string('model_type');
+            $table->unsignedBigInteger($columnNames['model_morph_key']);
+            $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
+
+            $table->foreign(PermissionRegistrar::$pivotPermission)
+                ->references('id') // permission id
+                ->on($tableNames['permissions'])
+                ->onDelete('cascade');
+            if ($teams) {
+                $table->unsignedBigInteger($columnNames['team_foreign_key']);
+                $table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
+
+                $table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_permissions_permission_model_type_primary');
+            } else {
+                $table->primary([PermissionRegistrar::$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_permissions_permission_model_type_primary');
+            }
+
+        });
+
+        Schema::create($tableNames['model_has_roles'], function (Blueprint $table) use ($tableNames, $columnNames, $teams) {
+            $table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
+
+            $table->string('model_type');
+            $table->unsignedBigInteger($columnNames['model_morph_key']);
+            $table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
+
+            $table->foreign(PermissionRegistrar::$pivotRole)
+                ->references('id') // role id
+                ->on($tableNames['roles'])
+                ->onDelete('cascade');
+            if ($teams) {
+                $table->unsignedBigInteger($columnNames['team_foreign_key']);
+                $table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
+
+                $table->primary([$columnNames['team_foreign_key'], PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_roles_role_model_type_primary');
+            } else {
+                $table->primary([PermissionRegistrar::$pivotRole, $columnNames['model_morph_key'], 'model_type'],
+                    'model_has_roles_role_model_type_primary');
+            }
+        });
+
+        Schema::create($tableNames['role_has_permissions'], function (Blueprint $table) use ($tableNames) {
+            $table->unsignedBigInteger(PermissionRegistrar::$pivotPermission);
+            $table->unsignedBigInteger(PermissionRegistrar::$pivotRole);
+
+            $table->foreign(PermissionRegistrar::$pivotPermission)
+                ->references('id') // permission id
+                ->on($tableNames['permissions'])
+                ->onDelete('cascade');
+
+            $table->foreign(PermissionRegistrar::$pivotRole)
+                ->references('id') // role id
+                ->on($tableNames['roles'])
+                ->onDelete('cascade');
+
+            $table->primary([PermissionRegistrar::$pivotPermission, PermissionRegistrar::$pivotRole], 'role_has_permissions_permission_id_role_id_primary');
+        });
+
+        app('cache')
+            ->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
+            ->forget(config('permission.cache.key'));
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        $tableNames = config('permission.table_names');
+
+        if (empty($tableNames)) {
+            throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
+        }
+
+        Schema::drop($tableNames['role_has_permissions']);
+        Schema::drop($tableNames['model_has_roles']);
+        Schema::drop($tableNames['model_has_permissions']);
+        Schema::drop($tableNames['roles']);
+        Schema::drop($tableNames['permissions']);
+    }
+}

+ 51 - 0
database/migrations/2023_04_29_233120_drop_roles.php

@@ -0,0 +1,51 @@
+<?php
+
+use App\Models\User;
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Artisan;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Artisan::call('db:seed', [
+            '--class' => 'PermissionsSeeder',
+        ]);
+
+        Schema::table('users', function ($table) {
+            $table->dropColumn('role');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('users', function($table) {
+            $table->string('role')->default('member');
+        });
+
+        $users = User::with('roles')->get();
+        foreach($users as $user){
+            if($user->hasRole(1)){
+                $user->role = "admin";
+            }elseif ($user->hasRole(3)){
+                $user->role = "client";
+            }else{
+                $user->role = "member";
+            }
+            $user->save();
+        }
+
+    }
+};

+ 74 - 0
database/seeders/PermissionsSeeder.php

@@ -0,0 +1,74 @@
+<?php
+
+namespace Database\Seeders;
+
+use App\Models\User;
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
+use Illuminate\Database\Seeder;
+use Spatie\Permission\Models\Permission;
+use Spatie\Permission\Models\Role;
+
+class PermissionsSeeder extends Seeder
+{
+    /**
+     * Run the database seeds.
+     *
+     * @return void
+     */
+    public function run()
+    {
+
+        $this->createPermissions();
+        $this->createRoles();
+
+
+        $users = User::all();
+        foreach($users as $user){
+            $user->assignRole(4);
+        }
+
+        $admins = User::where("role","admin")->get();
+        foreach($admins as $admin) {
+            $admin->syncRoles(1);
+        }
+
+        $admins = User::where("role","client")->get();
+        foreach($admins as $admin) {
+            $admin->syncRoles(3);
+        }
+
+
+
+
+    }
+
+    public function createPermissions()
+    {
+        foreach (config('permissions_web') as $name) {
+            Permission::findOrCreate($name);
+        }
+    }
+
+    //TODO run only once
+    public function createRoles()
+    {
+        $userPermissions=[
+            'user.server.create',
+            'user.server.upgrade',
+            'user.shop.buy',
+            'user.ticket.read',
+            'user.ticket.write',
+            'user.referral',
+        ];
+        /** @var Role $adminRole */
+        $adminRole = Role::updateOrCreate(["name"=>"Admin","color"=>"#fa0000"]);
+        $supportRole = Role::updateOrCreate(["name"=>"Support-Team","color"=>"#00b0b3"]);
+        $clientRole = Role::updateOrCreate(["name"=>"Client","color"=>"#008009"]);
+        $userRole =  Role::updateOrCreate(["name"=>"User","color"=>"#0052a3"]);
+
+        $adminRole->givePermissionTo(Permission::findByName('*'));
+
+        $userRole->syncPermissions($userPermissions);
+        $clientRole->syncPermissions($userPermissions);
+    }
+}

+ 3 - 3
public/install/forms.php

@@ -292,9 +292,9 @@ if (isset($_POST['createUser'])) {
     }
 
     $random = generateRandomString();
-    $query1 = 'INSERT INTO `' . getenv('DB_DATABASE') . "`.`users` (`name`, `role`, `credits`, `server_limit`, `pterodactyl_id`, `email`, `password`, `created_at`, `referral_code`) VALUES ('$name', 'admin', '250', '1', '$pteroID', '$mail', '$pass', CURRENT_TIMESTAMP, '$random')";
-
-    if ($db->query($query1)) {
+    $query1 = 'INSERT INTO `' . getenv('DB_DATABASE') . "`.`users` (`name`, `credits`, `server_limit`, `pterodactyl_id`, `email`, `password`, `created_at`, `referral_code`) VALUES ('$name', 'admin', '250', '1', '$pteroID', '$mail', '$pass', CURRENT_TIMESTAMP, '$random')";
+    $query2 = 'INSERT INTO `' . getenv('DB_DATABASE') . "`.`model_has_roles` (`role_id`, `model_type`, `model_id`) VALUES ('1', 'App\Models\User', '1')";
+    if ($db->query($query1) && $db->query($query2)) {
         wh_log('Created user with Email ' . $mail . ' and pterodactyl ID ' . $pteroID, 'info');
         header('LOCATION: index.php?step=7');
     } else {

+ 4 - 1
routes/web.php

@@ -13,6 +13,7 @@ use App\Http\Controllers\Admin\OverViewController;
 use App\Http\Controllers\Admin\PartnerController;
 use App\Http\Controllers\Admin\PaymentController;
 use App\Http\Controllers\Admin\ProductController;
+use App\Http\Controllers\Admin\RoleController;
 use App\Http\Controllers\Admin\ServerController as AdminServerController;
 use App\Http\Controllers\Admin\SettingsController;
 use App\Http\Controllers\Admin\ShopProductController;
@@ -117,7 +118,9 @@ Route::middleware(['auth', 'checkSuspended'])->group(function () {
 
     //admin
     Route::prefix('admin')->name('admin.')->middleware('admin')->group(function () {
-
+        //Roles
+        Route::get('roles/datatable', [RoleController::class, 'datatable'])->name('roles.datatable');
+        Route::resource('roles', RoleController::class);
         //overview
         Route::get('legal', [OverViewController::class, 'index'])->name('overview.index');
 

+ 2 - 2
themes/BlueInfinity/views/layouts/main.blade.php

@@ -253,7 +253,7 @@
                         </li>
                     @endif
 
-                    @if ((Auth::user()->role == 'admin' || Auth::user()->role == 'moderator') && config('SETTINGS::TICKET:ENABLED'))
+                    @if ((Auth::user()->hasRole("Admin") || Auth::user()->role == 'moderator') && config('SETTINGS::TICKET:ENABLED'))
                         <li class="nav-header">{{ __('Moderation') }}</li>
 
                         <li class="nav-item">
@@ -272,7 +272,7 @@
                         </li>
                     @endif
 
-                    @if (Auth::user()->role == 'admin')
+                    @if (Auth::user()->hasRole("Admin"))
                         <li class="nav-header">{{ __('Administration') }}</li>
 
                         <li class="nav-item">

+ 54 - 0
themes/default/views/admin/roles/edit.blade.php

@@ -0,0 +1,54 @@
+@extends('layouts.main')
+
+@section('content')
+    <div class="main py-4">
+
+        <div class="card card-body border-0 shadow table-wrapper table-responsive">
+            <h2 class="mb-4 h5">{{ isset($role) ?  __('Edit role') : __('Create role') }}</h2>
+
+            <form method="post"
+                  action="{{isset($role) ? route('admin.roles.update', $role->id) : route('admin.roles.store')}}">
+                @csrf
+                @isset($role)
+                    @method('PATCH')
+                @endisset
+
+                <div class="row">
+                    <div class="col-lg-6">
+
+                        <x-input.text label="{{(__('Name'))}}"
+                                      name="name"
+                                      value="{{ isset($role) ? $role->name : null}}"/>
+
+                        <x-input.text label="{{(__('Badge color'))}}"
+                                      type="color"
+                                      name="color"
+                                      value="{{ isset($role) ? $role->color : null}}"/>
+
+                    </div>
+
+                    <div class="col-lg-6">
+
+                        <x-input.select
+                            label="{{(__('Permissions'))}}"
+                            name="permissions"
+                            style="height: 200px"
+                            multiple>
+                            @foreach($permissions as $permission)
+                                <option @if(isset($role) && $role->permissions->contains($permission)) selected
+                                        @endif value="{{$permission->id}}">{{$permission->name}}</option>
+                            @endforeach
+                        </x-input.select>
+
+                    </div>
+                </div>
+
+                <div class="form-group d-flex justify-content-end mt-3">
+                    <button name="submit" type="submit" class="btn btn-primary">{{__('Submit')}}</button>
+                </div>
+            </form>
+
+        </div>
+
+    </div>
+@endsection

+ 58 - 0
themes/default/views/admin/roles/index.blade.php

@@ -0,0 +1,58 @@
+@extends('layouts.main')
+
+@section('content')
+    <div class="main py-4">
+
+        @can('admin.roles.write')
+            <div class="d-flex justify-content-end my-3">
+                <a href="{{route('admin.roles.create')}}" class="btn btn-primary"><i
+                        class="fa fas fa-shield-alt pe-2"></i>{{__('Create role')}}</a>
+            </div>
+        @endcan
+
+        <div class="card card-body border-0 shadow table-wrapper table-responsive">
+            <h2 class="mb-4 h5">{{ __('Roles') }}</h2>
+
+            <div class="card-body table-responsive">
+
+                <table id="datatable" class="table table-striped">
+                    <thead>
+                    <tr>
+                        <th>{{__("Name")}}</th>
+                        <th>{{__("User count")}}</th>
+                        <th>{{__("Permissions count")}}</th>
+                        <th>{{__("Actions")}}</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    </tbody>
+                </table>
+
+            </div>
+        </div>
+    </div>
+@endsection
+<script>
+
+    document.addEventListener("DOMContentLoaded", function () {
+        $('#datatable').DataTable({
+            language: {
+                url: '//cdn.datatables.net/plug-ins/1.11.3/i18n/{{config("SETTINGS::LOCALE:DATATABLES")}}.json'
+            },
+            processing: true,
+            serverSide: false, //increases loading times too much? change back to "true" if it does
+            stateSave: true,
+            ajax: "{{route('admin.roles.datatable')}}",
+            columns: [
+                {data: 'name'},
+                {data: 'usercount'},
+                {data: 'permissionscount'},
+                {data: 'actions' , sortable : false},
+            ],
+            fnDrawCallback: function( oSettings ) {
+                $('[data-toggle="popover"]').popover();
+            }
+        });
+    });
+</script>
+

+ 6 - 16
themes/default/views/admin/users/edit.blade.php

@@ -97,24 +97,14 @@
                                 <div class="form-group">
                                     <label for="role">{{__('Role')}}</label>
                                     <div>
-                                        <select id="role" name="role"
+                                        <select id="roles" name="roles"
                                                 class="custom-select @error('role') is-invalid @enderror"
                                                 required="required">
-                                            <option @if($user->role == 'admin') selected @endif class="text-danger"
-                                                    value="admin">
-                                        {{__(' Administrator')}}
-                                     </option>
-                                     <option @if($user->role == 'moderator') selected @endif class="text-info" value="moderator">
-                                        {{__('Moderator')}}
-                                     </option>
-                                     <option @if($user->role == 'client') selected @endif class="text-success"
-                                             value="client">
-                                        {{__('Client')}}
-                                    </option>
-                                    <option @if($user->role == 'member') selected @endif class="text-secondary"
-                                            value="member">
-                                        {{__('Member')}}
-                                    </option>
+                                            @foreach($roles as $role)
+                                                <option style="color: {{$role->color}}"
+                                                        @if(isset($user) && $user->roles->contains($role)) selected
+                                                        @endif value="{{$role->id}}">{{$role->name}}</option>
+                                            @endforeach
                                 </select>
                             </div>
                                 </div>

+ 1 - 1
themes/default/views/admin/users/show.blade.php

@@ -76,7 +76,7 @@
                                 <div class="col-lg-8">
                                     <span style="max-width: 250px;"
                                         class="d-inline-block text-truncate badge
-                                        @if ($user->role == 'admin') badge-danger
+                                        @if ($user->hasRole("Admin")) badge-danger
                                         @elseif ($user->role == 'moderator')
                                             badge-info
                                         @elseif ($user->role == 'client')

+ 1 - 1
themes/default/views/home.blade.php

@@ -18,7 +18,7 @@
     </section>
     <!-- END CONTENT HEADER -->
 
-    @if (!file_exists(base_path() . '/install.lock') && Auth::User()->role == 'admin')
+    @if (!file_exists(base_path() . '/install.lock') && Auth::User()->hasRole("Admin"))
         <div class="callout callout-danger">
             <h4>{{ __('The installer is not locked!') }}</h4>
             <p>{{ __('please create a file called "install.lock" in your dashboard Root directory. Otherwise no settings will be loaded!') }}

+ 9 - 2
themes/default/views/layouts/main.blade.php

@@ -255,7 +255,7 @@
                             </li>
                         @endif
 
-                        @if ((Auth::user()->role == 'admin' || Auth::user()->role == 'moderator') && $ticket_enabled)
+                        @if ((Auth::user()->hasRole("Admin") || Auth::user()->role == 'moderator') && $ticket_enabled)
                             <li class="nav-header">{{ __('Moderation') }}</li>
 
                             <li class="nav-item">
@@ -274,7 +274,7 @@
                             </li>
                         @endif
 
-                        @if (Auth::user()->role == 'admin')
+                        @if (Auth::user()->hasRole("Admin"))
                             <li class="nav-header">{{ __('Administration') }}</li>
 
                             <li class="nav-item">
@@ -285,6 +285,13 @@
                                 </a>
                             </li>
 
+                            <li class="nav-item">
+                                <a href="{{ route('admin.roles.index') }}"
+                                   class="nav-link @if (Request::routeIs('admin.roles.*')) active @endif">
+                                    <i class="nav-icon fa fa-user-check"></i>
+                                    <p>{{ __('Role Management') }}</p>
+                                </a>
+                            </li>
 
                             <li class="nav-item">
                                 <a href="{{ route('admin.settings.index') }}"

+ 2 - 2
themes/default/views/moderator/ticket/show.blade.php

@@ -118,7 +118,7 @@
                                             <span class="badge badge-success"> Client </span>
                                         @elseif ($ticket->user->role === "moderator")
                                             <span class="badge badge-info"> Moderator </span>
-                                        @elseif ($ticket->user->role === "admin")
+                                        @elseif ($ticket->user->hasRole("Admin"))
                                             <span class="badge badge-danger"> Admin </span>
                                         @endif
                                     </h5>
@@ -141,7 +141,7 @@
                                             <span class="badge badge-success"> Client </span>
                                         @elseif ($ticketcomment->user->role === "moderator")
                                             <span class="badge badge-info"> Moderator </span>
-                                        @elseif ($ticketcomment->user->role === "admin")
+                                        @elseif ($ticketcomment->user->hasRole("Admin"))
                                             <span class="badge badge-danger"> Admin </span>
                                         @endif
                                     </h5>

+ 1 - 1
themes/default/views/servers/create.blade.php

@@ -45,7 +45,7 @@
                             <div class="alert alert-danger p-2 m-2">
                                 <h5><i class="icon fas fa-exclamation-circle"></i>{{ __('Error!') }}</h5>
                                 <p class="pl-4">
-                                    @if (Auth::user()->role == 'admin')
+                                    @if (Auth::user()->hasRole("Admin"))
                                         {{ __('Make sure to link your products to nodes and eggs.') }} <br>
                                         {{ __('There has to be at least 1 valid product for server creation') }}
                                         <a href="{{ route('admin.overview.sync') }}">{{ __('Sync now') }}</a>

+ 2 - 2
themes/default/views/ticket/show.blade.php

@@ -118,7 +118,7 @@
                                                 <span class="badge badge-success"> Client </span>
                                             @elseif ($ticket->user->role === "moderator")
                                                 <span class="badge badge-info"> Moderator </span>
-                                            @elseif ($ticket->user->role === "admin")
+                                            @elseif ($ticket->user->hasRole("Admin"))
                                                 <span class="badge badge-danger"> Admin </span>
                                             @endif
                                         </h5>
@@ -142,7 +142,7 @@
                                                     <span class="badge badge-success"> Client </span>
                                                 @elseif ($ticketcomment->user->role === "moderator")
                                                     <span class="badge badge-info"> Moderator </span>
-                                                @elseif ($ticketcomment->user->role === "admin")
+                                                @elseif ($ticketcomment->user->hasRole("Admin"))
                                                     <span class="badge badge-danger"> Admin </span>
                                                 @endif
                                             </h5>