mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
Implement RBAC (only built in roles) (#15212)
* Install bouncer * Seeder and level migration * Display and edit roles * remove unused deluser page * Update Radius and SSO to assign roles * update AlertUtil direct level check to use roles instead * rewrite ircbot auth handling * Remove legacy auth getUserlist and getUserlevel methods, add getRoles Set roles in LegacyUserProvider * Small cleanups * centralize role sync code show roles on user preferences page * VueSelect component WIP and a little docs * WIP * SelectControllers id and text fields. * LibrenmsSelect component extracted from SettingSelectDynamic * Handle multiple selections * allow type coercion * full width settings * final style adjustments * Final compiled assets update * Style fixes * Fix SSO tests * Lint cleanups * small style fix * don't use json yet * Update baseline for usptream package issues * Change schema, not 100% sure it is correct not sure why xor doesn't work
This commit is contained in:
42
database/factories/RoleFactory.php
Normal file
42
database/factories/RoleFactory.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/*
|
||||
* RoleFactory.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2023 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Silber\Bouncer\Database\Role;
|
||||
|
||||
class RoleFactory extends Factory
|
||||
{
|
||||
protected $model = Role::class;
|
||||
|
||||
public function definition()
|
||||
{
|
||||
return [
|
||||
'name' => $this->faker->text(),
|
||||
'title' => $this->faker->text(),
|
||||
];
|
||||
}
|
||||
}
|
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Silber\Bouncer\BouncerFacade as Bouncer;
|
||||
|
||||
/** @extends Factory<User> */
|
||||
/** @extends Factory<\App\Models\User> */
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
@@ -21,25 +21,23 @@ class UserFactory extends Factory
|
||||
'realname' => $this->faker->name(),
|
||||
'email' => $this->faker->safeEmail(),
|
||||
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
|
||||
'level' => 1,
|
||||
];
|
||||
}
|
||||
|
||||
public function admin()
|
||||
{
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'level' => '10',
|
||||
];
|
||||
return $this->afterCreating(function ($user) {
|
||||
Bouncer::allow('admin')->everything();
|
||||
$user->assign('admin');
|
||||
});
|
||||
}
|
||||
|
||||
public function read()
|
||||
{
|
||||
return $this->state(function () {
|
||||
return [
|
||||
'level' => '5',
|
||||
];
|
||||
return $this->afterCreating(function ($user) {
|
||||
Bouncer::allow(Bouncer::role()->firstOrCreate(['name' => 'global-read'], ['title' => 'Global Read']))
|
||||
->to('viewAny', '*', []);
|
||||
$user->assign('global-read');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
100
database/migrations/2023_06_18_195618_create_bouncer_tables.php
Normal file
100
database/migrations/2023_06_18_195618_create_bouncer_tables.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Silber\Bouncer\Database\Models;
|
||||
|
||||
class CreateBouncerTables extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (! Schema::hasTable('abilities')) {
|
||||
Schema::create(Models::table('abilities'), function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('name');
|
||||
$table->string('title')->nullable();
|
||||
$table->bigInteger('entity_id')->unsigned()->nullable();
|
||||
$table->string('entity_type')->nullable();
|
||||
$table->boolean('only_owned')->default(false);
|
||||
$table->longText('options')->nullable();
|
||||
$table->integer('scope')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasTable('roles')) {
|
||||
Schema::create(Models::table('roles'), function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->string('name');
|
||||
$table->string('title')->nullable();
|
||||
$table->integer('scope')->nullable()->index();
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(
|
||||
['name', 'scope'],
|
||||
'roles_name_unique'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasTable('assigned_roles')) {
|
||||
Schema::create(Models::table('assigned_roles'), function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('role_id')->unsigned()->index();
|
||||
$table->bigInteger('entity_id')->unsigned();
|
||||
$table->string('entity_type');
|
||||
$table->bigInteger('restricted_to_id')->unsigned()->nullable();
|
||||
$table->string('restricted_to_type')->nullable();
|
||||
$table->integer('scope')->nullable()->index();
|
||||
|
||||
$table->index(
|
||||
['entity_id', 'entity_type', 'scope'],
|
||||
'assigned_roles_entity_index'
|
||||
);
|
||||
|
||||
$table->foreign('role_id')
|
||||
->references('id')->on(Models::table('roles'))
|
||||
->onUpdate('cascade')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasTable('permissions')) {
|
||||
Schema::create(Models::table('permissions'), function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('ability_id')->unsigned()->index();
|
||||
$table->bigInteger('entity_id')->unsigned()->nullable();
|
||||
$table->string('entity_type')->nullable();
|
||||
$table->boolean('forbidden')->default(false);
|
||||
$table->integer('scope')->nullable()->index();
|
||||
|
||||
$table->index(
|
||||
['entity_id', 'entity_type', 'scope'],
|
||||
'permissions_entity_index'
|
||||
);
|
||||
|
||||
$table->foreign('ability_id')
|
||||
->references('id')->on(Models::table('abilities'))
|
||||
->onUpdate('cascade')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::drop(Models::table('permissions'));
|
||||
Schema::drop(Models::table('assigned_roles'));
|
||||
Schema::drop(Models::table('roles'));
|
||||
Schema::drop(Models::table('abilities'));
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Silber\Bouncer\BouncerFacade as Bouncer;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
User::all()->each(function (User $user) {
|
||||
$role = match ($user->getAttribute('level')) {
|
||||
1 => 'user',
|
||||
5 => 'global-read',
|
||||
10 => 'admin',
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($role) {
|
||||
Bouncer::assign($role)->to($user);
|
||||
}
|
||||
});
|
||||
|
||||
Bouncer::refresh(); // clear cache
|
||||
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('level');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (! Schema::hasColumn('users', 'level')) {
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->tinyInteger('level')->default(0)->after('descr');
|
||||
});
|
||||
}
|
||||
|
||||
User::whereIs('admin', 'global-read', 'user')->get()->each(function (User $user) {
|
||||
$user->setAttribute('level', $this->getLevel($user));
|
||||
$user->save();
|
||||
});
|
||||
|
||||
Bouncer::refresh();
|
||||
}
|
||||
|
||||
private function getLevel(User $user): int
|
||||
{
|
||||
if ($user->isA('admin')) {
|
||||
return 10;
|
||||
}
|
||||
|
||||
if ($user->isA('global-read')) {
|
||||
return 7;
|
||||
}
|
||||
|
||||
if ($user->isA('user')) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
41
database/seeders/RolesSeeder.php
Normal file
41
database/seeders/RolesSeeder.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/**
|
||||
* RolesSeeder.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @link https://www.librenms.org
|
||||
*
|
||||
* @copyright 2023 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Silber\Bouncer\BouncerFacade as Bouncer;
|
||||
|
||||
class RolesSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
// set abilities for default rules
|
||||
Bouncer::allow('admin')->everything();
|
||||
Bouncer::allow(Bouncer::role()->firstOrCreate(['name' => 'global-read'], ['title' => 'Global Read']))
|
||||
->to('viewAny', '*', []);
|
||||
Bouncer::role()->firstOrCreate(['name' => 'user'], ['title' => 'User']);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user