From 3f62c844061af07435d032a6010f1a052e8b3ed0 Mon Sep 17 00:00:00 2001 From: Julian Prieber <60265788+JulianPrieber@users.noreply.github.com> Date: Thu, 13 Jul 2023 18:22:50 +0200 Subject: [PATCH] Added option for admins to impersonate other users Inspired by: https://github.com/nextcloud/impersonate --- app/Http/Controllers/AdminController.php | 50 ++++++ app/Http/Kernel.php | 1 + app/Http/Middleware/Impersonate.php | 155 ++++++++++++++++++ .../2014_10_12_000000_create_users_table.php | 1 + resources/lang/en/messages.php | 1 + .../views/components/finishing.blade.php | 8 + resources/views/panel/users.blade.php | 11 ++ routes/web.php | 6 +- 8 files changed, 231 insertions(+), 2 deletions(-) create mode 100644 app/Http/Middleware/Impersonate.php diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 0629c48..eb67cb6 100755 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -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'); + } + + } + } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 8269e39..14565be 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -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, ]; } diff --git a/app/Http/Middleware/Impersonate.php b/app/Http/Middleware/Impersonate.php new file mode 100644 index 0000000..9c7601a --- /dev/null +++ b/app/Http/Middleware/Impersonate.php @@ -0,0 +1,155 @@ +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 ""; + } + + 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 = 'avatar'; +} elseif (file_exists(base_path("assets/linkstack/images/").findFile('avatar'))) { + $img = 'avatar'; +} else { + $img = 'avatar'; +} +$dashboard = url('dashboard'); +$URL = url('/auth-as'); +$csrf = csrf_token(); +$remember_token = User::find($originalUser); +$token = $remember_token->remember_token; +$customHtml = +<< + .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 */ + } + + +
+

+ + $img $name + + + + + + +

+
+ + + + + +EOD;; +} else {$customHtml = "";} + + $response = $next($request); + $content = $response->getContent(); + $modifiedContent = preg_replace('/]*)>/', "{$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); + } + } +} diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php index aa5117f..ad253fb 100755 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -26,6 +26,7 @@ class CreateUsersTable extends Migration $table->rememberToken(); $table->timestamps(); $table->string('theme')->nullable(); + $table->unsignedBigInteger('auth_as')->nullable(); }); } diff --git a/resources/lang/en/messages.php b/resources/lang/en/messages.php index 0b1eca5..112ea69 100644 --- a/resources/lang/en/messages.php +++ b/resources/lang/en/messages.php @@ -521,6 +521,7 @@ return [ # Tooltips 'tt.Delete' => 'Delete', + 'tt.Impersonate' => 'Impersonate', 'tt.Edit' => 'Edit', 'tt.All links' => 'All links', diff --git a/resources/views/components/finishing.blade.php b/resources/views/components/finishing.blade.php index 269e84b..19024ae 100644 --- a/resources/views/components/finishing.blade.php +++ b/resources/views/components/finishing.blade.php @@ -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', diff --git a/resources/views/panel/users.blade.php b/resources/views/panel/users.blade.php index 37f196d..ca140bb 100755 --- a/resources/views/panel/users.blade.php +++ b/resources/views/panel/users.blade.php @@ -1,3 +1,5 @@ + + @extends('layouts.sidebar') @section('content') @@ -117,6 +119,15 @@ + @php $adminUser = User::whereNotNull('auth_as')->where('role', 'admin')->first(); @endphp + + + + + + + + diff --git a/routes/web.php b/routes/web.php index 2cef788..bcbc132 100755 --- a/routes/web.php +++ b/routes/web.php @@ -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', []);});