Added login/register theme events

This commit is contained in:
Dan Brown 2021-03-19 21:54:50 +00:00
parent 2ae89f2c32
commit 691db40a33
12 changed files with 95 additions and 16 deletions

View file

@ -5,14 +5,12 @@ namespace BookStack\Auth\Access\Guards;
use BookStack\Auth\Access\LdapService;
use BookStack\Auth\Access\RegistrationService;
use BookStack\Auth\User;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\LdapException;
use BookStack\Exceptions\LoginAttemptException;
use BookStack\Exceptions\LoginAttemptEmailNeededException;
use BookStack\Exceptions\UserRegistrationException;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Session\Session;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
class LdapSessionGuard extends ExternalBaseSessionGuard

View file

@ -6,6 +6,8 @@ use BookStack\Auth\User;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Activity;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use Exception;
class RegistrationService
@ -71,6 +73,7 @@ class RegistrationService
}
Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
Theme::dispatch(ThemeEvents::AUTH_REGISTER, $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver(), $newUser);
// Start email confirmation flow if required
if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {

View file

@ -6,6 +6,8 @@ use BookStack\Exceptions\JsonDebugException;
use BookStack\Exceptions\SamlException;
use BookStack\Exceptions\UserRegistrationException;
use BookStack\Facades\Activity;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use Exception;
use Illuminate\Support\Str;
use OneLogin\Saml2\Auth;
@ -375,6 +377,7 @@ class Saml2Service extends ExternalAuthService
auth()->login($user);
Activity::add(ActivityType::AUTH_LOGIN, "saml2; {$user->logDescriptor()}");
Theme::dispatch(ThemeEvents::AUTH_LOGIN, 'saml2', $user);
return $user;
}
}

View file

@ -7,6 +7,8 @@ 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;
@ -58,7 +60,7 @@ class SocialAuthService
{
// Check social account has not already been used
if (SocialAccount::query()->where('driver_id', '=', $socialUser->getId())->exists()) {
throw new UserRegistrationException(trans('errors.social_account_in_use', ['socialAccount'=>$socialDriver]), '/login');
throw new UserRegistrationException(trans('errors.social_account_in_use', ['socialAccount' => $socialDriver]), '/login');
}
if (User::query()->where('email', '=', $socialUser->getEmail())->exists()) {
@ -98,6 +100,7 @@ class SocialAuthService
if (!$isLoggedIn && $socialAccount !== null) {
auth()->login($socialAccount->user);
Activity::add(ActivityType::AUTH_LOGIN, $socialAccount);
Theme::dispatch(ThemeEvents::AUTH_LOGIN, $socialDriver, $socialAccount->user);
return redirect()->intended('/');
}
@ -127,7 +130,7 @@ class SocialAuthService
if (setting('registration-enabled') && config('auth.method') !== 'ldap' && config('auth.method') !== 'saml2') {
$message .= trans('errors.social_account_register_instructions', ['socialAccount' => $titleCaseDriver]);
}
throw new SocialSignInAccountNotUsed($message, '/login');
}
@ -207,9 +210,9 @@ class SocialAuthService
public function newSocialAccount(string $socialDriver, SocialUser $socialUser): SocialAccount
{
return new SocialAccount([
'driver' => $socialDriver,
'driver' => $socialDriver,
'driver_id' => $socialUser->getId(),
'avatar' => $socialUser->getAvatar()
'avatar' => $socialUser->getAvatar()
]);
}

View file

@ -2,12 +2,15 @@
namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\EmailConfirmationService;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\ConfirmationEmailException;
use BookStack\Exceptions\UserTokenExpiredException;
use BookStack\Exceptions\UserTokenNotFoundException;
use BookStack\Facades\Theme;
use BookStack\Http\Controllers\Controller;
use BookStack\Theming\ThemeEvents;
use Exception;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@ -80,6 +83,8 @@ class ConfirmEmailController extends Controller
$user->save();
auth()->login($user);
Theme::dispatch(ThemeEvents::AUTH_LOGIN, auth()->getDefaultDriver(), $user);
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
$this->showSuccessNotification(trans('auth.email_confirm_success'));
$this->emailConfirmationService->deleteByUser($user);

View file

@ -7,7 +7,9 @@ use BookStack\Actions\ActivityType;
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;
@ -150,6 +152,7 @@ class LoginController extends Controller
}
}
Theme::dispatch(ThemeEvents::AUTH_LOGIN, auth()->getDefaultDriver(), $user);
$this->logActivity(ActivityType::AUTH_LOGIN, $user);
return redirect()->intended($this->redirectPath());
}

View file

@ -2,11 +2,14 @@
namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType;
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;
@ -93,6 +96,8 @@ 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);
} catch (UserRegistrationException $exception) {
if ($exception->getMessage()) {
$this->showErrorNotification($exception->getMessage());

View file

@ -2,13 +2,16 @@
namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType;
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;
@ -127,6 +130,8 @@ 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->showSuccessNotification(trans('auth.register_success'));
return redirect('/');

View file

@ -2,11 +2,14 @@
namespace BookStack\Http\Controllers\Auth;
use BookStack\Actions\ActivityType;
use BookStack\Auth\Access\UserInviteService;
use BookStack\Auth\UserRepo;
use BookStack\Exceptions\UserTokenExpiredException;
use BookStack\Exceptions\UserTokenNotFoundException;
use BookStack\Facades\Theme;
use BookStack\Http\Controllers\Controller;
use BookStack\Theming\ThemeEvents;
use Exception;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@ -68,6 +71,8 @@ class UserInviteController extends Controller
$user->save();
auth()->login($user);
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->inviteService->deleteByUser($user);

View file

@ -41,6 +41,26 @@ class ThemeEvents
*/
const WEB_MIDDLEWARE_AFTER = 'web_middleware_after';
/**
* Auth login event.
* Runs right after a user is logged-in to the application by any authentication
* system as a standard app user. This includes a user becoming logged in
* after registration. This is not emitted upon API usage.
* @param string $authSystem
* @param \BookStack\Auth\User $user
*/
const AUTH_LOGIN = 'auth_login';
/**
* Auth register event.
* Runs right after a user is newly registered to the application by any authentication
* system as a standard app user. This includes auto-registration systems used
* by LDAP, SAML and social systems. It only includes self-registrations.
* @param string $authSystem
* @param \BookStack\Auth\User $user
*/
const AUTH_REGISTER = 'auth_register';
/**
* Commonmark environment configure.
* Provides the commonmark library environment for customization

View file

@ -17,8 +17,7 @@ class SocialAuthTest extends TestCase
$this->setSettings(['registration-enabled' => 'true']);
config(['GOOGLE_APP_ID' => 'abc123', 'GOOGLE_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']);
$mockSocialite = Mockery::mock(Factory::class);
$this->app[Factory::class] = $mockSocialite;
$mockSocialite = $this->mock(Factory::class);
$mockSocialDriver = Mockery::mock(Provider::class);
$mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
@ -46,8 +45,7 @@ class SocialAuthTest extends TestCase
'APP_URL' => 'http://localhost'
]);
$mockSocialite = Mockery::mock(Factory::class);
$this->app[Factory::class] = $mockSocialite;
$mockSocialite = $this->mock(Factory::class);
$mockSocialDriver = Mockery::mock(Provider::class);
$mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
@ -93,8 +91,7 @@ class SocialAuthTest extends TestCase
]);
$user = factory(User::class)->make();
$mockSocialite = Mockery::mock(Factory::class);
$this->app[Factory::class] = $mockSocialite;
$mockSocialite = $this->mock(Factory::class);
$mockSocialDriver = Mockery::mock(Provider::class);
$mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
@ -132,8 +129,7 @@ class SocialAuthTest extends TestCase
]);
$user = factory(User::class)->make();
$mockSocialite = Mockery::mock(Factory::class);
$this->app[Factory::class] = $mockSocialite;
$mockSocialite = $this->mock(Factory::class);
$mockSocialDriver = Mockery::mock(Provider::class);
$mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);
@ -169,8 +165,7 @@ class SocialAuthTest extends TestCase
$this->setSettings(['registration-enabled' => 'true']);
config(['GITHUB_APP_ID' => 'abc123', 'GITHUB_APP_SECRET' => '123abc', 'APP_URL' => 'http://localhost']);
$mockSocialite = Mockery::mock(Factory::class);
$this->app[Factory::class] = $mockSocialite;
$mockSocialite = $this->mock(Factory::class);
$mockSocialDriver = Mockery::mock(Provider::class);
$mockSocialUser = Mockery::mock(\Laravel\Socialite\Contracts\User::class);

View file

@ -1,5 +1,7 @@
<?php namespace Tests;
use BookStack\Auth\Access\SocialAuthService;
use BookStack\Auth\User;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Tools\PageContent;
use BookStack\Facades\Theme;
@ -122,6 +124,38 @@ class ThemeTest extends TestCase
$resp->assertStatus(443);
}
public function test_event_auth_login_standard()
{
$args = [];
$callback = function (...$eventArgs) use (&$args) {
$args = $eventArgs;
};
Theme::listen(ThemeEvents::AUTH_LOGIN, $callback);
$this->post('/login', ['email' => 'admin@admin.com', 'password' => 'password']);
$this->assertCount(2, $args);
$this->assertEquals('standard', $args[0]);
$this->assertInstanceOf(User::class, $args[1]);
}
public function test_event_auth_register_standard()
{
$args = [];
$callback = function (...$eventArgs) use (&$args) {
$args = $eventArgs;
};
Theme::listen(ThemeEvents::AUTH_REGISTER, $callback);
$this->setSettings(['registration-enabled' => 'true']);
$user = factory(User::class)->make();
$this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']);
$this->assertCount(2, $args);
$this->assertEquals('standard', $args[0]);
$this->assertInstanceOf(User::class, $args[1]);
}
public function test_add_social_driver()
{
Theme::addSocialDriver('catnet', [