Multi-users

This commit is contained in:
MarceauKa 2019-10-10 15:02:10 +02:00
parent 603eec7fe8
commit c678694de3
14 changed files with 134 additions and 18 deletions

View File

@ -25,7 +25,7 @@ class ChestController extends Controller
'content',
])->toArray());
$post = new Post(['is_private' => true]);
$post = new Post(['is_private' => true, 'user_id' => $request->user()->id]);
$post->postable()->associate($chest)->save();
if ($data['tags']) {

View File

@ -58,6 +58,7 @@ class LinkController extends Controller
$post = new Post();
$post->is_private = $data->get('is_private', 0);
$post->user_id = $request->user()->id;
$post->postable()->associate($link)->save();
if ($data['tags']) {

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\Manage;
use App\Http\Controllers\Controller;
use App\Http\Requests\Manage\StoreUserRequest;
use App\Http\Requests\Manage\UpdateUserRequest;
use App\Http\Resources\UserResource;
use App\Post;
use App\User;
@ -37,6 +38,28 @@ class UsersController extends Controller
]);
}
public function update(UpdateUserRequest $request, int $id)
{
$validated = collect($request->validated());
$user = User::findOrFail($id);
$user->fill($validated->only('name', 'email')->toArray());
$user->is_admin = $validated->get('is_admin', 0) == 1;
if ($validated->get('password')) {
$user->password = Hash::make($validated->get('password'));
$user->api_token = User::generateApiToken();
}
$user->save();
return response()->json([
'status' => 'updated',
'id' => $user->id,
]);
}
public function delete(Request $request, int $id)
{
if ($request->user()->id == $id) {
@ -49,7 +72,13 @@ class UsersController extends Controller
$user = User::findOrFail($id);
$user->delete();
Post::where('user_id', $id)->delete();
$admin = User::isAdmin()->first();
if ($admin) {
Post::where('user_id', $id)->update('user_id', $admin->id);
} else {
Post::where('user_id', $id)->update('user_id', null);
}
return response()->json([
'status' => 'deleted',

View File

@ -28,6 +28,7 @@ class StoryController extends Controller
$post = new Post();
$post->is_private = $data->get('is_private', 0);
$post->user_id = $request->user()->id;
$post->postable()->associate($story)->save();
if ($data['tags']) {

View File

@ -8,11 +8,13 @@ use App\Post;
use App\Story;
use App\Tag;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class BrowseController extends Controller
{
public function index(Request $request)
{
DB::enableQueryLog();
if (true === app('shaarli')->getHomepageAlt()) {
$posts = Post::with('tags', 'postable')
->withoutChests()
@ -35,7 +37,7 @@ class BrowseController extends Controller
->withPrivate($request)
->latest()
->paginate(20);
//dd(DB::getQueryLog());
return view('home')->with([
'page_title' => app('shaarli')->getName(),
'posts' => $posts,

View File

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests\Manage;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateUserRequest extends FormRequest
{
public function authorize()
{
return auth()->check() && auth()->user()->is_admin;
}
public function rules()
{
$rules = [
'name' => [
'required',
'min:2',
'max:255',
],
'email' => [
'required',
'email',
Rule::unique('users', 'email')->ignore($this->route('id'), 'id'),
],
'password' => [
'nullable',
'confirmed',
'min:8',
]
];
if (auth()->user()->id == $this->route('id')) {
$rules['is_admin'] = [
'required',
'in:1'
];
} else {
$rules['is_admin'] = [
'nullable',
];
}
return $rules;
}
}

View File

@ -5,6 +5,7 @@ namespace App;
use App\Concerns\Models\HasTags;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Http\Request;
use Laravel\Scout\Searchable;
@ -22,6 +23,7 @@ class Post extends Model
'postable_type',
'postable_id',
'is_private',
'user_id',
'created_at',
];
protected $casts = [
@ -33,6 +35,11 @@ class Post extends Model
return $this->morphTo();
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function getCreatedAtFormatedAttribute(): string
{
return $this->created_at->diffForHumans();
@ -40,18 +47,28 @@ class Post extends Model
public function scopeWithPrivate(Builder $query, $private = false): Builder
{
$user = null;
if ($private instanceof Request) {
$private = $private->user() instanceof User;
$user = $private->user();
} else if ($private instanceof User) {
$user = $private;
}
if ($private instanceof User) {
$private = true;
}
if ($private === false) {
if (! $user) {
return $query->where('is_private', 0);
}
if ($user->is_admin === false) {
return $query
->where('is_private', 0)
->orWhere(function ($query) use ($user) {
return $query
->where('is_private', 1)
->where('user_id', $user->id);
});
}
return $query;
}

View File

@ -2,6 +2,8 @@
namespace App;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
@ -31,6 +33,16 @@ class User extends Authenticatable implements HasLoginsAndDevicesInterface
'is_admin' => 'bool',
];
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
public function scopeIsAdmin(Builder $query): Builder
{
return $query->where('is_admin', 1);
}
public static function generateApiToken(): string
{
return Str::random(128);

View File

@ -54,7 +54,7 @@ class DatabaseSeeder extends Seeder
];
foreach ($items as $item) {
$post = new App\Post(['is_private' => get_class($item) === Chest::class]);
$post = new App\Post(['is_private' => get_class($item) === Chest::class, 'user_id' => 1]);
$item->post()->save($post);
}

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/app.js": "/js/app.js?id=742ab0f0dd07e1506a39",
"/js/app.js": "/js/app.js?id=290226c7dec36f245097",
"/css/app.css": "/css/app.css?id=3716ce4aecaaa0a26ddd"
}

View File

@ -45,8 +45,9 @@ but built with [Laravel](https://github.com/laravel/laravel) and [Vue.js](https:
- [x] Export
- [x] Dark mode
- [x] i18n (english and french)
- [x] **NEW** Archiving
- [x] **NEW** 2-FA with email
- [x] Archiving
- [x] 2-FA with email
- [x] **NEW** Multi-users
- [ ] RSS feed
## Demo
@ -103,7 +104,12 @@ Shaarli logs all successful and failed auths with their associated devices.
### Chests encryption
Since `1.2.9`, all chests data are encrypted in your database using AES-256-CBC and your app key.
Since `1.2.9`, all chests data are encrypted in your database using AES-256-CBC and your app key.
### Multi-users
Others users can be admin or non-admin. Admin users are like the main user and have an access to the entire content.
Non-admin users can't access the settings section and can only see their own private content.
## Update

View File

@ -18,7 +18,7 @@
</div>
</div>
<div class="row" v-if="! user">
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="form-group">
<label for="password">{{ __('Password') }}</label>
@ -41,7 +41,7 @@
<label class="custom-control-label" for="is_admin">{{ __('Is admin?') }}</label>
</div>
<span class="text-muted">{{ __("Admin users can access settings") }}</span>
<span class="text-muted">{{ __("Admin users can access settings and other users private content") }}</span>
</div>
<button class="btn btn-primary" @click.prevent="submit" :disabled="loading">

View File

@ -191,7 +191,7 @@
"Add user": "Ajouter utilisateur",
"Update user": "Modifier utilisateur",
"Is admin?": "Administrateur ?",
"Admin users can access settings": "Les utilisateurs admin peuvent accéder aux paramètres",
"Admin users can access settings and other users private content": "Les utilisateurs admin peuvent accéder aux paramètres et contenu privé des autres utilisateurs",
"Unable to load users": "Impossible de charger les utilisateurs",
"Unable to save user": "Impossible d'enregistrer l'utilisateur",
"User created": "L'utilisateur a été créé",