-
Notifications
You must be signed in to change notification settings - Fork 6
#863 Impersonation #866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
#863 Impersonation #866
Changes from all commits
9195f62
75dcc41
6e39399
bd20657
2ba8002
6e5571a
7ce78ec
66d1c7b
44aa20b
d9bc82a
1a51287
99cee5c
ec3d260
6796661
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?php | ||
|
|
||
| namespace App\Http\Controllers; | ||
|
|
||
| use App\Audit; | ||
| use App\Enums\SessionVariables; | ||
| use App\User; | ||
| use Illuminate\Http\Request; | ||
|
|
||
| class ImpersonationController extends Controller { | ||
| public function start(Request $request) { | ||
| $user = User::find($request->user_id); | ||
| $is_impersonating = session()->has(SessionVariables::IMPERSONATE->value); | ||
| if (is_null($user)) { | ||
| return redirect()->back()->with(SessionVariables::ERROR->value, 'That user does not exist'); | ||
| } | ||
|
|
||
| if ($is_impersonating) { | ||
| return redirect()->back()->with(SessionVariables::ERROR->value, 'You must first stop impersonating your current user before beginning a new session'); | ||
| } | ||
|
|
||
| session()->put(SessionVariables::IMPERSONATE->value, $user->id); | ||
| Audit::newAudit('started impersonating user ' . $user->impersonation_name . '.'); | ||
|
|
||
| return redirect('/dashboard')->with(SessionVariables::ERROR->value, 'Successfully started impersonationg ' . $user->full_name . '. CAUTION: Impersonating actively logs you into the user\'s REAL account. Changes made while impersonating will be reflected on the user\'s actual account. PROCEED WITH CARE.'); | ||
| } | ||
|
|
||
| public function stop() { | ||
| Audit::newAudit('impersonation session ending...'); | ||
|
|
||
| session()->forget(SessionVariables::IMPERSONATE->value); | ||
| return redirect('/dashboard'); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| <?php | ||
|
|
||
| namespace App\Http\Middleware; | ||
|
|
||
| use App\Enums\FeatureToggles; | ||
| use App\Enums\SessionVariables; | ||
| use Auth; | ||
| use Closure; | ||
| use Illuminate\Http\Request; | ||
| use Symfony\Component\HttpFoundation\Response; | ||
|
|
||
| class Impersonation { | ||
| /** | ||
| * Handle an incoming request. | ||
| * | ||
| * @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next | ||
| */ | ||
| public function handle(Request $request, Closure $next): Response { | ||
| if (toggleEnabled(FeatureToggles::IMPERSONATION) && session()->has(SessionVariables::IMPERSONATE->value) && Auth::user()->hasRole('wm') || Auth::user()->hasRole('awm')) { | ||
| session()->put(SessionVariables::IMPERSONATING_USER->value, Auth::id()); | ||
| Auth::onceUsingId(session(SessionVariables::IMPERSONATE->value)); | ||
| } | ||
|
|
||
| return $next($request); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?php | ||
|
|
||
| namespace App\View\Composers; | ||
|
|
||
| use App\Enums\FeatureToggles; | ||
| use App\Enums\SessionVariables; | ||
| use App\User; | ||
| use Auth; | ||
| use Illuminate\View\View; | ||
|
|
||
| class ImpersonationComposer { | ||
| /** | ||
| * Create a new profile composer. | ||
| */ | ||
| public function __construct( | ||
| ) { | ||
| } | ||
|
|
||
| /** | ||
| * Bind data to the view. | ||
| */ | ||
| public function compose(View $view): void { | ||
| if (toggleEnabled(FeatureToggles::IMPERSONATION)) { | ||
| $users = null; | ||
| $is_impersonating = session()->has(SessionVariables::IMPERSONATE->value); | ||
|
|
||
| if (Auth::user()->hasRole('wm') || Auth::user()->hasRole('awm')) { | ||
| $users = User::where('status', 1)->orderBy('lname', 'ASC')->get()->pluck('impersonation_name', 'id'); | ||
| } | ||
|
|
||
| $view->with('users', $users)->with('is_impersonating', $is_impersonating); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| <?php | ||
|
|
||
| use Illuminate\Database\Migrations\Migration; | ||
| use Illuminate\Database\Schema\Blueprint; | ||
| use Illuminate\Support\Facades\Schema; | ||
|
|
||
| return new class extends Migration { | ||
| /** | ||
| * Run the migrations. | ||
| */ | ||
| public function up(): void { | ||
| Schema::table('audits', function (Blueprint $table) { | ||
| $table->integer('impersonated_by_id')->nullable(); | ||
|
|
||
| $table->foreign('impersonated_by_id')->references('id')->on('roster')->nullOnDelete(); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Reverse the migrations. | ||
| */ | ||
| public function down(): void { | ||
| Schema::table('audits', function (Blueprint $table) { | ||
| $table->dropColumn('impersonated_by_id'); | ||
| }); | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,25 +1,29 @@ | ||
| <nav class="navbar navbar-expand-lg navbar-light"> | ||
| <div class="container-fluid"> | ||
| <nav class="navbar navbar-expand-lg navbar-light"> | ||
| <div class="container-fluid"> | ||
| <a class="navbar-brand" href="/dashboard"> | ||
| @include('inc.logo', ['color' => 'black']) | ||
| </a> | ||
| <div class="d-flex justify-content-start ms-5 collapse navbar-collapse"> | ||
|
|
||
| {{ html()->form()->route('searchAirport')->class(['row','row-cols-lg-auto'])->open() }} | ||
| <div class="col-12 input-group"> | ||
| {{ html()->text('apt', null)->placeholder('Search Airport ICAO')->class(['form-control']) }} | ||
| | ||
| <button class="btn btn-success" type="submit">Search</button> | ||
| </div> | ||
| <ul class="navbar-nav me-auto align-items-center"> | ||
| {{ html()->form()->route('searchAirport')->class(['form-inline'])->open() }} | ||
| <div class="col-12 input-group"> | ||
| {{ html()->text('apt', null)->placeholder('Search Airport ICAO')->class(['form-control']) }} | ||
| | ||
| <button class="btn btn-success" type="submit">Search</button> | ||
| </div> | ||
| {{ html()->form()->close() }} | ||
| </ul> | ||
| <ul class="navbar-nav ml-auto align-items-center"> | ||
| <a class="nav-link {{ Nav::isRoute('controller_dash_home') }}" href="/dashboard">Dashboard Home</a> | ||
| @if(toggleEnabled($FeatureToggles::IMPERSONATION) && $is_impersonating) | ||
| <a class="nav-link" href="/dashboard/admin/impersonation/stop">End Impersonation</a> | ||
| @endif | ||
| @if(toggleEnabled($FeatureToggles::IMPERSONATION) && (Auth::user()->hasRole('wm') || Auth::user()->hasRole('awm'))) | ||
| {{ html()->form()->route('startImpersonation')->class(['form-inline'])->open() }} | ||
| {{ html()->select('user_id', $users, Auth::id())->class(['form-select'])->attributes(['onchange' => 'this.form.submit()'])->disabled($is_impersonating) }} | ||
| {{ html()->form()->close() }} | ||
| </div> | ||
|
|
||
| <ul class="navbar-nav ms-auto"> | ||
| <a class="nav-link {{ Nav::isRoute('controller_dash_home') }}" href="/dashboard">Dashboard Home</a> | ||
| <li class="nav-item dropdown"> | ||
| <a class="nav-link" style="pointer-events:none">{{ Auth::user()->full_name }} - {{ Auth::user()->rating_short }}</a> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| </div> | ||
| </nav> | ||
| @else | ||
| <a class="nav-link disabled">{{ Auth::user()->full_name }} - {{ Auth::user()->rating_short }}</a> | ||
| @endif | ||
| </ul> | ||
| </div> | ||
| </nav> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -333,6 +333,11 @@ | |
| Route::prefix('monitor')->middleware('permission:staff')->group(function () { | ||
| Route::get('/', 'AdminDash@backgroundMonitor'); | ||
| }); | ||
|
|
||
| Route::prefix('impersonation')->middleware('toggle:impersonation')->group(function () { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not guard the entire
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stop can't be guarded because the middleware has to run on the impersonated user. If we don't allow stop to be hit by all users, we would never be able to end impersonation on non-privileged users. |
||
| Route::post('/', 'ImpersonationController@start')->middleware('role:wm|awm')->name('startImpersonation'); | ||
| Route::get('/stop', 'ImpersonationController@stop')->name('stopImpersonation'); | ||
| }); | ||
| }); | ||
| }); | ||
| /* | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.