Added option for admins to impersonate other users
Inspired by: https://github.com/nextcloud/impersonate
This commit is contained in:
parent
9933713d91
commit
3f62c84406
|
@ -665,4 +665,54 @@ public function SendTestMail(Request $request)
|
|||
{
|
||||
return view('/panel/theme');
|
||||
}
|
||||
|
||||
//Removes impersonation if authenticated
|
||||
public function authAs(request $request)
|
||||
{
|
||||
|
||||
$userID = $request->id;
|
||||
$token = $request->token;
|
||||
|
||||
$user = User::find($userID);
|
||||
|
||||
if($user->remember_token == $token){
|
||||
$user->auth_as = null;
|
||||
$user->remember_token = null;
|
||||
$user->save();
|
||||
|
||||
setcookie("display_auth_nav", "", time() - 3600, "/");
|
||||
|
||||
Auth::loginUsingId($userID);
|
||||
|
||||
return redirect('/admin/users/all');
|
||||
} else {
|
||||
return redirect('');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Removes impersonation if authenticated
|
||||
public function authAsID(request $request)
|
||||
{
|
||||
|
||||
$adminUser = User::whereNotNull('auth_as')->where('role', 'admin')->first();
|
||||
|
||||
if (!$adminUser) {
|
||||
|
||||
$userID = $request->id;
|
||||
$id = Auth::user()->id;
|
||||
|
||||
$user = User::find($id);
|
||||
|
||||
$user->auth_as = $userID;
|
||||
$user->save();
|
||||
|
||||
return redirect('dashboard');
|
||||
|
||||
} else {
|
||||
return redirect('admin/users/all');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,5 +65,6 @@ class Kernel extends HttpKernel
|
|||
'admin' => \App\Http\Middleware\admin::class,
|
||||
'blocked' => \App\Http\Middleware\CheckBlockedUser::class,
|
||||
'max.users' => \App\Http\Middleware\MaxUsers::class,
|
||||
'impersonate' => \App\Http\Middleware\Impersonate::class,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
|
||||
class Impersonate
|
||||
{
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$adminUser = User::whereNotNull('auth_as')->where('role', 'admin')->first();
|
||||
|
||||
if ($adminUser) {
|
||||
|
||||
$originalUser = $adminUser->id;
|
||||
|
||||
$id = is_numeric($adminUser->auth_as) ? $adminUser->auth_as : $adminUser->id;
|
||||
$user = User::find($id);
|
||||
|
||||
$name = $user->name;
|
||||
|
||||
if(Auth::user()->id === $originalUser) {
|
||||
|
||||
// Generate unique token
|
||||
$token = Str::random(60);
|
||||
if(\Route::currentRouteName() !== 'authAs'){
|
||||
$adminUser->remember_token = $token;
|
||||
$adminUser->save();
|
||||
echo "<script>window.location.href = '".url('studio/links')."';</script>";
|
||||
}
|
||||
|
||||
Auth::loginUsingId($id);
|
||||
setcookie("display_auth_nav", "true", time() + (10 * 365 * 24 * 60 * 60), "/");
|
||||
}
|
||||
|
||||
if(isset($_COOKIE['display_auth_nav'])) {
|
||||
if (file_exists(base_path(findAvatar($id)))) {
|
||||
$img = '<img alt="avatar" class="iimg irounded" src="' . url(findAvatar($id)) . '">';
|
||||
} elseif (file_exists(base_path("assets/linkstack/images/").findFile('avatar'))) {
|
||||
$img = '<img alt="avatar" class="iimg irounded" src="' . url("assets/linkstack/images/") . "/" . findFile('avatar') . '">';
|
||||
} else {
|
||||
$img = '<img alt="avatar" class="iimg" src="' . asset('assets/linkstack/images/logo.svg') . '">';
|
||||
}
|
||||
$dashboard = url('dashboard');
|
||||
$URL = url('/auth-as');
|
||||
$csrf = csrf_token();
|
||||
$remember_token = User::find($originalUser);
|
||||
$token = $remember_token->remember_token;
|
||||
$customHtml =
|
||||
<<<EOD
|
||||
|
||||
<style>
|
||||
.ibar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 67px;
|
||||
background-color: #4d4c51;
|
||||
z-index: 911;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.itext1 {
|
||||
color: white;
|
||||
font-family: "Inter", sans-serif;
|
||||
font-size: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 17px 16px;
|
||||
}
|
||||
|
||||
.itext1 span a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.itext1 a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.itext1 svg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
fill: currentColor;
|
||||
margin-left: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.iimg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.irounded {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 60px; /* Add padding equal to the height of .ibar */
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="ibar">
|
||||
<p class="itext1">
|
||||
<span>
|
||||
<a href="$dashboard">$img $name</a>
|
||||
</span>
|
||||
<a style="cursor:pointer" onclick="document.getElementById('submitForm').submit(); return false;">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="bi bi-x" viewBox="0 0 16 16">
|
||||
<path
|
||||
d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<form id="submitForm" action="$URL" method="POST" style="display: none;">
|
||||
<input type="hidden" name="_token" value="$csrf">
|
||||
<input type="hidden" name="token" value="$token">
|
||||
<input type="hidden" name="id" value="$originalUser">
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function submitForm() {
|
||||
document.getElementById('submitForm').submit();
|
||||
}
|
||||
</script>
|
||||
|
||||
EOD;;
|
||||
} else {$customHtml = "";}
|
||||
|
||||
$response = $next($request);
|
||||
$content = $response->getContent();
|
||||
$modifiedContent = preg_replace('/<body([^>]*)>/', "<body$1>{$customHtml}", $content);
|
||||
$response->setContent($modifiedContent);
|
||||
|
||||
return $response;
|
||||
} else {
|
||||
if(isset($_COOKIE['display_auth_nav'])) {
|
||||
setcookie("display_auth_nav", "", time() - 3600, "/");
|
||||
Auth::logout();
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ class CreateUsersTable extends Migration
|
|||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
$table->string('theme')->nullable();
|
||||
$table->unsignedBigInteger('auth_as')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -521,6 +521,7 @@ return [
|
|||
|
||||
# Tooltips
|
||||
'tt.Delete' => 'Delete',
|
||||
'tt.Impersonate' => 'Impersonate',
|
||||
'tt.Edit' => 'Edit',
|
||||
'tt.All links' => 'All links',
|
||||
|
||||
|
|
|
@ -154,6 +154,14 @@ use App\Models\Page;
|
|||
} catch (exception $e) {}
|
||||
Schema::enableForeignKeyConstraints();
|
||||
|
||||
// Adds new column to the users table
|
||||
try {
|
||||
if (!Schema::hasColumn('users', 'auth_as')) {
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->unsignedBigInteger('auth_as')->nullable();
|
||||
});
|
||||
}} catch (exception $e) {}
|
||||
|
||||
try {
|
||||
DB::table('link_types')->updateOrInsert([
|
||||
'typename' => 'text',
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
<?php use App\Models\User; ?>
|
||||
|
||||
@extends('layouts.sidebar')
|
||||
|
||||
@section('content')
|
||||
|
@ -117,6 +119,15 @@
|
|||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
@php $adminUser = User::whereNotNull('auth_as')->where('role', 'admin')->first(); @endphp
|
||||
<a class="btn btn-sm btn-icon btn-primary" style="@if(!$adminUser) background:#3a57e8;border-color:#3a57e8; @else background:#6c757d;border-color:#6c757d; @endif" data-bs-toggle="tooltip" data-bs-placement="top" data-original-title="{{__('messages.tt.Impersonate')}}" @if(!$adminUser) href="{{ route('authAsID', $user->id ) }}" @endif aria-label="Impersonate" data-bs-original-title="Impersonate">
|
||||
<span class="btn-inner">
|
||||
<svg class="icon-20" width="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.59151 15.2068C13.2805 15.2068 16.4335 15.7658 16.4335 17.9988C16.4335 20.2318 13.3015 20.8068 9.59151 20.8068C5.90151 20.8068 2.74951 20.2528 2.74951 18.0188C2.74951 15.7848 5.88051 15.2068 9.59151 15.2068Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.59157 12.0198C7.16957 12.0198 5.20557 10.0568 5.20557 7.63476C5.20557 5.21276 7.16957 3.24976 9.59157 3.24976C12.0126 3.24976 13.9766 5.21276 13.9766 7.63476C13.9856 10.0478 12.0356 12.0108 9.62257 12.0198H9.59157Z" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M16.4829 10.8815C18.0839 10.6565 19.3169 9.28253 19.3199 7.61953C19.3199 5.98053 18.1249 4.62053 16.5579 4.36353" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path> <path d="M18.5952 14.7322C20.1462 14.9632 21.2292 15.5072 21.2292 16.6272C21.2292 17.3982 20.7192 17.8982 19.8952 18.2112" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-icon btn-danger confirmation" data-bs-toggle="tooltip" data-bs-placement="top" data-original-title="{{__('messages.tt.Delete')}}" href="{{ route('deleteUser', ['id' => $user->id] ) }}" aria-label="Delete" data-bs-original-title="Delete">
|
||||
<span class="btn-inner">
|
||||
<svg class="icon-20" width="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="currentColor">
|
||||
|
|
|
@ -96,7 +96,7 @@ Route::get('/vcard/{id?}', [UserController::class, 'vcard'])->name('vcard');
|
|||
|
||||
Route::get('/demo-page', [App\Http\Controllers\HomeController::class, 'demo'])->name('demo');
|
||||
|
||||
Route::middleware(['auth', 'blocked'])->group(function () {
|
||||
Route::middleware(['auth', 'blocked', 'impersonate'])->group(function () {
|
||||
//User route
|
||||
Route::group([
|
||||
'middleware' => env('REGISTER_AUTH'),
|
||||
|
@ -128,6 +128,7 @@ Route::post('/edit-icons', [UserController::class, 'editIcons'])->name('editIcon
|
|||
Route::get('/clearIcon/{id}', [UserController::class, 'clearIcon'])->name('clearIcon');
|
||||
Route::get('/studio/page/delprofilepicture', [UserController::class, 'delProfilePicture'])->name('delProfilePicture');
|
||||
Route::get('/studio/delete-user/{id}', [UserController::class, 'deleteUser'])->name('deleteUser')->middleware('verified');
|
||||
Route::post('/auth-as', [AdminController::class, 'authAs'])->name('authAs');
|
||||
if(env('ALLOW_USER_EXPORT') != false){
|
||||
Route::get('/export-links', [UserController::class, 'exportLinks'])->name('exportLinks');
|
||||
Route::get('/export-all', [UserController::class, 'exportAll'])->name('exportAll');
|
||||
|
@ -144,7 +145,7 @@ Route::get('/studio/linkparamform_part/{typeid}/{linkid}', [LinkTypeViewControll
|
|||
Route::get('/social-auth/{provider}/callback', [SocialLoginController::class, 'providerCallback']);
|
||||
Route::get('/social-auth/{provider}', [SocialLoginController::class, 'redirectToProvider'])->name('social.redirect');
|
||||
|
||||
Route::middleware(['auth', 'blocked'])->group(function () {
|
||||
Route::middleware(['auth', 'blocked', 'impersonate'])->group(function () {
|
||||
//Admin route
|
||||
Route::group([
|
||||
'middleware' => 'admin',
|
||||
|
@ -179,6 +180,7 @@ Route::group([
|
|||
Route::get('/admin/config', [AdminController::class, 'showConfig'])->name('showConfig');
|
||||
Route::post('/admin/config', [AdminController::class, 'editConfig'])->name('editConfig');
|
||||
Route::get('/send-test-email', [AdminController::class, 'SendTestMail'])->name('SendTestMail');
|
||||
Route::get('/auth-as/{id}', [AdminController::class, 'authAsID'])->name('authAsID');
|
||||
Route::get('/theme-updater', function () {return view('studio/theme-updater', []);});
|
||||
Route::get('/update', function () {return view('update', []);});
|
||||
Route::get('/backup', function () {return view('backup', []);});
|
||||
|
|
Loading…
Reference in New Issue