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)
{
if (!is_null($user = $this->provider->retrieveById($id))) {
$this->login($user, $remember);
return $user;
}
// Always return false as to disable this method,
// Logins should route through LoginService.
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 $registrationService;
protected $user;
protected $loginService;
/**
* Saml2Service constructor.
*/
public function __construct(RegistrationService $registrationService, User $user)
public function __construct(RegistrationService $registrationService, LoginService $loginService)
{
$this->config = config('saml2');
$this->registrationService = $registrationService;
$this->user = $user;
$this->loginService = $loginService;
}
/**
@ -332,7 +332,7 @@ class Saml2Service extends ExternalAuthService
*/
protected function getOrRegisterUser(array $userDetails): ?User
{
$user = $this->user->newQuery()
$user = User::query()
->where('external_auth_id', '=', $userDetails['external_id'])
->first();
@ -389,10 +389,7 @@ class Saml2Service extends ExternalAuthService
$this->syncWithGroups($user, $groups);
}
auth()->login($user);
Activity::add(ActivityType::AUTH_LOGIN, "saml2; {$user->logDescriptor()}");
Theme::dispatch(ThemeEvents::AUTH_LOGIN, 'saml2', $user);
$this->loginService->login($user, 'saml2');
return $user;
}
}

View File

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

View File

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

View File

@ -3,13 +3,11 @@
namespace BookStack\Http\Controllers\Auth;
use Activity;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Exceptions\LoginAttemptEmailNeededException;
use BookStack\Exceptions\LoginAttemptException;
use BookStack\Facades\Theme;
use BookStack\Http\Controllers\Controller;
use BookStack\Theming\ThemeEvents;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
@ -37,16 +35,19 @@ class LoginController extends Controller
protected $redirectAfterLogout = '/login';
protected $socialAuthService;
protected $loginService;
/**
* 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('guard:standard,ldap', ['only' => ['login', 'logout']]);
$this->socialAuthService = $socialAuthService;
$this->loginService = $loginService;
$this->redirectPath = url('/');
$this->redirectAfterLogout = url('/login');
}
@ -140,6 +141,19 @@ class LoginController extends Controller
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.
*
@ -150,17 +164,6 @@ class LoginController extends Controller
*/
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());
}

View File

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

View File

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

View File

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

View File

@ -31,7 +31,7 @@ class EnforceMfaRequirements
&& !$request->is('mfa/verify*', 'uploads/images/user/*')
&& $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

View File

@ -3,6 +3,7 @@
namespace BookStack\Providers;
use Blade;
use BookStack\Auth\Access\LoginService;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Entities\BreadcrumbsViewComposer;
use BookStack\Entities\Models\Book;
@ -68,7 +69,7 @@ class AppServiceProvider extends ServiceProvider
});
$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));
});
}
}