return [
// Method of authentication to use
- // Options: standard, ldap
+ // Options: standard, ldap, saml2
'method' => env('AUTH_METHOD', 'standard'),
// Authentication Defaults
// This option controls the default authentication "guard" and password
// reset options for your application.
'defaults' => [
- 'guard' => env('AUTH_METHOD', 'standard') === 'standard' ? 'web' : env('AUTH_METHOD'),
+ 'guard' => env('AUTH_METHOD', 'standard'),
'passwords' => 'users',
],
// mechanisms used by this application to persist your user's data.
// Supported drivers: "session", "api-token", "ldap-session"
'guards' => [
- 'web' => [
+ 'standard' => [
'driver' => 'session',
'provider' => 'users',
],
public function __construct()
{
$this->middleware('guest');
+ $this->middleware('guard:standard');
parent::__construct();
}
*/
public function __construct(SocialAuthService $socialAuthService)
{
- $this->middleware('guest', ['only' => ['getLogin', 'postLogin']]);
+ $this->middleware('guest', ['only' => ['getLogin', 'login']]);
+ $this->middleware('guard:standard,ldap', ['only' => ['login', 'logout']]);
+
$this->socialAuthService = $socialAuthService;
$this->redirectPath = url('/');
$this->redirectAfterLogout = url('/login');
return redirect('/login');
}
- /**
- * Log the user out of the application.
- */
- public function logout(Request $request)
- {
- $this->guard()->logout();
- $request->session()->invalidate();
-
- return $this->loggedOut($request) ?: redirect('/');
- }
}
*/
public function __construct(SocialAuthService $socialAuthService, RegistrationService $registrationService)
{
- $this->middleware('guest')->only(['getRegister', 'postRegister']);
+ $this->middleware('guest');
+ $this->middleware('guard:standard');
$this->socialAuthService = $socialAuthService;
$this->registrationService = $registrationService;
public function __construct()
{
$this->middleware('guest');
+ $this->middleware('guard:standard');
parent::__construct();
}
{
parent::__construct();
$this->samlService = $samlService;
-
- // SAML2 access middleware
- $this->middleware(function ($request, $next) {
-
- if (config('auth.method') !== 'saml2') {
- $this->showPermissionError();
- }
-
- return $next($request);
- });
+ $this->middleware('guard:saml2');
}
/**
use BookStack\Exceptions\UserTokenNotFoundException;
use BookStack\Http\Controllers\Controller;
use Exception;
-use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
-use Illuminate\View\View;
class UserInviteController extends Controller
{
/**
* Create a new controller instance.
- *
- * @param UserInviteService $inviteService
- * @param UserRepo $userRepo
*/
public function __construct(UserInviteService $inviteService, UserRepo $userRepo)
{
+ $this->middleware('guest');
+ $this->middleware('guard:standard');
+
$this->inviteService = $inviteService;
$this->userRepo = $userRepo;
- $this->middleware('guest');
+
parent::__construct();
}
/**
* Show the page for the user to set the password for their account.
- * @param string $token
- * @return Factory|View|RedirectResponse
* @throws Exception
*/
public function showSetPassword(string $token)
/**
* Sets the password for an invited user and then grants them access.
- * @param Request $request
- * @param string $token
- * @return RedirectResponse|Redirector
* @throws Exception
*/
public function setPassword(Request $request, string $token)
/**
* Check and validate the exception thrown when checking an invite token.
- * @param Exception $exception
* @return RedirectResponse|Redirector
* @throws Exception
*/
'auth' => \BookStack\Http\Middleware\Authenticate::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \BookStack\Http\Middleware\RedirectIfAuthenticated::class,
- 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
- 'perm' => \BookStack\Http\Middleware\PermissionMiddleware::class
+ 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
+ 'perm' => \BookStack\Http\Middleware\PermissionMiddleware::class,
+ 'guard' => \BookStack\Http\Middleware\CheckGuard::class,
];
}
--- /dev/null
+<?php
+
+namespace BookStack\Http\Middleware;
+
+use Closure;
+
+class CheckGuard
+{
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @param string $allowedGuards
+ * @return mixed
+ */
+ public function handle($request, Closure $next, ...$allowedGuards)
+ {
+ $activeGuard = config('auth.method');
+ if (!in_array($activeGuard, $allowedGuards)) {
+ session()->flash('error', trans('errors.permission'));
+ return redirect('/');
+ }
+
+ return $next($request);
+ }
+}
{
if (!$request->user() || !$request->user()->can($permission)) {
- Session::flash('error', trans('errors.permission'));
+ session()->flash('error', trans('errors.permission'));
return redirect()->back();
}
'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
'users_send_invite_option' => 'Send user invite email',
'users_external_auth_id' => 'External Authentication ID',
- 'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+ 'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
'users_password_warning' => 'Only fill the below if you would like to change your password.',
'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
'users_delete' => 'Delete User',
// Social auth routes
Route::get('/login/service/{socialDriver}', 'Auth\SocialController@getSocialLogin');
Route::get('/login/service/{socialDriver}/callback', 'Auth\SocialController@socialCallback');
-Route::get('/login/service/{socialDriver}/detach', 'Auth\SocialController@detachSocialAccount');
+Route::group(['middleware' => 'auth'], function () {
+ Route::get('/login/service/{socialDriver}/detach', 'Auth\SocialController@detachSocialAccount');
+});
Route::get('/register/service/{socialDriver}', 'Auth\SocialController@socialRegister');
// Login/Logout routes
$resp = $this->get($this->endpoint);
$resp->assertStatus(401);
- $this->actingAs($viewer, 'web');
+ $this->actingAs($viewer, 'standard');
$resp = $this->get($this->endpoint);
$resp->assertStatus(200);
public function test_api_access_permission_required_to_access_api_with_session_auth()
{
$editor = $this->getEditor();
- $this->actingAs($editor, 'web');
+ $this->actingAs($editor, 'standard');
$resp = $this->get($this->endpoint);
$resp->assertStatus(200);
- auth('web')->logout();
+ auth('standard')->logout();
$accessApiPermission = RolePermission::getByName('access-api');
$editorRole = $this->getEditor()->roles()->first();
$editor = User::query()->where('id', '=', $editor->id)->first();
- $this->actingAs($editor, 'web');
+ $this->actingAs($editor, 'standard');
$resp = $this->get($this->endpoint);
$resp->assertStatus(403);
$resp->assertJson($this->errorResponse("The owner of the used API token does not have permission to make API calls", 403));
{
$this->checkLdapReceivesCorrectDetails('ldap.bookstack.com', 'ldap.bookstack.com', 389);
}
+
+ public function test_forgot_password_routes_inaccessible()
+ {
+ $resp = $this->get('/password/email');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/password/email');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->get('/password/reset/abc123');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/password/reset');
+ $this->assertPermissionError($resp);
+ }
+
+ public function test_user_invite_routes_inaccessible()
+ {
+ $resp = $this->get('/register/invite/abc123');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/register/invite/abc123');
+ $this->assertPermissionError($resp);
+ }
+
+ public function test_user_register_routes_inaccessible()
+ {
+ $resp = $this->get('/register');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/register');
+ $this->assertPermissionError($resp);
+ }
}
$getRoutes = ['/logout', '/metadata', '/sls'];
foreach ($getRoutes as $route) {
$req = $this->get('/saml2' . $route);
- $req->assertRedirect('/');
- $error = session()->get('error');
- $this->assertStringStartsWith('You do not have permission to access', $error);
- session()->flush();
+ $this->assertPermissionError($req);
}
$postRoutes = ['/login', '/acs'];
foreach ($postRoutes as $route) {
$req = $this->post('/saml2' . $route);
- $req->assertRedirect('/');
- $error = session()->get('error');
- $this->assertStringStartsWith('You do not have permission to access', $error);
- session()->flush();
+ $this->assertPermissionError($req);
}
}
+ public function test_forgot_password_routes_inaccessible()
+ {
+ $resp = $this->get('/password/email');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/password/email');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->get('/password/reset/abc123');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/password/reset');
+ $this->assertPermissionError($resp);
+ }
+
+ public function test_standard_login_routes_inaccessible()
+ {
+ $resp = $this->post('/login');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->get('/logout');
+ $this->assertPermissionError($resp);
+ }
+
+ public function test_user_invite_routes_inaccessible()
+ {
+ $resp = $this->get('/register/invite/abc123');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/register/invite/abc123');
+ $this->assertPermissionError($resp);
+ }
+
+ public function test_user_register_routes_inaccessible()
+ {
+ $resp = $this->get('/register');
+ $this->assertPermissionError($resp);
+
+ $resp = $this->post('/register');
+ $this->assertPermissionError($resp);
+ }
+
protected function withGet(array $options, callable $callback)
{
return $this->withGlobal($_GET, $options, $callback);
self::assertThat($passed, self::isTrue(), "Failed asserting that given map:\n\n{$toCheckStr}\n\nincludes:\n\n{$toIncludeStr}");
}
+ /**
+ * Assert a permission error has occurred.
+ */
+ protected function assertPermissionError($response)
+ {
+ if ($response instanceof BrowserKitTest) {
+ $response = \Illuminate\Foundation\Testing\TestResponse::fromBaseResponse($response->response);
+ }
+
+ $response->assertRedirect('/');
+ $this->assertSessionHas('error');
+ $error = session()->pull('error');
+ $this->assertStringStartsWith('You do not have permission to access', $error);
+ }
+
}
\ No newline at end of file
*/
protected $baseUrl = 'http://localhost';
- /**
- * Assert a permission error has occurred.
- * @param TestResponse $response
- * @return TestCase
- */
- protected function assertPermissionError(TestResponse $response)
- {
- $response->assertRedirect('/');
- $this->assertSessionHas('error');
- session()->remove('error');
- return $this;
- }
-
/**
* Assert the session contains a specific entry.
* @param string $key