Updated all login events to route through single service

This commit is contained in:
Dan Brown 2021-07-17 17:45:00 +01:00
parent 78f9c01519
commit 9249addb5c
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
11 changed files with 118 additions and 63 deletions

View File

@ -186,12 +186,8 @@ class ExternalBaseSessionGuard implements StatefulGuard
*/ */
public function loginUsingId($id, $remember = false) public function loginUsingId($id, $remember = false)
{ {
if (!is_null($user = $this->provider->retrieveById($id))) { // Always return false as to disable this method,
$this->login($user, $remember); // Logins should route through LoginService.
return $user;
}
return false; return false;
} }

View File

@ -0,0 +1,50 @@
<?php
namespace BookStack\Auth\Access;
use BookStack\Actions\ActivityType;
use BookStack\Auth\User;
use BookStack\Facades\Activity;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
class LoginService
{
/**
* Log the given user into the system.
*/
public function login(User $user, string $method): void
{
auth()->login($user);
Activity::add(ActivityType::AUTH_LOGIN, "{$method}; {$user->logDescriptor()}");
Theme::dispatch(ThemeEvents::AUTH_LOGIN, $method, $user);
// Authenticate on all session guards if a likely admin
if ($user->can('users-manage') && $user->can('user-roles-manage')) {
$guards = ['standard', 'ldap', 'saml2'];
foreach ($guards as $guard) {
auth($guard)->login($user);
}
}
}
/**
* Attempt the login of a user using the given credentials.
* Meant to mirror laravel's default guard 'attempt' method
* but in a manner that always routes through our login system.
*/
public function attempt(array $credentials, string $method, bool $remember = false): bool
{
$result = auth()->attempt($credentials, $remember);
if ($result) {
$user = auth()->user();
auth()->logout();
$this->login($user, $method);
}
return $result;
}
}

View File

@ -25,16 +25,16 @@ class Saml2Service extends ExternalAuthService
{ {
protected $config; protected $config;
protected $registrationService; protected $registrationService;
protected $user; protected $loginService;
/** /**
* Saml2Service constructor. * Saml2Service constructor.
*/ */
public function __construct(RegistrationService $registrationService, User $user) public function __construct(RegistrationService $registrationService, LoginService $loginService)
{ {
$this->config = config('saml2'); $this->config = config('saml2');
$this->registrationService = $registrationService; $this->registrationService = $registrationService;
$this->user = $user; $this->loginService = $loginService;
} }
/** /**
@ -332,7 +332,7 @@ class Saml2Service extends ExternalAuthService
*/ */
protected function getOrRegisterUser(array $userDetails): ?User protected function getOrRegisterUser(array $userDetails): ?User
{ {
$user = $this->user->newQuery() $user = User::query()
->where('external_auth_id', '=', $userDetails['external_id']) ->where('external_auth_id', '=', $userDetails['external_id'])
->first(); ->first();
@ -389,10 +389,7 @@ class Saml2Service extends ExternalAuthService
$this->syncWithGroups($user, $groups); $this->syncWithGroups($user, $groups);
} }
auth()->login($user); $this->loginService->login($user, 'saml2');
Activity::add(ActivityType::AUTH_LOGIN, "saml2; {$user->logDescriptor()}");
Theme::dispatch(ThemeEvents::AUTH_LOGIN, 'saml2', $user);
return $user; return $user;
} }
} }

View File

@ -2,15 +2,11 @@
namespace BookStack\Auth\Access; namespace BookStack\Auth\Access;
use BookStack\Actions\ActivityType;
use BookStack\Auth\SocialAccount; use BookStack\Auth\SocialAccount;
use BookStack\Auth\User; use BookStack\Auth\User;
use BookStack\Exceptions\SocialDriverNotConfigured; use BookStack\Exceptions\SocialDriverNotConfigured;
use BookStack\Exceptions\SocialSignInAccountNotUsed; use BookStack\Exceptions\SocialSignInAccountNotUsed;
use BookStack\Exceptions\UserRegistrationException; use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Activity;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\Factory as Socialite; use Laravel\Socialite\Contracts\Factory as Socialite;
@ -28,6 +24,11 @@ class SocialAuthService
*/ */
protected $socialite; protected $socialite;
/**
* @var LoginService
*/
protected $loginService;
/** /**
* The default built-in social drivers we support. * The default built-in social drivers we support.
* *
@ -59,9 +60,10 @@ class SocialAuthService
/** /**
* SocialAuthService constructor. * SocialAuthService constructor.
*/ */
public function __construct(Socialite $socialite) public function __construct(Socialite $socialite, LoginService $loginService)
{ {
$this->socialite = $socialite; $this->socialite = $socialite;
$this->loginService = $loginService;
} }
/** /**
@ -139,10 +141,7 @@ class SocialAuthService
// When a user is not logged in and a matching SocialAccount exists, // When a user is not logged in and a matching SocialAccount exists,
// Simply log the user into the application. // Simply log the user into the application.
if (!$isLoggedIn && $socialAccount !== null) { if (!$isLoggedIn && $socialAccount !== null) {
auth()->login($socialAccount->user); $this->loginService->login($socialAccount->user, $socialAccount);
Activity::add(ActivityType::AUTH_LOGIN, $socialAccount);
Theme::dispatch(ThemeEvents::AUTH_LOGIN, $socialDriver, $socialAccount->user);
return redirect()->intended('/'); return redirect()->intended('/');
} }

View File

@ -4,6 +4,7 @@ namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType; use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\EmailConfirmationService; use BookStack\Auth\Access\EmailConfirmationService;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\UserRepo; use BookStack\Auth\UserRepo;
use BookStack\Exceptions\ConfirmationEmailException; use BookStack\Exceptions\ConfirmationEmailException;
use BookStack\Exceptions\UserTokenExpiredException; use BookStack\Exceptions\UserTokenExpiredException;
@ -20,14 +21,20 @@ use Illuminate\View\View;
class ConfirmEmailController extends Controller class ConfirmEmailController extends Controller
{ {
protected $emailConfirmationService; protected $emailConfirmationService;
protected $loginService;
protected $userRepo; protected $userRepo;
/** /**
* Create a new controller instance. * Create a new controller instance.
*/ */
public function __construct(EmailConfirmationService $emailConfirmationService, UserRepo $userRepo) public function __construct(
EmailConfirmationService $emailConfirmationService,
LoginService $loginService,
UserRepo $userRepo
)
{ {
$this->emailConfirmationService = $emailConfirmationService; $this->emailConfirmationService = $emailConfirmationService;
$this->loginService = $loginService;
$this->userRepo = $userRepo; $this->userRepo = $userRepo;
} }
@ -87,9 +94,7 @@ class ConfirmEmailController extends Controller
$user->email_confirmed = true; $user->email_confirmed = true;
$user->save(); $user->save();
auth()->login($user); $this->loginService->login($user, auth()->getDefaultDriver());
Theme::dispatch(ThemeEvents::AUTH_LOGIN, auth()->getDefaultDriver(), $user);
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
$this->showSuccessNotification(trans('auth.email_confirm_success')); $this->showSuccessNotification(trans('auth.email_confirm_success'));
$this->emailConfirmationService->deleteByUser($user); $this->emailConfirmationService->deleteByUser($user);

View File

@ -3,13 +3,11 @@
namespace BookStack\Http\Controllers\Auth; namespace BookStack\Http\Controllers\Auth;
use Activity; use Activity;
use BookStack\Actions\ActivityType; use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\SocialAuthService; use BookStack\Auth\Access\SocialAuthService;
use BookStack\Exceptions\LoginAttemptEmailNeededException; use BookStack\Exceptions\LoginAttemptEmailNeededException;
use BookStack\Exceptions\LoginAttemptException; use BookStack\Exceptions\LoginAttemptException;
use BookStack\Facades\Theme;
use BookStack\Http\Controllers\Controller; use BookStack\Http\Controllers\Controller;
use BookStack\Theming\ThemeEvents;
use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
@ -37,16 +35,19 @@ class LoginController extends Controller
protected $redirectAfterLogout = '/login'; protected $redirectAfterLogout = '/login';
protected $socialAuthService; protected $socialAuthService;
protected $loginService;
/** /**
* Create a new controller instance. * Create a new controller instance.
*/ */
public function __construct(SocialAuthService $socialAuthService) public function __construct(SocialAuthService $socialAuthService, LoginService $loginService)
{ {
$this->middleware('guest', ['only' => ['getLogin', 'login']]); $this->middleware('guest', ['only' => ['getLogin', 'login']]);
$this->middleware('guard:standard,ldap', ['only' => ['login', 'logout']]); $this->middleware('guard:standard,ldap', ['only' => ['login', 'logout']]);
$this->socialAuthService = $socialAuthService; $this->socialAuthService = $socialAuthService;
$this->loginService = $loginService;
$this->redirectPath = url('/'); $this->redirectPath = url('/');
$this->redirectAfterLogout = url('/login'); $this->redirectAfterLogout = url('/login');
} }
@ -140,6 +141,19 @@ class LoginController extends Controller
return $this->sendFailedLoginResponse($request); return $this->sendFailedLoginResponse($request);
} }
/**
* Attempt to log the user into the application.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function attemptLogin(Request $request)
{
return $this->loginService->attempt(
$this->credentials($request), auth()->getDefaultDriver(), $request->filled('remember')
);
}
/** /**
* The user has been authenticated. * The user has been authenticated.
* *
@ -150,17 +164,6 @@ class LoginController extends Controller
*/ */
protected function authenticated(Request $request, $user) protected function authenticated(Request $request, $user)
{ {
// Authenticate on all session guards if a likely admin
if ($user->can('users-manage') && $user->can('user-roles-manage')) {
$guards = ['standard', 'ldap', 'saml2'];
foreach ($guards as $guard) {
auth($guard)->login($user);
}
}
Theme::dispatch(ThemeEvents::AUTH_LOGIN, auth()->getDefaultDriver(), $user);
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
return redirect()->intended($this->redirectPath()); return redirect()->intended($this->redirectPath());
} }

View File

@ -2,14 +2,12 @@
namespace BookStack\Http\Controllers\Auth; namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType; use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\RegistrationService; use BookStack\Auth\Access\RegistrationService;
use BookStack\Auth\Access\SocialAuthService; use BookStack\Auth\Access\SocialAuthService;
use BookStack\Auth\User; use BookStack\Auth\User;
use BookStack\Exceptions\UserRegistrationException; use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Theme;
use BookStack\Http\Controllers\Controller; use BookStack\Http\Controllers\Controller;
use BookStack\Theming\ThemeEvents;
use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
@ -32,6 +30,7 @@ class RegisterController extends Controller
protected $socialAuthService; protected $socialAuthService;
protected $registrationService; protected $registrationService;
protected $loginService;
/** /**
* Where to redirect users after login / registration. * Where to redirect users after login / registration.
@ -44,13 +43,18 @@ class RegisterController extends Controller
/** /**
* Create a new controller instance. * Create a new controller instance.
*/ */
public function __construct(SocialAuthService $socialAuthService, RegistrationService $registrationService) public function __construct(
SocialAuthService $socialAuthService,
RegistrationService $registrationService,
LoginService $loginService
)
{ {
$this->middleware('guest'); $this->middleware('guest');
$this->middleware('guard:standard'); $this->middleware('guard:standard');
$this->socialAuthService = $socialAuthService; $this->socialAuthService = $socialAuthService;
$this->registrationService = $registrationService; $this->registrationService = $registrationService;
$this->loginService = $loginService;
$this->redirectTo = url('/'); $this->redirectTo = url('/');
$this->redirectPath = url('/'); $this->redirectPath = url('/');
@ -98,9 +102,7 @@ class RegisterController extends Controller
try { try {
$user = $this->registrationService->registerUser($userData); $user = $this->registrationService->registerUser($userData);
auth()->login($user); $this->loginService->login($user, auth()->getDefaultDriver());
Theme::dispatch(ThemeEvents::AUTH_LOGIN, auth()->getDefaultDriver(), $user);
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
} catch (UserRegistrationException $exception) { } catch (UserRegistrationException $exception) {
if ($exception->getMessage()) { if ($exception->getMessage()) {
$this->showErrorNotification($exception->getMessage()); $this->showErrorNotification($exception->getMessage());

View File

@ -2,16 +2,14 @@
namespace BookStack\Http\Controllers\Auth; namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType; use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\RegistrationService; use BookStack\Auth\Access\RegistrationService;
use BookStack\Auth\Access\SocialAuthService; use BookStack\Auth\Access\SocialAuthService;
use BookStack\Exceptions\SocialDriverNotConfigured; use BookStack\Exceptions\SocialDriverNotConfigured;
use BookStack\Exceptions\SocialSignInAccountNotUsed; use BookStack\Exceptions\SocialSignInAccountNotUsed;
use BookStack\Exceptions\SocialSignInException; use BookStack\Exceptions\SocialSignInException;
use BookStack\Exceptions\UserRegistrationException; use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Theme;
use BookStack\Http\Controllers\Controller; use BookStack\Http\Controllers\Controller;
use BookStack\Theming\ThemeEvents;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Laravel\Socialite\Contracts\User as SocialUser; use Laravel\Socialite\Contracts\User as SocialUser;
@ -20,15 +18,21 @@ class SocialController extends Controller
{ {
protected $socialAuthService; protected $socialAuthService;
protected $registrationService; protected $registrationService;
protected $loginService;
/** /**
* SocialController constructor. * SocialController constructor.
*/ */
public function __construct(SocialAuthService $socialAuthService, RegistrationService $registrationService) public function __construct(
SocialAuthService $socialAuthService,
RegistrationService $registrationService,
LoginService $loginService
)
{ {
$this->middleware('guest')->only(['getRegister', 'postRegister']); $this->middleware('guest')->only(['getRegister', 'postRegister']);
$this->socialAuthService = $socialAuthService; $this->socialAuthService = $socialAuthService;
$this->registrationService = $registrationService; $this->registrationService = $registrationService;
$this->loginService = $loginService;
} }
/** /**
@ -136,12 +140,9 @@ class SocialController extends Controller
} }
$user = $this->registrationService->registerUser($userData, $socialAccount, $emailVerified); $user = $this->registrationService->registerUser($userData, $socialAccount, $emailVerified);
auth()->login($user); $this->loginService->login($user, $socialDriver);
Theme::dispatch(ThemeEvents::AUTH_LOGIN, $socialDriver, $user);
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
$this->showSuccessNotification(trans('auth.register_success')); $this->showSuccessNotification(trans('auth.register_success'));
return redirect('/'); return redirect('/');
} }
} }

View File

@ -3,6 +3,7 @@
namespace BookStack\Http\Controllers\Auth; namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType; use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\UserInviteService; use BookStack\Auth\Access\UserInviteService;
use BookStack\Auth\UserRepo; use BookStack\Auth\UserRepo;
use BookStack\Exceptions\UserTokenExpiredException; use BookStack\Exceptions\UserTokenExpiredException;
@ -18,17 +19,19 @@ use Illuminate\Routing\Redirector;
class UserInviteController extends Controller class UserInviteController extends Controller
{ {
protected $inviteService; protected $inviteService;
protected $loginService;
protected $userRepo; protected $userRepo;
/** /**
* Create a new controller instance. * Create a new controller instance.
*/ */
public function __construct(UserInviteService $inviteService, UserRepo $userRepo) public function __construct(UserInviteService $inviteService, LoginService $loginService, UserRepo $userRepo)
{ {
$this->middleware('guest'); $this->middleware('guest');
$this->middleware('guard:standard'); $this->middleware('guard:standard');
$this->inviteService = $inviteService; $this->inviteService = $inviteService;
$this->loginService = $loginService;
$this->userRepo = $userRepo; $this->userRepo = $userRepo;
} }
@ -72,9 +75,7 @@ class UserInviteController extends Controller
$user->email_confirmed = true; $user->email_confirmed = true;
$user->save(); $user->save();
auth()->login($user); $this->loginService->login($user, auth()->getDefaultDriver());
Theme::dispatch(ThemeEvents::AUTH_LOGIN, auth()->getDefaultDriver(), $user);
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
$this->showSuccessNotification(trans('auth.user_invite_success', ['appName' => setting('app-name')])); $this->showSuccessNotification(trans('auth.user_invite_success', ['appName' => setting('app-name')]));
$this->inviteService->deleteByUser($user); $this->inviteService->deleteByUser($user);

View File

@ -31,7 +31,7 @@ class EnforceMfaRequirements
&& !$request->is('mfa/verify*', 'uploads/images/user/*') && !$request->is('mfa/verify*', 'uploads/images/user/*')
&& $this->mfaSession->requiredForCurrentUser() && $this->mfaSession->requiredForCurrentUser()
) { ) {
return redirect('/mfa/verify'); // return redirect('/mfa/verify');
} }
// TODO - URI wildcard exceptions above allow access to the 404 page of this user // TODO - URI wildcard exceptions above allow access to the 404 page of this user

View File

@ -3,6 +3,7 @@
namespace BookStack\Providers; namespace BookStack\Providers;
use Blade; use Blade;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\SocialAuthService; use BookStack\Auth\Access\SocialAuthService;
use BookStack\Entities\BreadcrumbsViewComposer; use BookStack\Entities\BreadcrumbsViewComposer;
use BookStack\Entities\Models\Book; use BookStack\Entities\Models\Book;
@ -68,7 +69,7 @@ class AppServiceProvider extends ServiceProvider
}); });
$this->app->singleton(SocialAuthService::class, function ($app) { $this->app->singleton(SocialAuthService::class, function ($app) {
return new SocialAuthService($app->make(SocialiteFactory::class)); return new SocialAuthService($app->make(SocialiteFactory::class), $app->make(LoginService::class));
}); });
} }
} }