Add admin panel system

Add two commands RemoveUnconfirmedAccount and SetAccountAdmin
This commit is contained in:
Timothée Jaussoin 2020-04-29 16:42:11 +02:00
parent 44ee674480
commit f98df3f830
18 changed files with 379 additions and 10 deletions

View file

@ -25,7 +25,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
class Account extends Authenticatable
{
protected $connection = 'external';
protected $with = ['passwords'];
protected $with = ['passwords', 'admin'];
protected $dates = ['creation_time'];
public $timestamps = false;
@ -44,8 +44,18 @@ class Account extends Authenticatable
return $this->hasMany('App\DigestNonce');
}
public function admin()
{
return $this->hasOne('App\Admin');
}
public function getIdentifierAttribute()
{
return $this->attributes['username'].'@'.$this->attributes['domain'];
}
public function isAdmin()
{
return ($this->admin);
}
}

16
flexiapi/app/Admin.php Normal file
View file

@ -0,0 +1,16 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Admin extends Model
{
protected $connection = 'local';
protected $table = 'admins';
public function account()
{
return $this->belongsTo('App\Account');
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Carbon\Carbon;
use App\Account;
class RemoveUnconfirmedAccounts extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'accounts:clear-unconfirmed {days} {--apply}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clear unconfirmed accounts after n days';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$accounts = Account::where('activated', false)->where('creation_time', '<',
Carbon::now()->subDays($this->argument('days'))->toDateTimeString()
)->get();
if ($this->option('apply')) {
$this->info($accounts->count() . ' accounts deleted');
$accounts->delete();
} else {
$this->info($accounts->count() . ' accounts to delete');
}
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Account;
use App\Admin;
class SetAccountAdmin extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'accounts:set-admin {id}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Give the admin role to an account';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$account = Account::where('id', $this->argument('id'))->first();
if (!$account) $this->error('Account not found, please use an existing ID');
$admin = new Admin;
$admin->account_id = $account->id;
$admin->save();
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Account;
use App\Admin;
class AccountController extends Controller
{
public function index(Request $request)
{
return view('admin.account.index', [
'accounts' => Account::orderBy('creation_time', 'desc')->paginate(30)
]);
}
public function show(Request $request, $id)
{
return view('admin.account.show', [
'account' => Account::findOrFail($id)
]);
}
public function activate(Request $request, $id)
{
$account = Account::findOrFail($id);
$account->activated = true;
$account->save();
return redirect()->back();
}
public function deactivate(Request $request, $id)
{
$account = Account::findOrFail($id);
$account->activated = false;
$account->save();
return redirect()->back();
}
public function admin(Request $request, $id)
{
$account = Account::findOrFail($id);
$admin = new Admin;
$admin->account_id = $account->id;
$admin->save();
return redirect()->back();
}
public function unadmin(Request $request, $id)
{
$account = Account::findOrFail($id);
// An admin cannot remove it's own permission
if ($account->id == $request->user()->id) abort(403);
if ($account->admin) $account->admin->delete();
return redirect()->back();
}
}

View file

@ -69,6 +69,7 @@ class Kernel extends HttpKernel
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.admin' => \App\Http\Middleware\AuthenticateAdmin::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.digest' => \App\Http\Middleware\AuthenticateDigest::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,

View file

@ -0,0 +1,24 @@
<?php
namespace App\Http\Middleware;
use Closure;
class AuthenticateAdmin
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (!$request->user()->isAdmin()) {
return abort(403, 'Unauthorized area');
}
return $next($request);
}
}

View file

@ -13,7 +13,7 @@ class CreateUsersTable extends Migration
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
Schema::connection('local')->create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
@ -31,6 +31,6 @@ class CreateUsersTable extends Migration
*/
public function down()
{
Schema::dropIfExists('users');
Schema::connection('local')->dropIfExists('users');
}
}

View file

@ -13,7 +13,7 @@ class CreatePasswordResetsTable extends Migration
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
Schema::connection('local')->create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
@ -27,6 +27,6 @@ class CreatePasswordResetsTable extends Migration
*/
public function down()
{
Schema::dropIfExists('password_resets');
Schema::connection('local')->dropIfExists('password_resets');
}
}

View file

@ -13,7 +13,7 @@ class CreateFailedJobsTable extends Migration
*/
public function up()
{
Schema::create('failed_jobs', function (Blueprint $table) {
Schema::connection('local')->create('failed_jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('connection');
$table->text('queue');
@ -30,6 +30,6 @@ class CreateFailedJobsTable extends Migration
*/
public function down()
{
Schema::dropIfExists('failed_jobs');
Schema::connection('local')->dropIfExists('failed_jobs');
}
}

View file

@ -68,7 +68,7 @@ class CreateAccountsPasswordsTables extends Migration
*/
public function down()
{
Schema::connection('external')->dropIfExists('passwords');
Schema::connection('external')->dropIfExists('accounts');
//Schema::connection('external')->dropIfExists('passwords');
//Schema::connection('external')->dropIfExists('accounts');
}
}

View file

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateAdminsTable extends Migration
{
public function up()
{
Schema::connection('local')->create('admins', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('account_id')->unsigned();
$table->timestamps();
});
}
public function down()
{
Schema::connection('local')->dropIfExists('admins');
}
}

View file

@ -2,7 +2,7 @@
@section('content')
<div class="list-group">
<div class="list-group mb-3">
<a href="{{ route('account.email') }}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Change my current account email</h5>
@ -31,4 +31,20 @@
</a>
</div>
@if($account->isAdmin())
<h3>Admin area</h3>
<div class="list-group">
<a href="{{ route('admin.account.index') }}" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Change my current account email</h5>
</div>
@if (!empty($account->email))
<p class="mb-1">{{ $account->email }}</p>
@else
<p class="mb-1">No email yet</p>
@endif
</a>
</div>
@endif
@endsection

View file

@ -0,0 +1,48 @@
@extends('layouts.account')
@section('breadcrumb')
<li class="breadcrumb-item active" aria-current="page">Accounts</li>
@endsection
@section('content')
<h2>Accounts</h2>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Identifier</th>
<th scope="col">Email</th>
<th scope="col">Created</th>
<th scope="col">Tags</th>
</tr>
</thead>
<tbody>
@foreach ($accounts as $account)
<tr>
<th scope="row">
<a href="{{ route('admin.account.show', $account->id) }}">{{ $account->id }}</a>
</th>
<td>{{ $account->identifier }}</td>
<td>{{ $account->email }}</td>
<td>{{ $account->creation_time}}</td>
<td>
@if ($account->activated)
<span class="badge badge-success">Activated</span>
@else
<span class="badge badge-danger">Unactivated</span>
@endif
@if ($account->admin)
<span class="badge badge-primary">Admin</span>
@endif
</td>
</tr>
@endforeach
</tbody>
</table>
{{ $accounts->links() }}
@endsection

View file

@ -0,0 +1,42 @@
@extends('layouts.account')
@section('breadcrumb')
<li class="breadcrumb-item" aria-current="page">
<a href="{{ route('admin.account.index') }}">Accounts</a>
</li>
<li class="breadcrumb-item active" aria-current="page">Show</li>
@endsection
@section('content')
<h2>Account</h2>
<p>
<b>Id:</b> {{ $account->id }}<br />
<b>Identifier:</b> {{ $account->identifier }}<br />
<b>Email:</b> {{ $account->email }}</p>
</p>
@if ($account->alias)
<p><b>Alias:</b> {{ $account->alias->alias }}</p>
@else
<p>No alias</p>
@endif
<p>
@if ($account->activated)
<span class="badge badge-success">Activated</span> <a href="{{ route('admin.account.deactivate', $account->id) }}">Deactivate</a>
@else
<span class="badge badge-danger">Unactivated</span> <a href="{{ route('admin.account.activate', $account->id) }}">Activate</a>
@endif
</p>
<p>
@if ($account->admin)
<span class="badge badge-success">Activated</span> <a href="{{ route('admin.account.unadmin', $account->id) }}">Remove admin role</a>
@else
<span class="badge badge-danger">Unactivated</span> <a href="{{ route('admin.account.admin', $account->id) }}">Add admin role</a>
@endif
</p>
@endsection

View file

@ -19,6 +19,7 @@
</nav>
<div class="container-lg pt-3">
@include('parts.errors')
@include('parts.breadcrumb')
@yield('content')
</div>
@endsection

View file

@ -0,0 +1,8 @@
@hasSection('breadcrumb')
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Home</a></li>
@yield('breadcrumb')
</ol>
</nav>
@endif

View file

@ -45,4 +45,13 @@ Route::group(['middleware' => 'auth'], function () {
Route::post('email', 'AccountEmailController@update')->name('account.email.update');
Route::get('password', 'AccountPasswordController@show')->name('account.password');
Route::post('password', 'AccountPasswordController@update')->name('account.password.update');
});
Route::group(['middleware' => 'auth.admin'], function () {
Route::get('admin/accounts', 'Admin\AccountController@index')->name('admin.account.index');
Route::get('admin/accounts/{id}', 'Admin\AccountController@show')->name('admin.account.show');
Route::get('admin/accounts/{id}/activate', 'Admin\AccountController@activate')->name('admin.account.activate');
Route::get('admin/accounts/{id}/deactivate', 'Admin\AccountController@deactivate')->name('admin.account.deactivate');
Route::get('admin/accounts/{id}/admin', 'Admin\AccountController@admin')->name('admin.account.admin');
Route::get('admin/accounts/{id}/unadmin', 'Admin\AccountController@unadmin')->name('admin.account.unadmin');
});