Add page to view and delete user sessions
This commit is contained in:
parent
102c8428c8
commit
5c59fec1cf
|
@ -41,6 +41,8 @@ $route->addGroup(
|
||||||
$route->get('/certificates', 'SettingsController@ifsgCertificate');
|
$route->get('/certificates', 'SettingsController@ifsgCertificate');
|
||||||
$route->post('/certificates', 'SettingsController@saveIfsgCertificate');
|
$route->post('/certificates', 'SettingsController@saveIfsgCertificate');
|
||||||
$route->get('/oauth', 'SettingsController@oauth');
|
$route->get('/oauth', 'SettingsController@oauth');
|
||||||
|
$route->get('/sessions', 'SettingsController@sessions');
|
||||||
|
$route->post('/sessions', 'SettingsController@sessionsDelete');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,9 @@ msgstr "Bitte gib Dein geplantes Abreisedatum an, damit wir ein Gefühl für die
|
||||||
msgid "settings.profile.success"
|
msgid "settings.profile.success"
|
||||||
msgstr "Einstellungen gespeichert."
|
msgstr "Einstellungen gespeichert."
|
||||||
|
|
||||||
|
msgid "settings.sessions.delete_success"
|
||||||
|
msgstr "Sitzung erfolgreich gelöscht."
|
||||||
|
|
||||||
msgid "faq.delete.success"
|
msgid "faq.delete.success"
|
||||||
msgstr "FAQ Eintrag erfolgreich gelöscht."
|
msgstr "FAQ Eintrag erfolgreich gelöscht."
|
||||||
|
|
||||||
|
|
|
@ -1122,6 +1122,9 @@ msgstr "Senden"
|
||||||
msgid "Y-m-d H:i"
|
msgid "Y-m-d H:i"
|
||||||
msgstr "d.m.Y H:i"
|
msgstr "d.m.Y H:i"
|
||||||
|
|
||||||
|
msgid "Y-m-d H:i:s"
|
||||||
|
msgstr "d.m.Y H:i:s"
|
||||||
|
|
||||||
msgid "mark as read"
|
msgid "mark as read"
|
||||||
msgstr "als gelesen markieren"
|
msgstr "als gelesen markieren"
|
||||||
|
|
||||||
|
@ -1974,6 +1977,9 @@ msgstr "Vorschau"
|
||||||
msgid "form.delete"
|
msgid "form.delete"
|
||||||
msgstr "Löschen"
|
msgstr "Löschen"
|
||||||
|
|
||||||
|
msgid "form.delete_all"
|
||||||
|
msgstr "Alle löschen"
|
||||||
|
|
||||||
msgid "form.updated"
|
msgid "form.updated"
|
||||||
msgstr "Aktualisiert"
|
msgstr "Aktualisiert"
|
||||||
|
|
||||||
|
@ -2201,6 +2207,21 @@ msgstr "Passwort wiederholen"
|
||||||
msgid "settings.password.success"
|
msgid "settings.password.success"
|
||||||
msgstr "Passwort wurde erfolgreich geändert."
|
msgstr "Passwort wurde erfolgreich geändert."
|
||||||
|
|
||||||
|
msgid "settings.sessions"
|
||||||
|
msgstr "Sitzungen"
|
||||||
|
|
||||||
|
msgid "settings.sessions.info"
|
||||||
|
msgstr "Hier kannst Du deine Bowser-Sitzungen sehen und löschen."
|
||||||
|
|
||||||
|
msgid "settings.sessions.current"
|
||||||
|
msgstr "Aktuelle Sitzung"
|
||||||
|
|
||||||
|
msgid "settings.sessions.id"
|
||||||
|
msgstr "Sitzungs-ID"
|
||||||
|
|
||||||
|
msgid "settings.sessions.last_activity"
|
||||||
|
msgstr "Zuletzt verwendet"
|
||||||
|
|
||||||
msgid "settings.theme"
|
msgid "settings.theme"
|
||||||
msgstr "Theme"
|
msgstr "Theme"
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,9 @@ msgstr "Please enter your planned date of departure. "
|
||||||
msgid "settings.profile.success"
|
msgid "settings.profile.success"
|
||||||
msgstr "Settings saved."
|
msgstr "Settings saved."
|
||||||
|
|
||||||
|
msgid "settings.sessions.delete_success"
|
||||||
|
msgstr "Session deleted successfully."
|
||||||
|
|
||||||
msgid "faq.delete.success"
|
msgid "faq.delete.success"
|
||||||
msgstr "FAQ entry successfully deleted."
|
msgstr "FAQ entry successfully deleted."
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,9 @@ msgstr "Preview"
|
||||||
msgid "form.delete"
|
msgid "form.delete"
|
||||||
msgstr "Delete"
|
msgstr "Delete"
|
||||||
|
|
||||||
|
msgid "form.delete_all"
|
||||||
|
msgstr "Delete all"
|
||||||
|
|
||||||
msgid "form.updated"
|
msgid "form.updated"
|
||||||
msgstr "Updated"
|
msgstr "Updated"
|
||||||
|
|
||||||
|
@ -299,6 +302,21 @@ msgstr "Password confirmation"
|
||||||
msgid "settings.password.success"
|
msgid "settings.password.success"
|
||||||
msgstr "Password was changed successfully."
|
msgstr "Password was changed successfully."
|
||||||
|
|
||||||
|
msgid "settings.sessions"
|
||||||
|
msgstr "Sessions"
|
||||||
|
|
||||||
|
msgid "settings.sessions.info"
|
||||||
|
msgstr "Here you can see and delete your browser sessions."
|
||||||
|
|
||||||
|
msgid "settings.sessions.current"
|
||||||
|
msgstr "Current session"
|
||||||
|
|
||||||
|
msgid "settings.sessions.id"
|
||||||
|
msgstr "Session ID"
|
||||||
|
|
||||||
|
msgid "settings.sessions.last_activity"
|
||||||
|
msgstr "Last activity"
|
||||||
|
|
||||||
msgid "settings.theme"
|
msgid "settings.theme"
|
||||||
msgstr "Theme"
|
msgstr "Theme"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
{% extends 'pages/settings/settings.twig' %}
|
||||||
|
{% import 'macros/form.twig' as f %}
|
||||||
|
{% import 'macros/base.twig' as m %}
|
||||||
|
|
||||||
|
{% block title %}{{ __('settings.sessions') }}{% endblock %}
|
||||||
|
|
||||||
|
{% block row_content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
{{ m.info(__('settings.sessions.info')) }}
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ __('settings.sessions.id') }}</th>
|
||||||
|
<th>{{ __('settings.sessions.last_activity') }}</th>
|
||||||
|
<th>
|
||||||
|
{% if sessions|length > 1 %}
|
||||||
|
<form action="" enctype="multipart/form-data" method="post">
|
||||||
|
{{ csrf() }}
|
||||||
|
{{ f.hidden('id', 'all') }}
|
||||||
|
{{ f.submit(
|
||||||
|
__('form.delete_all'),
|
||||||
|
{'name': 'delete', 'btn_type': 'danger', 'size': 'sm', 'icon_left': 'trash'}
|
||||||
|
) }}
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{% for session in sessions %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<pre>{{ session['id'] }}</pre>
|
||||||
|
</td>
|
||||||
|
<td>{{ session.last_activity.format(__('Y-m-d H:i:s')) }}</td>
|
||||||
|
<td>
|
||||||
|
{% if session.id != current_session %}
|
||||||
|
<form action="" enctype="multipart/form-data" method="post">
|
||||||
|
{{ csrf() }}
|
||||||
|
{{ f.hidden('id', session.id) }}
|
||||||
|
{{ f.submit(
|
||||||
|
__('form.delete'),
|
||||||
|
{'name': 'delete', 'btn_type': 'danger', 'size': 'sm', 'icon_left': 'trash'}
|
||||||
|
) }}
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
{{ __('settings.sessions.current') }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -279,6 +279,38 @@ class SettingsController extends BaseController
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function sessions(): Response
|
||||||
|
{
|
||||||
|
$sessions = $this->auth->user()->sessions->sortByDesc('last_activity');
|
||||||
|
|
||||||
|
return $this->response->withView(
|
||||||
|
'pages/settings/sessions',
|
||||||
|
[
|
||||||
|
'settings_menu' => $this->settingsMenu(),
|
||||||
|
'sessions' => $sessions,
|
||||||
|
'current_session' => session()->getId(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sessionsDelete(Request $request): Response
|
||||||
|
{
|
||||||
|
$id = $request->postData('id');
|
||||||
|
$query = $this->auth->user()
|
||||||
|
->sessions()
|
||||||
|
->getQuery()
|
||||||
|
->where('id', '!=', session()->getId());
|
||||||
|
|
||||||
|
if ($id != 'all') {
|
||||||
|
$query = $query->where('id', $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$query->delete();
|
||||||
|
$this->addNotification('settings.sessions.delete_success');
|
||||||
|
|
||||||
|
return $this->redirect->to('/settings/sessions');
|
||||||
|
}
|
||||||
|
|
||||||
public function settingsMenu(): array
|
public function settingsMenu(): array
|
||||||
{
|
{
|
||||||
$menu = [
|
$menu = [
|
||||||
|
@ -298,6 +330,8 @@ class SettingsController extends BaseController
|
||||||
$menu[url('/settings/certificates')] = 'settings.certificates';
|
$menu[url('/settings/certificates')] = 'settings.certificates';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$menu[url('/settings/sessions')] = 'settings.sessions';
|
||||||
|
|
||||||
if (!empty(config('oauth'))) {
|
if (!empty(config('oauth'))) {
|
||||||
$menu[url('/settings/oauth')] = ['title' => 'settings.oauth', 'hidden' => $this->checkOauthHidden()];
|
$menu[url('/settings/oauth')] = ['title' => 'settings.oauth', 'hidden' => $this->checkOauthHidden()];
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use Engelsystem\Controllers\NotificationType;
|
||||||
use Engelsystem\Controllers\SettingsController;
|
use Engelsystem\Controllers\SettingsController;
|
||||||
use Engelsystem\Http\Exceptions\HttpNotFound;
|
use Engelsystem\Http\Exceptions\HttpNotFound;
|
||||||
use Engelsystem\Http\Response;
|
use Engelsystem\Http\Response;
|
||||||
|
use Engelsystem\Models\Session as SessionModel;
|
||||||
use Engelsystem\Models\User\License;
|
use Engelsystem\Models\User\License;
|
||||||
use Engelsystem\Models\User\Settings;
|
use Engelsystem\Models\User\Settings;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
@ -32,6 +33,10 @@ class SettingsControllerTest extends ControllerTest
|
||||||
|
|
||||||
protected SettingsController $controller;
|
protected SettingsController $controller;
|
||||||
|
|
||||||
|
protected SessionModel $currentSession;
|
||||||
|
protected SessionModel $secondSession;
|
||||||
|
protected SessionModel $otherSession;
|
||||||
|
|
||||||
protected function setUpProfileTest(): array
|
protected function setUpProfileTest(): array
|
||||||
{
|
{
|
||||||
$body = [
|
$body = [
|
||||||
|
@ -574,7 +579,95 @@ class SettingsControllerTest extends ControllerTest
|
||||||
$this->controller->oauth();
|
$this->controller->oauth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::sessions
|
||||||
|
*/
|
||||||
|
public function testSessions(): void
|
||||||
|
{
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
|
||||||
|
|
||||||
|
$this->response->expects($this->once())
|
||||||
|
->method('withView')
|
||||||
|
->willReturnCallback(function ($view, $data) {
|
||||||
|
$this->assertEquals('pages/settings/sessions', $view);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('sessions', $data);
|
||||||
|
$this->assertCount(3, $data['sessions']);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('current_session', $data);
|
||||||
|
$this->assertEquals($this->currentSession->id, $data['current_session']);
|
||||||
|
|
||||||
|
return $this->response;
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->controller->sessions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::sessionsDelete
|
||||||
|
*/
|
||||||
|
public function testSessionsDelete(): void
|
||||||
|
{
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
|
||||||
|
$this->setExpects($this->response, 'redirectTo', ['http://localhost/settings/sessions'], $this->response);
|
||||||
|
|
||||||
|
// Delete old user session
|
||||||
|
$this->request = $this->request->withParsedBody(['id' => $this->secondSession->id]);
|
||||||
|
$this->controller->sessionsDelete($this->request);
|
||||||
|
|
||||||
|
$this->assertHasNotification('settings.sessions.delete_success');
|
||||||
|
$this->assertCount(3, SessionModel::all());
|
||||||
|
$this->assertNull(SessionModel::find($this->secondSession->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::sessionsDelete
|
||||||
|
*/
|
||||||
|
public function testSessionsDeleteActiveSession(): void
|
||||||
|
{
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
|
||||||
|
$this->setExpects($this->response, 'redirectTo', null, $this->response);
|
||||||
|
|
||||||
|
// Delete active user session
|
||||||
|
$this->request = $this->request->withParsedBody(['id' => $this->currentSession->id]);
|
||||||
|
$this->controller->sessionsDelete($this->request);
|
||||||
|
|
||||||
|
$this->assertCount(4, SessionModel::all()); // None got deleted
|
||||||
|
$this->assertNotNull(SessionModel::find($this->currentSession->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::sessionsDelete
|
||||||
|
*/
|
||||||
|
public function testSessionsDeleteOtherUsersSession(): void
|
||||||
|
{
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
|
||||||
|
$this->setExpects($this->response, 'redirectTo', null, $this->response);
|
||||||
|
|
||||||
|
// Delete another users session
|
||||||
|
$this->request = $this->request->withParsedBody(['id' => $this->otherSession->id]);
|
||||||
|
$this->controller->sessionsDelete($this->request);
|
||||||
|
|
||||||
|
$this->assertCount(4, SessionModel::all()); // None got deleted
|
||||||
|
$this->assertNotNull(SessionModel::find($this->otherSession->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \Engelsystem\Controllers\SettingsController::sessionsDelete
|
||||||
|
*/
|
||||||
|
public function testSessionsDeleteAllSessions(): void
|
||||||
|
{
|
||||||
|
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
|
||||||
|
$this->setExpects($this->response, 'redirectTo', null, $this->response);
|
||||||
|
|
||||||
|
// Delete all other user sessions
|
||||||
|
$this->request = $this->request->withParsedBody(['id' => 'all']);
|
||||||
|
$this->controller->sessionsDelete($this->request);
|
||||||
|
|
||||||
|
$this->assertCount(2, SessionModel::all()); // Two got deleted
|
||||||
|
$this->assertNotNull(SessionModel::find($this->currentSession->id));
|
||||||
|
$this->assertNull(SessionModel::find($this->secondSession->id));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Engelsystem\Controllers\SettingsController::__construct
|
* @covers \Engelsystem\Controllers\SettingsController::__construct
|
||||||
|
@ -849,6 +942,13 @@ class SettingsControllerTest extends ControllerTest
|
||||||
->has(License::factory())
|
->has(License::factory())
|
||||||
->create();
|
->create();
|
||||||
|
|
||||||
|
// Create 4 sessions, 3 for the active user
|
||||||
|
$this->otherSession = SessionModel::factory()->create()->first(); // Other users sessions
|
||||||
|
$sessions = SessionModel::factory(3)->create(['user_id' => $this->user->id]);
|
||||||
|
$this->currentSession = $sessions->first();
|
||||||
|
$this->secondSession = $sessions->last();
|
||||||
|
$this->session->setId($this->currentSession->id);
|
||||||
|
|
||||||
$this->controller = $this->app->make(SettingsController::class);
|
$this->controller = $this->app->make(SettingsController::class);
|
||||||
$this->controller->setValidator(new Validator());
|
$this->controller->setValidator(new Validator());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue