Compare commits

...

3 Commits

Author SHA1 Message Date
5cc9134a4c Change we now store logged in user details in session, instead of cookies.
All checks were successful
Create Docker Image / Test Application (x86_64) (push) Successful in 31s
Create Docker Image / Build Docker Image (x86_64) (push) Successful in 1m25s
Create Docker Image / Build Docker Image (arm64) (push) Successful in 4m34s
Create Docker Image / Final Docker Image Manifest (push) Successful in 9s
This is so when the session expires, the logged in user details are expired as well, which wasnt happening with cookies.
2025-04-26 18:02:50 +10:00
b6dbaed606 Move our /api routes into /ajax under web.php. The /api routes werent authenticated and may not have been using the logged in users details 2025-04-26 18:02:50 +10:00
5f8eb2bb91 Move out view variables until after our session has been setup. This was needed so that auth()->user() could be resolved correctly and needed to be done after we have started the session and swapped in the users cookies 2025-04-26 18:02:50 +10:00
16 changed files with 106 additions and 125 deletions

View File

@ -8,9 +8,7 @@ use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use LdapRecord\LdapRecordException;
use LdapRecord\Models\Model;
use LdapRecord\Query\Collection as LDAPCollection;
@ -173,16 +171,6 @@ final class Server
} catch (LdapRecordException $e) {
switch ($e->getDetailedError()?->getErrorCode()) {
case 49:
// Since we failed authentication, we should delete our auth cookie
if (Cookie::has('password_encrypt')) {
Log::alert('Clearing user credentials and logging out');
Cookie::queue(Cookie::forget('password_encrypt'));
Cookie::queue(Cookie::forget('username_encrypt'));
Session::invalidate();
}
abort(401,$e->getDetailedError()->getErrorMessage());
default:
@ -196,8 +184,8 @@ final class Server
/**
* @note While we are caching our baseDNs, it seems if we have more than 1,
* our caching doesnt generate a hit on a subsequent call to this function (before the cache expires).
* IE: If we have 5 baseDNs, it takes 5 calls to this function to case them all.
* @todo Possibly a bug wtih ldaprecord, so need to investigate
* IE: If we have 5 baseDNs, it takes 5 calls to this function to cache them all.
* @todo Possibly a bug with ldaprecord, so need to investigate
*/
$result = collect();
foreach ($base->namingcontexts as $dn)

View File

@ -10,7 +10,7 @@ use Illuminate\Support\Collection;
use App\Classes\LDAP\Server;
class APIController extends Controller
class AjaxController extends Controller
{
/**
* Get the LDAP server BASE DNs

View File

@ -5,41 +5,43 @@ namespace App\Http\Controllers\Auth;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controller;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/';
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')
->except('logout');
}
protected function credentials(Request $request): array
{
@ -58,17 +60,14 @@ class LoginController extends Controller
*/
public function logout(Request $request)
{
// Delete our LDAP authentication cookies
Cookie::queue(Cookie::forget('username_encrypt'));
Cookie::queue(Cookie::forget('password_encrypt'));
$user = Auth::user();
$this->guard()->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
if ($response = $this->loggedOut($request)) {
Log::info(sprintf('Logged out [%s]',$user->dn));
return $response;
}
@ -100,4 +99,4 @@ class LoginController extends Controller
{
return login_attr_name();
}
}
}

View File

@ -17,7 +17,9 @@ class AllowAnonymous
*/
public function handle(Request $request,Closure $next): mixed
{
if (((! Cookie::has('username_encrypt')) || (! Cookie::has('password_encrypt'))) && (! config('pla.allow_guest',FALSE)))
if ((! config('pla.allow_guest',FALSE))
&& ($request->path() !== 'login')
&& ((! Cookie::has('username_encrypt')) || (! Cookie::has('password_encrypt'))))
return redirect()
->to('/login');

View File

@ -7,7 +7,6 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use App\Classes\LDAP\Server;
use App\Ldap\User;
/**
* This sets up our application session with any required values, ultimately for cache optimisation reasons
@ -25,9 +24,6 @@ class ApplicationSession
{
Config::set('server',new Server);
view()->share('server', Config::get('server'));
view()->share('user', auth()->user() ?: new User);
return $next($request);
}
}

View File

@ -5,8 +5,9 @@ namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use LdapRecord\Container;
use App\Ldap\Connection;
@ -28,21 +29,11 @@ class SwapinAuthUser
if (! array_key_exists($key,config('ldap.connections')))
abort(599,sprintf('LDAP default server [%s] configuration doesnt exist?',$key));
/*
// Rebuild our connection with the authenticated user.
if (Session::has('username_encrypt') && Session::has('password_encrypt')) {
Config::set('ldap.connections.'.$key.'.username',Crypt::decryptString(Session::get('username_encrypt')));
Config::set('ldap.connections.'.$key.'.password',Crypt::decryptString(Session::get('password_encrypt')));
} else
*/
// @todo it seems sometimes we have cookies that show the logged in user, but Auth::user() has expired?
if (Cookie::has('username_encrypt') && Cookie::has('password_encrypt')) {
Config::set('ldap.connections.'.$key.'.username',Cookie::get('username_encrypt'));
Config::set('ldap.connections.'.$key.'.password',Cookie::get('password_encrypt'));
Log::debug('Swapping out configured LDAP credentials with the user\'s cookie.',['key'=>$key,'user'=>Cookie::get('username_encrypt')]);
Log::debug('Swapping out configured LDAP credentials with the user\'s session.',['key'=>$key]);
}
// We need to override our Connection object so that we can store and retrieve the logged in user and swap out the credentials to use them.

View File

@ -0,0 +1,30 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use App\Ldap\User;
/**
* This sets up our application session with any required values, ultimately for cache optimisation reasons
*/
class ViewVariables
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @return mixed
*/
public function handle(Request $request,Closure $next): mixed
{
view()->share('server',Config::get('server'));
view()->share('user',auth()->user() ?: new User);
return $next($request);
}
}

View File

@ -2,26 +2,20 @@
namespace App\Ldap;
use Illuminate\Support\Facades\Cookie;
// use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Log;
use LdapRecord\Auth\Guard as GuardBase;
class Guard extends GuardBase
{
public function attempt(string $username, string $password, bool $stayBound = false): bool
{
if ($result = parent::attempt($username,$password,$stayBound)) {
/*
* We can either use our session or cookies to store this. If using session, then Http/Kernel needs to be
* updated to start a session for API calls.
// We need to store our password so that we can swap in the user in during SwapinAuthUser::class middleware
request()->session()->put('username_encrypt',Crypt::encryptString($username));
request()->session()->put('password_encrypt',Crypt::encryptString($password));
*/
Log::info(sprintf('Attempting login for [%s] with password [%s]',$username,($password ? str_repeat('*',16) : str_repeat('?',16))));
// For our API calls, we store the cookie - which our cookies are already encrypted
Cookie::queue('username_encrypt',$username);
Cookie::queue('password_encrypt',$password);
if ($result = parent::attempt($username,$password,$stayBound)) {
// Store user details so we can swap in auth details in SwapinAuthUser
session()->put('username_encrypt',Crypt::encryptString($username));
session()->put('password_encrypt',Crypt::encryptString($password));
}
return $result;

View File

@ -1,32 +1,27 @@
<?php
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use App\Http\Middleware\{AllowAnonymous,ApplicationSession,CheckUpdate,SwapinAuthUser};
use App\Http\Middleware\{AllowAnonymous,ApplicationSession,CheckUpdate,SwapinAuthUser,ViewVariables};
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('web', [
SwapinAuthUser::class,
ApplicationSession::class,
CheckUpdate::class,
]);
$middleware->prependToGroup('api', [
EncryptCookies::class,
SwapinAuthUser::class,
ApplicationSession::class,
AllowAnonymous::class,
]);
$middleware->appendToGroup(
group: 'web',
middleware: [
AllowAnonymous::class,
ApplicationSession::class,
SwapinAuthUser::class,
ViewVariables::class,
CheckUpdate::class,
]);
$middleware->trustProxies(at: [
'10.0.0.0/8',

View File

@ -1,5 +1,5 @@
<?php
return [
App\Providers\AppServiceProvider::class,
App\Providers\AppServiceProvider::class,
];

View File

@ -68,7 +68,7 @@ return [
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'level' => env('LOG_LEVEL', 'info'),
'days' => env('LOG_DAILY_DAYS', 14),
'replace_placeholders' => true,
],

4
public/js/custom.js vendored
View File

@ -59,7 +59,7 @@ $(document).ready(function() {
if (typeof basedn !== 'undefined') {
sources = basedn;
} else {
sources = { url: 'api/bases' };
sources = { url: 'ajax/bases' };
}
// Attach the fancytree widget to an existing <div id="tree"> element
@ -95,7 +95,7 @@ $(document).ready(function() {
source: sources,
lazyLoad: function(event,data) {
data.result = {
url: '/api/children',
url: '/ajax/children',
data: {key: data.node.data.item,depth: 1}
};

View File

@ -91,7 +91,7 @@
// Get a list of attributes already on the page, so we dont double up
$.ajax({
method: 'POST',
url: '{{ url('api/schema/objectclass/attrs') }}/'+item,
url: '{{ url('ajax/schema/objectclass/attrs') }}/'+item,
cache: false,
success: function(data) {
// Render any must attributes
@ -156,7 +156,7 @@
$.ajax({
method: 'POST',
url: '{{ url('api/schema/objectclass/attrs') }}/'+item,
url: '{{ url('ajax/schema/objectclass/attrs') }}/'+item,
cache: false,
success: function(data) {
var attrs = [];

View File

@ -58,7 +58,7 @@
return false;
$.ajax({
url: '{{ url('api/schema/view') }}',
url: '{{ url('ajax/schema/view') }}',
method: 'POST',
data: { type: type },
dataType: 'html',

View File

@ -1,23 +0,0 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\APIController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::controller(APIController::class)->group(function() {
Route::get('bases','bases');
Route::get('children','children');
Route::post('schema/view','schema_view');
Route::post('schema/objectclass/attrs/{id}','schema_objectclass_attrs');
});

View File

@ -2,7 +2,7 @@
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\{AjaxController,HomeController};
use App\Http\Controllers\Auth\LoginController;
use App\Http\Middleware\AllowAnonymous;
@ -57,4 +57,13 @@ Route::controller(HomeController::class)->group(function() {
Route::view('modal/export/{dn}','modals.entry-export');
Route::view('modal/userpassword-check/{dn}','modals.entry-userpassword-check');
});
});
});
Route::controller(AjaxController::class)
->prefix('ajax')
->group(function() {
Route::get('bases','bases');
Route::get('children','children');
Route::post('schema/view','schema_view');
Route::post('schema/objectclass/attrs/{id}','schema_objectclass_attrs');
});