Skip to content

Commit

Permalink
Add login_required option so access is only after login in. (#2461)
Browse files Browse the repository at this point in the history
  • Loading branch information
ildyria authored Jun 10, 2024
1 parent 56c2f6d commit 4619506
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 31 deletions.
1 change: 1 addition & 0 deletions app/Http/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,5 +104,6 @@ class Kernel extends HttpKernel
'accept_content_type' => \App\Http\Middleware\AcceptContentType::class,
'redirect-legacy-id' => \App\Http\Middleware\RedirectLegacyPhotoID::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'login_required' => \App\Http\Middleware\LoginRequired::class,
];
}
58 changes: 58 additions & 0 deletions app/Http/Middleware/LoginRequired.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App\Http\Middleware;

use App\Assets\Features;
use App\Exceptions\ConfigurationException;
use App\Exceptions\ConfigurationKeyMissingException;
use App\Exceptions\Internal\FrameworkException;
use App\Models\Configs;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

/**
* Class LoginRequired.
*
* This middleware is ensures that only logged in users can access Lychee.
*/
class LoginRequired
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param \Closure $next
*
* @return mixed
*
* @throws ConfigurationException
* @throws FrameworkException
*/
public function handle(Request $request, \Closure $next): mixed
{
$dir_url = config('app.dir_url');
if (Features::inactive('livewire') && !Str::startsWith($request->getRequestUri(), $dir_url . '/livewire/')) {
return $next($request);
}

try {
if (!Configs::getValueAsBool('login_required')) {
// Login is not required. Proceed.
return $next($request);
}

if (Auth::user() !== null) {
// We are logged in. Proceed.
return $next($request);
}

return redirect()->route('login');
} catch (ConfigurationKeyMissingException $e) {
Log::warning(__METHOD__ . ':' . __LINE__ . ' ' . $e->getMessage());

return $next($request);
}
}
}
52 changes: 52 additions & 0 deletions app/Livewire/Components/Pages/Login.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace App\Livewire\Components\Pages;

use App\Models\Configs;
use Illuminate\Support\Facades\Auth;
use Illuminate\View\View;
use Laragear\WebAuthn\Models\WebAuthnCredential;
use Livewire\Attributes\Locked;
use Livewire\Attributes\On;
use Livewire\Component;

/**
* Login page.
*/
class Login extends Component
{
#[Locked] public string $title;
#[Locked] public bool $can_use_2fa;
/**
* @return void
*/
public function mount(): void
{
if (!Configs::getValueAsBool('login_required') || Auth::user() !== null) {
redirect(route('livewire-gallery'));
}
$this->title = Configs::getValueAsString('site_title');
$this->can_use_2fa = !Auth::check() && (WebAuthnCredential::query()->whereNull('disabled_at')->count() > 0);
}

/**
* Rendering of the front-end.
*
* @return View
*/
public function render(): View
{
return view('livewire.pages.login');
}

public function getIsLoginLeftProperty(): bool
{
return Configs::getValueAsString('login_button_position') === 'left';
}

#[On('reloadPage')]
public function reloadPage(): void
{
redirect(route('livewire-gallery'));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

use App\Models\Extensions\BaseConfigMigration;

return new class() extends BaseConfigMigration {
public const GALLERY = 'Gallery';
public const BOOL = '0|1';

public function getConfigs(): array
{
return [
[
'key' => 'login_required',
'value' => '0',
'is_secret' => false,
'cat' => self::GALLERY,
'type_range' => self::BOOL,
'description' => 'Require user to login to access gallery.',
],
];
}
};
31 changes: 31 additions & 0 deletions resources/views/livewire/pages/login.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<div class="w-full">
<!-- toolbar -->
<x-header.bar>
<x-backButtonHeader class="{{ $this->isLoginLeft ? 'order-4' : 'order-0' }}" />
<!-- NOT LOGGED -->
<x-header.button x-on:click="loginModalOpen = true" icon="account-login" class="{{ $this->isLoginLeft ? 'order-0' : 'order-4' }}" />
<x-header.title>{{ $title }}</x-header.title>
</x-header.bar>
<!-- albums -->
<div class="overflow-x-clip overflow-y-auto h-[calc(100vh-56px)] flex flex-col">
<div class="h-full flex flex-col justify-center">
<div class="w-full text-center"><x-icons.iconic icon="eye" /></div>
<p class="w-full text-center text-text-main-400">{{ __('lychee.VIEW_NO_PUBLIC_ALBUMS') }}</p>
</div>
<x-footer />
</div>
<div class="basicModalContainer transition-opacity duration-1000 ease-in animate-fadeIn
bg-black/80 z-50 fixed flex items-center justify-center w-full h-full top-0 left-0 box-border opacity-100"
data-closable="true">
<div class="basicModal transition-opacity ease-in duration-1000
opacity-100 bg-gradient-to-b from-bg-300 to-bg-400
relative w-[500px] text-sm rounded-md text-text-main-400 animate-moveUp
"
role="dialog">
<livewire:modals.login />
</div>
</div>
@if($this->can_use_2fa)
<x-webauthn.login />
@endif
</div>
12 changes: 6 additions & 6 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@
/**
* ALBUMS.
*/
Route::post('/Albums::get', [AlbumsController::class, 'get']);
Route::post('/Albums::getPositionData', [AlbumsController::class, 'getPositionData']);
Route::post('/Albums::tree', [AlbumsController::class, 'tree']);
Route::post('/Albums::get', [AlbumsController::class, 'get'])->middleware(['login_required']);
Route::post('/Albums::getPositionData', [AlbumsController::class, 'getPositionData'])->middleware(['login_required']);
Route::post('/Albums::tree', [AlbumsController::class, 'tree'])->middleware(['login_required']);

/**
* ALBUM.
*/
Route::post('/Album::get', [AlbumController::class, 'get']);
Route::post('/Album::get', [AlbumController::class, 'get'])->middleware(['login_required']);
Route::post('/Album::getPositionData', [AlbumController::class, 'getPositionData']);
Route::post('/Album::unlock', [AlbumController::class, 'unlock']);
Route::post('/Album::add', [AlbumController::class, 'add']);
Expand Down Expand Up @@ -68,8 +68,8 @@
/**
* PHOTO.
*/
Route::post('/Photo::get', [PhotoController::class, 'get']);
Route::post('/Photo::getRandom', [PhotoController::class, 'getRandom']);
Route::post('/Photo::get', [PhotoController::class, 'get'])->middleware(['login_required']);
Route::post('/Photo::getRandom', [PhotoController::class, 'getRandom'])->middleware(['login_required']);
Route::post('/Photo::setTitle', [PhotoController::class, 'setTitle']);
Route::post('/Photo::setDescription', [PhotoController::class, 'setDescription']);
Route::post('/Photo::setStar', [PhotoController::class, 'setStar']);
Expand Down
55 changes: 30 additions & 25 deletions routes/web-livewire.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,44 @@
| contains the "web" middleware group. Now create something great!
|
*/

Route::prefix(Features::when('livewire', '', 'livewire'))
->group(function () {
Route::get('/diagnostics', Diagnostics::class)->name('diagnostics');
});

Route::prefix(Features::when('livewire', '', 'livewire'))
->group(function () {
// Oauth routes.
Route::get('/auth/{provider}/redirect', [Oauth::class, 'redirected'])->whereIn('provider', OauthProvidersType::values());
Route::get('/auth/{provider}/authenticate', [Oauth::class, 'authenticate'])->name('oauth-authenticate')->whereIn('provider', OauthProvidersType::values());
Route::get('/auth/{provider}/register', [Oauth::class, 'register'])->name('oauth-register')->whereIn('provider', OauthProvidersType::values());

Route::get('/all-settings', AllSettings::class)->name('all-settings');
Route::get('/settings', Settings::class)->name('settings');
Route::get('/profile', Profile::class)->name('profile');
Route::get('/users', Users::class)->name('users');
Route::get('/sharing', Sharing::class)->name('sharing');
Route::get('/jobs', Jobs::class)->name('jobs');
Route::get('/maintenance', Maintenance::class)->name('maintenance');
Route::get('/map/{albumId?}', Map::class)->name('livewire-map');
Route::get('/frame/{albumId?}', Frame::class)->name('livewire-frame');
Route::get('/search/{albumId?}', Search::class)->name('livewire-search');
Route::get('/gallery/{albumId}/', Album::class)->name('livewire-gallery-album');
Route::get('/gallery/{albumId}/{photoId}', Album::class)->name('livewire-gallery-photo');
});
->group(function () {
// Oauth routes.
Route::get('/auth/{provider}/redirect', [Oauth::class, 'redirected'])->whereIn('provider', OauthProvidersType::values());
Route::get('/auth/{provider}/authenticate', [Oauth::class, 'authenticate'])->name('oauth-authenticate')->whereIn('provider', OauthProvidersType::values());
Route::get('/auth/{provider}/register', [Oauth::class, 'register'])->name('oauth-register')->whereIn('provider', OauthProvidersType::values());

Route::get('/login', Login::class)->name('login');
Route::get('/all-settings', AllSettings::class)->name('all-settings');
Route::get('/settings', Settings::class)->name('settings');
Route::get('/profile', Profile::class)->name('profile');
Route::get('/users', Users::class)->name('users');
Route::get('/sharing', Sharing::class)->name('sharing');
Route::get('/jobs', Jobs::class)->name('jobs');
Route::get('/maintenance', Maintenance::class)->name('maintenance');

Route::middleware(['login_required'])
->group(function () {
Route::get('/map/{albumId?}', Map::class)->name('livewire-map');
Route::get('/frame/{albumId?}', Frame::class)->name('livewire-frame');
Route::get('/search/{albumId?}', Search::class)->name('livewire-search');
Route::get('/gallery/{albumId}/', Album::class)->name('livewire-gallery-album');
Route::get('/gallery/{albumId}/{photoId}', Album::class)->name('livewire-gallery-photo');
});
});

Route::middleware(['installation:complete', 'migration:complete'])
->group(function () {
Route::prefix(Features::when('livewire', '', 'livewire'))
->group(function () {
Route::get('/landing', Landing::class)->name('landing');
Route::get('/gallery', Albums::class)->name('livewire-gallery');
Route::get('/', [RedirectController::class, 'view'])->name('livewire-index');
});
->group(function () {
Route::get('/landing', Landing::class)->name('landing');
Route::get('/gallery', Albums::class)->middleware(['login_required'])->name('livewire-gallery');
Route::get('/', [RedirectController::class, 'view'])->name('livewire-index');
});
});

85 changes: 85 additions & 0 deletions tests/Livewire/Pages/LoginTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

/**
* We don't care for unhandled exceptions in tests.
* It is the nature of a test to throw an exception.
* Without this suppression we had 100+ Linter warning in this file which
* don't help anything.
*
* @noinspection PhpDocMissingThrowsInspection
* @noinspection PhpUnhandledExceptionInspection
*/

namespace Tests\Livewire\Pages;

use App\Livewire\Components\Pages\Login;
use App\Models\Configs;
use Illuminate\Support\Facades\Auth;
use Livewire\Livewire;
use Tests\Livewire\Base\BaseLivewireTest;

class LoginTest extends BaseLivewireTest
{
/**
* Test that when we are not logged in,
* and login is required, we are properly redirected.
*
* @return void
*/
public function testLoggedOutRedirect(): void
{
Configs::set('login_required', '1');

$response = $this->get(route('livewire-gallery'));
$this->assertRedirect($response);
$response->assertRedirect(route('login'));

Configs::set('login_required', '0');
}

/**
* Test that when we are not logged in,
* and login is NOT required, we are not redirected.
*
* @return void
*/
public function testLoggedOutNoRedirect(): void
{
Configs::set('login_required', '0');

$response = $this->get(route('livewire-gallery'));
$this->assertOk($response);

Configs::set('login_required', '0');
}

/**
* Test that when we are logged in,
* and login is required, we are not redirected.
*
* @return void
*/
public function testLoggedInNoRedirect(): void
{
Auth::login($this->admin);

Configs::set('login_required', '1');

$response = $this->get(route('livewire-gallery'));
$this->assertOk($response);

Configs::set('login_required', '0');
}

/**
* Test that when we are logged in,
* and we are properly redirected when accessing login page.
*
* @return void
*/
public function testLoginRedirect(): void
{
Livewire::actingAs($this->admin)->test(Login::class)
->assertRedirect(route('livewire-gallery'));
}
}

0 comments on commit 4619506

Please sign in to comment.