From 239f953e36c8c89131ae452285c5711e31390469 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sat, 27 Jan 2018 21:27:53 +0100 Subject: [PATCH 01/14] Add an "always authenticate" setting --- app/Foundation/Providers/RouteServiceProvider.php | 6 ++++++ config/security.php | 11 +++++++++++ resources/lang/en/forms.php | 6 ++++-- .../views/dashboard/settings/security.blade.php | 12 ++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/app/Foundation/Providers/RouteServiceProvider.php b/app/Foundation/Providers/RouteServiceProvider.php index cca41e32..e1a65a28 100644 --- a/app/Foundation/Providers/RouteServiceProvider.php +++ b/app/Foundation/Providers/RouteServiceProvider.php @@ -13,7 +13,9 @@ namespace CachetHQ\Cachet\Foundation\Providers; use Barryvdh\Cors\HandleCors; use CachetHQ\Cachet\Http\Middleware\Acceptable; +use CachetHQ\Cachet\Http\Middleware\Authenticate; use CachetHQ\Cachet\Http\Middleware\Timezone; +use CachetHQ\Cachet\Http\Routes\AuthRoutes; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -127,6 +129,10 @@ class RouteServiceProvider extends ServiceProvider SubstituteBindings::class, ]; + if ($this->app['config']->get('setting.always_authenticate', false) && !$routes instanceof AuthRoutes) { + $middleware[] = Authenticate::class; + } + $router->group(['middleware' => $middleware], function (Router $router) use ($routes) { $routes->map($router); }); diff --git a/config/security.php b/config/security.php index 724338af..247584c7 100644 --- a/config/security.php +++ b/config/security.php @@ -22,4 +22,15 @@ return [ */ 'evil' => ['(? true, + ]; diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php index cb1ed400..7570e8c9 100644 --- a/resources/lang/en/forms.php +++ b/resources/lang/en/forms.php @@ -177,8 +177,10 @@ return [ 'incident-date-format' => 'Incident timestamp format', ], 'security' => [ - 'allowed-domains' => 'Allowed domains', - 'allowed-domains-help' => 'Comma separated. The domain set above is automatically allowed by default.', + 'allowed-domains' => 'Allowed domains', + 'allowed-domains-help' => 'Comma separated. The domain set above is automatically allowed by default.', + 'always-authenticate' => 'Always authenticate', + 'always-authenticate-help' => 'Require login to view any Cachet page', ], 'stylesheet' => [ 'custom-css' => 'Custom Stylesheet', diff --git a/resources/views/dashboard/settings/security.blade.php b/resources/views/dashboard/settings/security.blade.php index 7d832b2b..c0cb81c1 100644 --- a/resources/views/dashboard/settings/security.blade.php +++ b/resources/views/dashboard/settings/security.blade.php @@ -15,6 +15,18 @@ @include('dashboard.partials.errors')
+
+
+ +
+ +
+
+
From 9107035db8fa56a73d0728b91e8b7506fe7c915e Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sat, 27 Jan 2018 21:40:11 +0100 Subject: [PATCH 02/14] Hide the back to home button from the login when always_auth is enabled --- resources/views/auth/login.blade.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 7c17920f..a5f709bf 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -36,11 +36,13 @@
+ @if(!config('setting.always_authenticate', false)) + @endif
From b866ffea4e9750308322fd994f077c5f9567a663 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sat, 27 Jan 2018 22:22:31 +0100 Subject: [PATCH 03/14] Also whitelist the setup routes when enforcing auth --- .../Providers/RouteServiceProvider.php | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/app/Foundation/Providers/RouteServiceProvider.php b/app/Foundation/Providers/RouteServiceProvider.php index e1a65a28..19ef0aa2 100644 --- a/app/Foundation/Providers/RouteServiceProvider.php +++ b/app/Foundation/Providers/RouteServiceProvider.php @@ -16,6 +16,8 @@ use CachetHQ\Cachet\Http\Middleware\Acceptable; use CachetHQ\Cachet\Http\Middleware\Authenticate; use CachetHQ\Cachet\Http\Middleware\Timezone; use CachetHQ\Cachet\Http\Routes\AuthRoutes; +use CachetHQ\Cachet\Http\Routes\Setup\ApiRoutes; +use CachetHQ\Cachet\Http\Routes\SetupRoutes; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -43,6 +45,15 @@ class RouteServiceProvider extends ServiceProvider */ protected $namespace = 'CachetHQ\Cachet\Http\Controllers'; + /** + * These are the route files that should always be available anonymously. + * + * When applying the always_authenticate feature, these routes will be skipped. + * + * @var string[] + */ + protected $whitelistedAuthRoutes = [AuthRoutes::class, SetupRoutes::class, ApiRoutes::class]; + /** * Define the route model bindings, pattern filters, etc. * @@ -129,7 +140,8 @@ class RouteServiceProvider extends ServiceProvider SubstituteBindings::class, ]; - if ($this->app['config']->get('setting.always_authenticate', false) && !$routes instanceof AuthRoutes) { + $applyAlwaysAuthenticate = $this->app['config']->get('setting.always_authenticate', false); + if ($applyAlwaysAuthenticate && !$this->isWhiteListedAuthRoute($routes)) { $middleware[] = Authenticate::class; } @@ -159,4 +171,14 @@ class RouteServiceProvider extends ServiceProvider $routes->map($router); }); } + + private function isWhiteListedAuthRoute($route) + { + foreach ($this->whitelistedAuthRoutes as $whitelistedRoute) { + if(is_a($route, $whitelistedRoute)) { + return true; + } + } + return false; + } } From 11f8e57259522829b1b719b1e57470dc682dafc6 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sat, 27 Jan 2018 23:04:25 +0100 Subject: [PATCH 04/14] Apply always authenticate to the api routes --- app/Foundation/Providers/RouteServiceProvider.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Foundation/Providers/RouteServiceProvider.php b/app/Foundation/Providers/RouteServiceProvider.php index 19ef0aa2..8b191544 100644 --- a/app/Foundation/Providers/RouteServiceProvider.php +++ b/app/Foundation/Providers/RouteServiceProvider.php @@ -167,6 +167,11 @@ class RouteServiceProvider extends ServiceProvider Timezone::class, ]; + $applyAlwaysAuthenticate = $this->app['config']->get('setting.always_authenticate', false); + if ($applyAlwaysAuthenticate && !$this->isWhiteListedAuthRoute($routes)) { + $middleware[] = 'auth.api:true'; + } + $router->group(['middleware' => $middleware], function (Router $router) use ($routes) { $routes->map($router); }); From 5d02ec59c1160e4dacf954e833658097dc966a2e Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sat, 27 Jan 2018 23:11:30 +0100 Subject: [PATCH 05/14] Fix a bug where the dynamic middleware would fail to load due to caching --- app/Http/Controllers/Dashboard/SettingsController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/Http/Controllers/Dashboard/SettingsController.php b/app/Http/Controllers/Dashboard/SettingsController.php index 44201e57..354c0642 100644 --- a/app/Http/Controllers/Dashboard/SettingsController.php +++ b/app/Http/Controllers/Dashboard/SettingsController.php @@ -21,6 +21,7 @@ use GrahamCampbell\Binput\Facades\Binput; use Illuminate\Log\Writer; use Illuminate\Routing\Controller; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\Redirect; @@ -384,6 +385,10 @@ class SettingsController extends Controller Lang::setLocale(Binput::get('app_locale')); } + if (Binput::has('always_authenticate')) { + Artisan::call('route:cache'); + } + return Redirect::back()->withSuccess(trans('dashboard.settings.edit.success')); } From 82861a37ae61075f50d417a84ffce28f43e24f7b Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sat, 27 Jan 2018 23:14:58 +0100 Subject: [PATCH 06/14] Cleanup logic and add doc blocks --- .../Providers/RouteServiceProvider.php | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/app/Foundation/Providers/RouteServiceProvider.php b/app/Foundation/Providers/RouteServiceProvider.php index 8b191544..60a50e41 100644 --- a/app/Foundation/Providers/RouteServiceProvider.php +++ b/app/Foundation/Providers/RouteServiceProvider.php @@ -102,6 +102,7 @@ class RouteServiceProvider extends ServiceProvider $router->group(['namespace' => $this->namespace, 'as' => 'core::'], function (Router $router) { $path = app_path('Http/Routes'); + $applyAlwaysAuthenticate = $this->app['config']->get('setting.always_authenticate', false); $AllFileIterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); $PhpFileIterator = new \RegexIterator($AllFileIterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH); @@ -113,9 +114,9 @@ class RouteServiceProvider extends ServiceProvider $routes = $this->app->make("CachetHQ\\Cachet\\Http\\Routes${class}"); if ($routes::$browser) { - $this->mapForBrowser($router, $routes); + $this->mapForBrowser($router, $routes, $applyAlwaysAuthenticate); } else { - $this->mapOtherwise($router, $routes); + $this->mapOtherwise($router, $routes, $applyAlwaysAuthenticate); } } }); @@ -126,10 +127,11 @@ class RouteServiceProvider extends ServiceProvider * * @param \Illuminate\Routing\Router $router * @param object $routes + * @param bool $applyAlwaysAuthenticate * * @return void */ - protected function mapForBrowser(Router $router, $routes) + protected function mapForBrowser(Router $router, $routes, $applyAlwaysAuthenticate) { $middleware = [ EncryptCookies::class, @@ -140,7 +142,7 @@ class RouteServiceProvider extends ServiceProvider SubstituteBindings::class, ]; - $applyAlwaysAuthenticate = $this->app['config']->get('setting.always_authenticate', false); + if ($applyAlwaysAuthenticate && !$this->isWhiteListedAuthRoute($routes)) { $middleware[] = Authenticate::class; } @@ -155,10 +157,11 @@ class RouteServiceProvider extends ServiceProvider * * @param \Illuminate\Routing\Router $router * @param object $routes + * @param bool $applyAlwaysAuthenticate * * @return void */ - protected function mapOtherwise(Router $router, $routes) + protected function mapOtherwise(Router $router, $routes, $applyAlwaysAuthenticate) { $middleware = [ HandleCors::class, @@ -167,7 +170,6 @@ class RouteServiceProvider extends ServiceProvider Timezone::class, ]; - $applyAlwaysAuthenticate = $this->app['config']->get('setting.always_authenticate', false); if ($applyAlwaysAuthenticate && !$this->isWhiteListedAuthRoute($routes)) { $middleware[] = 'auth.api:true'; } @@ -177,10 +179,18 @@ class RouteServiceProvider extends ServiceProvider }); } - private function isWhiteListedAuthRoute($route) + /** + * Validates if the route object is an instance of the whitelisted routes. + * A small workaround since we cant use multiple classes in a `instanceof` comparison + * + * @param object $routes + * + * @return bool + */ + private function isWhiteListedAuthRoute($routes) { foreach ($this->whitelistedAuthRoutes as $whitelistedRoute) { - if(is_a($route, $whitelistedRoute)) { + if(is_a($routes, $whitelistedRoute)) { return true; } } From 68116129552673112062986450ef617e42853c53 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sat, 27 Jan 2018 23:54:40 +0100 Subject: [PATCH 07/14] Add a hack to append the X-Cachet-Token when in the dashboard --- resources/assets/js/cachet.js | 3 +++ resources/views/layout/dashboard.blade.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/resources/assets/js/cachet.js b/resources/assets/js/cachet.js index 2444940a..e532ce45 100644 --- a/resources/assets/js/cachet.js +++ b/resources/assets/js/cachet.js @@ -16,6 +16,9 @@ $(function () { beforeSend: function (xhr) { xhr.setRequestHeader('Accept', 'application/json'); // xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8'); + if (typeof window.apiKey !== 'undefined') { + xhr.setRequestHeader('X-Cachet-Token', window.apiKey); + } }, statusCode: { 401: function () { diff --git a/resources/views/layout/dashboard.blade.php b/resources/views/layout/dashboard.blade.php index 9438fb8d..ada681c5 100644 --- a/resources/views/layout/dashboard.blade.php +++ b/resources/views/layout/dashboard.blade.php @@ -62,4 +62,7 @@ @yield('js') + From c8e6a8f7c3cc37c3b480694c8027610f2cf9c136 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sun, 4 Feb 2018 13:20:27 +0100 Subject: [PATCH 08/14] Fix a bug where route:cache would not properly clear the old setup --- app/Http/Controllers/Dashboard/SettingsController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Controllers/Dashboard/SettingsController.php b/app/Http/Controllers/Dashboard/SettingsController.php index 354c0642..383db5e9 100644 --- a/app/Http/Controllers/Dashboard/SettingsController.php +++ b/app/Http/Controllers/Dashboard/SettingsController.php @@ -386,7 +386,7 @@ class SettingsController extends Controller } if (Binput::has('always_authenticate')) { - Artisan::call('route:cache'); + Artisan::call('route:clear'); } return Redirect::back()->withSuccess(trans('dashboard.settings.edit.success')); From 6881859e364ffdee25a4de9f3132aaa1a37621ef Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sun, 4 Feb 2018 13:22:21 +0100 Subject: [PATCH 09/14] Move generic ApiRoutes to a whitelisted routeprovider file --- .../Providers/RouteServiceProvider.php | 10 +++- app/Http/Routes/ApiRoutes.php | 4 -- app/Http/Routes/ApiSystemRoutes.php | 50 +++++++++++++++++++ resources/assets/js/cachet.js | 3 -- resources/views/layout/dashboard.blade.php | 3 -- 5 files changed, 58 insertions(+), 12 deletions(-) create mode 100644 app/Http/Routes/ApiSystemRoutes.php diff --git a/app/Foundation/Providers/RouteServiceProvider.php b/app/Foundation/Providers/RouteServiceProvider.php index 60a50e41..df1a1f54 100644 --- a/app/Foundation/Providers/RouteServiceProvider.php +++ b/app/Foundation/Providers/RouteServiceProvider.php @@ -15,8 +15,9 @@ use Barryvdh\Cors\HandleCors; use CachetHQ\Cachet\Http\Middleware\Acceptable; use CachetHQ\Cachet\Http\Middleware\Authenticate; use CachetHQ\Cachet\Http\Middleware\Timezone; +use CachetHQ\Cachet\Http\Routes\ApiSystemRoutes; use CachetHQ\Cachet\Http\Routes\AuthRoutes; -use CachetHQ\Cachet\Http\Routes\Setup\ApiRoutes; +use CachetHQ\Cachet\Http\Routes\Setup\ApiRoutes as ApiSetupRoutes; use CachetHQ\Cachet\Http\Routes\SetupRoutes; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; @@ -52,7 +53,12 @@ class RouteServiceProvider extends ServiceProvider * * @var string[] */ - protected $whitelistedAuthRoutes = [AuthRoutes::class, SetupRoutes::class, ApiRoutes::class]; + protected $whitelistedAuthRoutes = [ + AuthRoutes::class, + SetupRoutes::class, + ApiSystemRoutes::class, + ApiSetupRoutes::class + ]; /** * Define the route model bindings, pattern filters, etc. diff --git a/app/Http/Routes/ApiRoutes.php b/app/Http/Routes/ApiRoutes.php index 3408123a..d18e4064 100644 --- a/app/Http/Routes/ApiRoutes.php +++ b/app/Http/Routes/ApiRoutes.php @@ -41,10 +41,6 @@ class ApiRoutes 'prefix' => 'api/v1', ], function (Registrar $router) { $router->group(['middleware' => ['auth.api']], function (Registrar $router) { - $router->get('ping', 'GeneralController@ping'); - $router->get('version', 'GeneralController@version'); - $router->get('status', 'GeneralController@status'); - $router->get('components', 'ComponentController@index'); $router->get('components/groups', 'ComponentGroupController@index'); $router->get('components/groups/{component_group}', 'ComponentGroupController@show'); diff --git a/app/Http/Routes/ApiSystemRoutes.php b/app/Http/Routes/ApiSystemRoutes.php new file mode 100644 index 00000000..80899f71 --- /dev/null +++ b/app/Http/Routes/ApiSystemRoutes.php @@ -0,0 +1,50 @@ + + */ +class ApiSystemRoutes +{ + /** + * Defines if these routes are for the browser. + * + * @var bool + */ + public static $browser = false; + + /** + * Define the api routes for the system status, ping and version. + * + * @param \Illuminate\Contracts\Routing\Registrar $router + * + * @return void + */ + public function map(Registrar $router) + { + $router->group([ + 'namespace' => 'Api', + 'prefix' => 'api/v1', + ], function (Registrar $router) { + $router->group(['middleware' => ['auth.api']], function (Registrar $router) { + $router->get('ping', 'GeneralController@ping'); + $router->get('version', 'GeneralController@version'); + $router->get('status', 'GeneralController@status'); + }); + }); + } +} diff --git a/resources/assets/js/cachet.js b/resources/assets/js/cachet.js index e532ce45..2444940a 100644 --- a/resources/assets/js/cachet.js +++ b/resources/assets/js/cachet.js @@ -16,9 +16,6 @@ $(function () { beforeSend: function (xhr) { xhr.setRequestHeader('Accept', 'application/json'); // xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8'); - if (typeof window.apiKey !== 'undefined') { - xhr.setRequestHeader('X-Cachet-Token', window.apiKey); - } }, statusCode: { 401: function () { diff --git a/resources/views/layout/dashboard.blade.php b/resources/views/layout/dashboard.blade.php index ada681c5..9438fb8d 100644 --- a/resources/views/layout/dashboard.blade.php +++ b/resources/views/layout/dashboard.blade.php @@ -62,7 +62,4 @@ @yield('js') - From 52db03cb6640ed68d778ab68b079bf3e6ae88200 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sun, 4 Feb 2018 13:39:16 +0100 Subject: [PATCH 10/14] Set the default always_authenticate flag to false --- config/security.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/security.php b/config/security.php index 247584c7..c1789893 100644 --- a/config/security.php +++ b/config/security.php @@ -31,6 +31,6 @@ return [ | when authenticated. | */ - 'always_authenticate' => true, + 'always_authenticate' => false, ]; From f4af5398819cd008344bc29c6bcd30033fc0ba70 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Sun, 4 Feb 2018 13:42:59 +0100 Subject: [PATCH 11/14] StyleCI fixes --- app/Foundation/Providers/RouteServiceProvider.php | 10 +++++----- app/Http/Controllers/Dashboard/SettingsController.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/Foundation/Providers/RouteServiceProvider.php b/app/Foundation/Providers/RouteServiceProvider.php index df1a1f54..99179da5 100644 --- a/app/Foundation/Providers/RouteServiceProvider.php +++ b/app/Foundation/Providers/RouteServiceProvider.php @@ -57,7 +57,7 @@ class RouteServiceProvider extends ServiceProvider AuthRoutes::class, SetupRoutes::class, ApiSystemRoutes::class, - ApiSetupRoutes::class + ApiSetupRoutes::class, ]; /** @@ -148,7 +148,6 @@ class RouteServiceProvider extends ServiceProvider SubstituteBindings::class, ]; - if ($applyAlwaysAuthenticate && !$this->isWhiteListedAuthRoute($routes)) { $middleware[] = Authenticate::class; } @@ -187,19 +186,20 @@ class RouteServiceProvider extends ServiceProvider /** * Validates if the route object is an instance of the whitelisted routes. - * A small workaround since we cant use multiple classes in a `instanceof` comparison + * A small workaround since we cant use multiple classes in a `instanceof` comparison. * - * @param object $routes + * @param object $routes * * @return bool */ private function isWhiteListedAuthRoute($routes) { foreach ($this->whitelistedAuthRoutes as $whitelistedRoute) { - if(is_a($routes, $whitelistedRoute)) { + if (is_a($routes, $whitelistedRoute)) { return true; } } + return false; } } diff --git a/app/Http/Controllers/Dashboard/SettingsController.php b/app/Http/Controllers/Dashboard/SettingsController.php index 383db5e9..55db43a5 100644 --- a/app/Http/Controllers/Dashboard/SettingsController.php +++ b/app/Http/Controllers/Dashboard/SettingsController.php @@ -20,8 +20,8 @@ use Exception; use GrahamCampbell\Binput\Facades\Binput; use Illuminate\Log\Writer; use Illuminate\Routing\Controller; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Lang; use Illuminate\Support\Facades\Redirect; From f545d65a38dd5b90ff220a209af237440dc65611 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Tue, 6 Mar 2018 23:28:38 +0100 Subject: [PATCH 12/14] Add tests that confirm the middleware injection into the routes --- .../Providers/RouteServiceProviderTest.php | 197 ++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/tests/Foundation/Providers/RouteServiceProviderTest.php b/tests/Foundation/Providers/RouteServiceProviderTest.php index 911c6f08..356bf534 100644 --- a/tests/Foundation/Providers/RouteServiceProviderTest.php +++ b/tests/Foundation/Providers/RouteServiceProviderTest.php @@ -11,8 +11,13 @@ namespace CachetHQ\Tests\Cachet\Foundation\Providers; +use CachetHQ\Cachet\Http\Middleware\Authenticate; use AltThree\TestBench\ServiceProviderTrait; +use CachetHQ\Cachet\Foundation\Providers\RouteServiceProvider; use CachetHQ\Tests\Cachet\AbstractTestCase; +use Illuminate\Routing\Route; +use Illuminate\Routing\RouteCollection; +use Illuminate\Routing\Router; /** * This is the route service provider test class. @@ -22,4 +27,196 @@ use CachetHQ\Tests\Cachet\AbstractTestCase; class RouteServiceProviderTest extends AbstractTestCase { use ServiceProviderTrait; + + /** + * The login routes should always be available regardless of the always authenticate setting. + */ + public function testWhenAlwaysAuthenticateIsEnabledLoginRoutesAreWhiteListed() + { + $loginRoutes = [ + 'core::get:auth.login', + 'core::post:auth.login', + 'core::post:auth.two-factor', + 'core::get:auth.logout' + ]; + + $this->assertRoutesDontHaveAuthMiddleware($loginRoutes, $this->bootRouter(true)); + } + + /** + * The setup routes should always be available regardless of the always authenticate setting. + */ + public function testWhenAlwaysAuthenticateIsEnabledSetupRoutesAreWhiteListed() + { + $loginRoutes = [ + 'core::get:setup', + 'core::post:setup.step1', + 'core::post:setup.step2', + 'core::post:setup.step3', + ]; + + $this->assertRoutesDontHaveAuthMiddleware($loginRoutes, $this->bootRouter(true)); + } + + /** + * It's possible to retrieve the cachet version, status and ping endpoints regardless of the + * always authenticate setting. + */ + public function testWhenAlwaysAuthenticateIsEnabledApiSystemRoutesAreWhiteListed() + { + $routeActions = [ + 'CachetHQ\Cachet\Http\Controllers\Api\GeneralController@ping', + 'CachetHQ\Cachet\Http\Controllers\Api\GeneralController@version', + 'CachetHQ\Cachet\Http\Controllers\Api\GeneralController@status', + ]; + + $router = $this->bootRouter(true); + + foreach ($routeActions as $routeAction) { + $route = $router->getRoutes()->getByAction($routeAction); + $this->assertInstanceOf(Route::class, $route); + + $middleware = $route->gatherMiddleware(); + $this->assertFalse(in_array('auth.api:true', $middleware, true)); + } + } + + /** + * When using always authenticate, normal graceful api routes will require full authentication. + */ + public function testWhenAlwaysAuthenticateIsEnabledApiRoutesAreHardAuthenticated() + { + $routeActions = [ + 'CachetHQ\Cachet\Http\Controllers\Api\ComponentController@index', + 'CachetHQ\Cachet\Http\Controllers\Api\ComponentGroupController@index', + 'CachetHQ\Cachet\Http\Controllers\Api\ComponentGroupController@show', + 'CachetHQ\Cachet\Http\Controllers\Api\ComponentController@show', + 'CachetHQ\Cachet\Http\Controllers\Api\IncidentController@index', + 'CachetHQ\Cachet\Http\Controllers\Api\IncidentController@show', + 'CachetHQ\Cachet\Http\Controllers\Api\IncidentUpdateController@index', + 'CachetHQ\Cachet\Http\Controllers\Api\IncidentUpdateController@show', + 'CachetHQ\Cachet\Http\Controllers\Api\MetricController@index', + 'CachetHQ\Cachet\Http\Controllers\Api\MetricController@show', + 'CachetHQ\Cachet\Http\Controllers\Api\MetricPointController@index', + 'CachetHQ\Cachet\Http\Controllers\Api\ScheduleController@index', + 'CachetHQ\Cachet\Http\Controllers\Api\ScheduleController@show', + ]; + + $router = $this->bootRouter(true); + + foreach ($routeActions as $routeAction) { + $route = $router->getRoutes()->getByAction($routeAction); + $this->assertInstanceOf(Route::class, $route); + + $middleware = $route->gatherMiddleware(); + $this->assertTrue(in_array('auth.api:true', $middleware, true)); + } + } + + + /** + * When enabling the always authenticate setting, the core frontpage routes require authentication. + */ + public function testWhenAlwaysAuthenticateIsEnabledAllNormalRoutesAreAuthenticated() + { + $namedRoutes = [ + 'core::get:status-page', + 'core::get:incident', + 'core::get:schedule', + 'core::get:metric', + 'core::get:component_shield', + 'core::get:feed.atom', + 'core::get:feed.rss', + 'core::get:signup.invite', + 'core::post:signup.invite', + 'core::get:subscribe', + 'core::post:subscribe', + 'core::get:subscribe.manage', + 'core::post:subscribe.manage', + 'core::get:subscribe.verify', + 'core::get:subscribe.unsubscribe', + ]; + + $this->assertRoutesHaveAuthMiddleware($namedRoutes, $this->bootRouter(true)); + } + + /** + * This test asserts that when always authenticate is disabled, you are allowed to visit the frontpage + * routes without enforced authentication. + */ + public function testWhenAlwaysAuthenticateIsDisabledAllNormalRoutesAreUnauthenticated() + { + $namedRoutes = [ + 'core::get:status-page', + 'core::get:incident', + 'core::get:schedule', + 'core::get:metric', + 'core::get:component_shield', + 'core::get:feed.atom', + 'core::get:feed.rss', + 'core::get:signup.invite', + 'core::post:signup.invite', + 'core::get:subscribe', + 'core::post:subscribe', + 'core::get:subscribe.manage', + 'core::post:subscribe.manage', + 'core::get:subscribe.verify', + 'core::get:subscribe.unsubscribe', + ]; + + $this->assertRoutesDontHaveAuthMiddleware($namedRoutes, $this->bootRouter(false)); + } + + /** + * A helper method that will execute the RouteProvider's map function and return a clean router. + * + * @param boolean $alwaysAuthenticate + * @return Router + */ + private function bootRouter($alwaysAuthenticate) + { + $this->app->config->set('setting.always_authenticate', $alwaysAuthenticate); + $router = $this->app->make(Router::class); + $router->setRoutes(new RouteCollection()); + + $routeServiceProvider = new RouteServiceProvider($this->app); + $routeServiceProvider->map($router); + return $router; + } + + /** + * Assertion helper that asserts if the authentication middleware has not been injected onto + * the collection of named routes. + * + * @param array $routeNames + * @param Router $router + */ + private function assertRoutesDontHaveAuthMiddleware(array $routeNames, Router $router) + { + foreach ($routeNames as $routeName) { + $route = $router->getRoutes()->getByName($routeName); + $this->assertInstanceOf(Route::class, $route); + + $middleware = $route->gatherMiddleware(); + $this->assertFalse(in_array(Authenticate::class, $middleware, true)); + } + } + + /** + * Assertion helper that asserts if the authentication middleware has been injected onto + * the collection of named routes. + * + * @param array $routeNames + * @param Router $router + */ + private function assertRoutesHaveAuthMiddleware(array $routeNames, Router $router) + { + foreach ($routeNames as $routeName) { + $route = $router->getRoutes()->getByName($routeName); + $this->assertInstanceOf(Route::class, $route); + + $middleware = $route->gatherMiddleware(); + $this->assertTrue(in_array(Authenticate::class, $middleware, true)); + } + } } From e424c2638b7fd86e8fd619d1f26898295a1aad65 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Tue, 6 Mar 2018 23:30:53 +0100 Subject: [PATCH 13/14] Signup routes are actually whitelisted --- .../Providers/RouteServiceProvider.php | 2 ++ .../Providers/RouteServiceProviderTest.php | 19 +++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/Foundation/Providers/RouteServiceProvider.php b/app/Foundation/Providers/RouteServiceProvider.php index 99179da5..9a17c2a6 100644 --- a/app/Foundation/Providers/RouteServiceProvider.php +++ b/app/Foundation/Providers/RouteServiceProvider.php @@ -19,6 +19,7 @@ use CachetHQ\Cachet\Http\Routes\ApiSystemRoutes; use CachetHQ\Cachet\Http\Routes\AuthRoutes; use CachetHQ\Cachet\Http\Routes\Setup\ApiRoutes as ApiSetupRoutes; use CachetHQ\Cachet\Http\Routes\SetupRoutes; +use CachetHQ\Cachet\Http\Routes\SignupRoutes; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; @@ -56,6 +57,7 @@ class RouteServiceProvider extends ServiceProvider protected $whitelistedAuthRoutes = [ AuthRoutes::class, SetupRoutes::class, + SignupRoutes::class, ApiSystemRoutes::class, ApiSetupRoutes::class, ]; diff --git a/tests/Foundation/Providers/RouteServiceProviderTest.php b/tests/Foundation/Providers/RouteServiceProviderTest.php index 356bf534..f88f03f8 100644 --- a/tests/Foundation/Providers/RouteServiceProviderTest.php +++ b/tests/Foundation/Providers/RouteServiceProviderTest.php @@ -11,9 +11,9 @@ namespace CachetHQ\Tests\Cachet\Foundation\Providers; -use CachetHQ\Cachet\Http\Middleware\Authenticate; use AltThree\TestBench\ServiceProviderTrait; use CachetHQ\Cachet\Foundation\Providers\RouteServiceProvider; +use CachetHQ\Cachet\Http\Middleware\Authenticate; use CachetHQ\Tests\Cachet\AbstractTestCase; use Illuminate\Routing\Route; use Illuminate\Routing\RouteCollection; @@ -37,7 +37,9 @@ class RouteServiceProviderTest extends AbstractTestCase 'core::get:auth.login', 'core::post:auth.login', 'core::post:auth.two-factor', - 'core::get:auth.logout' + 'core::get:auth.logout', + 'core::get:signup.invite', + 'core::post:signup.invite', ]; $this->assertRoutesDontHaveAuthMiddleware($loginRoutes, $this->bootRouter(true)); @@ -113,7 +115,6 @@ class RouteServiceProviderTest extends AbstractTestCase } } - /** * When enabling the always authenticate setting, the core frontpage routes require authentication. */ @@ -127,8 +128,6 @@ class RouteServiceProviderTest extends AbstractTestCase 'core::get:component_shield', 'core::get:feed.atom', 'core::get:feed.rss', - 'core::get:signup.invite', - 'core::post:signup.invite', 'core::get:subscribe', 'core::post:subscribe', 'core::get:subscribe.manage', @@ -154,8 +153,6 @@ class RouteServiceProviderTest extends AbstractTestCase 'core::get:component_shield', 'core::get:feed.atom', 'core::get:feed.rss', - 'core::get:signup.invite', - 'core::post:signup.invite', 'core::get:subscribe', 'core::post:subscribe', 'core::get:subscribe.manage', @@ -170,7 +167,8 @@ class RouteServiceProviderTest extends AbstractTestCase /** * A helper method that will execute the RouteProvider's map function and return a clean router. * - * @param boolean $alwaysAuthenticate + * @param bool $alwaysAuthenticate + * * @return Router */ private function bootRouter($alwaysAuthenticate) @@ -181,6 +179,7 @@ class RouteServiceProviderTest extends AbstractTestCase $routeServiceProvider = new RouteServiceProvider($this->app); $routeServiceProvider->map($router); + return $router; } @@ -188,7 +187,7 @@ class RouteServiceProviderTest extends AbstractTestCase * Assertion helper that asserts if the authentication middleware has not been injected onto * the collection of named routes. * - * @param array $routeNames + * @param array $routeNames * @param Router $router */ private function assertRoutesDontHaveAuthMiddleware(array $routeNames, Router $router) @@ -206,7 +205,7 @@ class RouteServiceProviderTest extends AbstractTestCase * Assertion helper that asserts if the authentication middleware has been injected onto * the collection of named routes. * - * @param array $routeNames + * @param array $routeNames * @param Router $router */ private function assertRoutesHaveAuthMiddleware(array $routeNames, Router $router) From 50bcfb509305386233567e6742be8992f0e583c0 Mon Sep 17 00:00:00 2001 From: Nico Stapelbroek Date: Wed, 4 Apr 2018 18:37:52 +0200 Subject: [PATCH 14/14] Move always_authenticate config to the setting.php file --- config/security.php | 11 ----------- config/setting.php | 12 ++++++++++++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/config/security.php b/config/security.php index c1789893..724338af 100644 --- a/config/security.php +++ b/config/security.php @@ -22,15 +22,4 @@ return [ */ 'evil' => ['(? false, - ]; diff --git a/config/setting.php b/config/setting.php index 4f58d404..c9615c35 100644 --- a/config/setting.php +++ b/config/setting.php @@ -111,4 +111,16 @@ return [ */ 'only_disrupted_days' => false, + + /* + |-------------------------------------------------------------------------- + | Always authenticate + |-------------------------------------------------------------------------- + | + | Whether to lock down Cachet and only allow viewing pages + | when authenticated. + | + */ + + 'always_authenticate' => false, ];