diff --git a/.github/workflows/enlightn.yml b/.github/workflows/enlightn.yml deleted file mode 100644 index 805d840f5..000000000 --- a/.github/workflows/enlightn.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Run Enlightn Checks - -on: [push, pull_request] - -jobs: - enlightn: - env: - ENLIGHTN_USERNAME: ${{ secrets.ENLIGHTN_USERNAME }} - ENLIGHTN_API_TOKEN: ${{ secrets.ENLIGHTN_API_TOKEN }} - ENLIGHTN_GITHUB_REPO: ${{ github.repository }} - - name: Enlightn - - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 7.4 - extensions: bcmath, ctype, dom, fileinfo, intl, gd, json, mbstring, pdo, pdo_sqlite, openssl, sqlite, xml, zip - coverage: none - - - name: Install dependencies with Enlightn Pro - if: env.ENLIGHTN_API_TOKEN - run: | - composer config http-basic.satis.laravel-enlightn.com "$ENLIGHTN_USERNAME" "$ENLIGHTN_API_TOKEN" - composer config repositories.enlightn composer https://satis.laravel-enlightn.com - composer require --prefer-dist --no-interaction enlightn/enlightnpro - - - name: Install Composer dependencies - if: ${{ !env.ENLIGHTN_API_TOKEN }} - run: composer install --prefer-dist --no-interaction - - - name: Run Enlightn Checks and Trigger the Enlightn Bot - if: github.event_name == 'pull_request' && env.ENLIGHTN_API_TOKEN - env: - APP_ENV: local - run: | - cp .env.example .env - php artisan enlightn --ci --report --review --issue=${{ github.event.number }} - - - name: Run Enlightn Checks - if: ${{ github.event_name != 'pull_request' || !env.ENLIGHTN_API_TOKEN }} - env: - APP_ENV: local - run: | - cp .env.example .env - php artisan enlightn --ci diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 9fe18fe7a..83006e4a1 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -54,6 +54,7 @@ class Kernel extends HttpKernel 'permission:read-api', 'company.identify', 'bindings', + 'read.only', 'language', 'firewall.all', ], @@ -62,12 +63,14 @@ class Kernel extends HttpKernel 'web', 'company.identify', 'bindings', + 'read.only', 'wizard.redirect', ], 'guest' => [ 'web', 'auth.redirect', + 'read.only', ], 'admin' => [ @@ -76,6 +79,7 @@ class Kernel extends HttpKernel 'auth.disabled', 'company.identify', 'bindings', + 'read.only', 'wizard.redirect', 'menu.admin', 'permission:read-admin-panel', @@ -96,6 +100,7 @@ class Kernel extends HttpKernel 'auth.disabled', 'company.identify', 'bindings', + 'read.only', 'menu.portal', 'permission:read-client-portal', ], @@ -109,6 +114,7 @@ class Kernel extends HttpKernel 'signature', 'company.identify', 'bindings', + 'read.only', 'header.x', 'language', 'firewall.all', @@ -157,6 +163,7 @@ class Kernel extends HttpKernel 'install.can' => \App\Http\Middleware\CanInstall::class, 'install.redirect' => \App\Http\Middleware\RedirectIfNotInstalled::class, 'money' => \App\Http\Middleware\Money::class, + 'read.only' => \App\Http\Middleware\CheckForReadOnlyMode::class, 'wizard.redirect' => \App\Http\Middleware\RedirectIfWizardNotCompleted::class, // Vendor diff --git a/app/Http/Middleware/CheckForReadOnlyMode.php b/app/Http/Middleware/CheckForReadOnlyMode.php new file mode 100644 index 000000000..e6f1847e8 --- /dev/null +++ b/app/Http/Middleware/CheckForReadOnlyMode.php @@ -0,0 +1,68 @@ +routeIs(config('read-only.login_route')); + $is_logout = $request->routeIs(config('read-only.logout_route')); + + if ($is_login || $is_logout) { + return $next($request); + } + } + + foreach (config('read-only.whitelist') as $method => $route) { + if (! $request->isMethod($method) || ! $request->routeIs($route)) { + continue; + } + + return $next($request); + } + + foreach (config('read-only.livewire') as $path) { + $url = company_id() . '/livewire/message/' . $path; + + if (! $request->isMethod('post') || ! $request->is($url)) { + continue; + } + + return $next($request); + } + + foreach (config('read-only.methods') as $method) { + if (! $request->isMethod(strtolower($method))) { + continue; + } + + //abort(Response::HTTP_UNAUTHORIZED); + + return response()->json([ + 'success' => false, + 'error' => true, + 'data' => null, + 'message' => trans('maintenance.read_only'), + ], Response::HTTP_UNAUTHORIZED); + } + + return $next($request); + } +} diff --git a/app/Http/ViewComposers/ReadOnlyNotification.php b/app/Http/ViewComposers/ReadOnlyNotification.php new file mode 100644 index 000000000..1e8d488bc --- /dev/null +++ b/app/Http/ViewComposers/ReadOnlyNotification.php @@ -0,0 +1,17 @@ +getFactory()->startPush('content_content_start', view('partials.read-only')); + } +} diff --git a/app/Listeners/Common/SkipScheduleInReadOnlyMode.php b/app/Listeners/Common/SkipScheduleInReadOnlyMode.php new file mode 100644 index 000000000..fe72f3b7a --- /dev/null +++ b/app/Listeners/Common/SkipScheduleInReadOnlyMode.php @@ -0,0 +1,30 @@ +task->skip(true); + + $schedule = app(Schedule::class); + + foreach ($schedule->events() as $task) { + $task->skip(true); + } + } +} diff --git a/app/Providers/Blade.php b/app/Providers/Blade.php index 2073f81c3..d0c1e99ea 100644 --- a/app/Providers/Blade.php +++ b/app/Providers/Blade.php @@ -24,6 +24,10 @@ class Blade extends ServiceProvider Facade::directive('widget', function ($expression) { return ""; }); + + Facade::if('readonly', function () { + return config('read-only.enabled'); + }); } /** diff --git a/app/Providers/Event.php b/app/Providers/Event.php index d792276a7..1bd084658 100644 --- a/app/Providers/Event.php +++ b/app/Providers/Event.php @@ -48,6 +48,10 @@ class Event extends Provider 'Illuminate\Auth\Events\Logout' => [ 'App\Listeners\Auth\Logout', ], + //'Illuminate\Console\Events\ScheduledTaskStarting' => [ + 'Illuminate\Console\Events\CommandStarting' => [ + 'App\Listeners\Common\SkipScheduleInReadOnlyMode', + ], 'App\Events\Auth\LandingPageShowing' => [ 'App\Listeners\Auth\AddLandingPages', ], diff --git a/app/Providers/ViewComposer.php b/app/Providers/ViewComposer.php index 9b1114be5..53425626f 100644 --- a/app/Providers/ViewComposer.php +++ b/app/Providers/ViewComposer.php @@ -66,6 +66,11 @@ class ViewComposer extends Provider View::composer( 'layouts.wizard', 'App\Http\ViewComposers\Wizard' ); + + View::composer( + ['partials.admin.content'], + 'App\Http\ViewComposers\ReadOnlyNotification' + ); } /** diff --git a/composer.json b/composer.json index 9899293f7..a0695231d 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,6 @@ "consoletvs/charts": "6.5.*", "dingo/api": "3.0.*", "doctrine/dbal": "^3.1", - "enlightn/enlightn": "^1.16", "fideloper/proxy": "^4.4", "fruitcake/laravel-cors": "^2.0", "genealabs/laravel-model-caching": "0.11.*", diff --git a/config/enlightn.php b/config/enlightn.php deleted file mode 100644 index d0738dcec..000000000 --- a/config/enlightn.php +++ /dev/null @@ -1,191 +0,0 @@ - ['*'], - - // If you wish to skip running some analyzers, list the classes in the array below. - 'exclude_analyzers' => [], - - // If you wish to skip running some analyzers in CI mode, list the classes below. - 'ci_mode_exclude_analyzers' => [], - - /* - |-------------------------------------------------------------------------- - | Enlightn Analyzer Paths - |-------------------------------------------------------------------------- - | - | The following array lists the "analyzer" paths that will be searched - | recursively to find analyzer classes. This option will only be used - | if the analyzers option above is set to the asterisk wildcard. The - | key is the base namespace to resolve the class name. - | - */ - 'analyzer_paths' => [ - 'Enlightn\\Enlightn\\Analyzers' => base_path('vendor/enlightn/enlightn/src/Analyzers'), - 'Enlightn\\EnlightnPro\\Analyzers' => base_path('vendor/enlightn/enlightnpro/src/Analyzers'), - ], - - /* - |-------------------------------------------------------------------------- - | Enlightn Base Path - |-------------------------------------------------------------------------- - | - | The following array lists the directories that will be scanned for - | application specific code. By default, we are scanning your app - | folder, migrations folder and the seeders folder. - | - */ - 'base_path' => [ - app_path(), - database_path('migrations'), - database_path('seeders'), - ], - - /* - |-------------------------------------------------------------------------- - | Environment Specific Analyzers - |-------------------------------------------------------------------------- - | - | There are some analyzers that are meant to be run for specific environments. - | The options below specify whether we should skip environment specific - | analyzers if the environment does not match. - | - */ - 'skip_env_specific' => env('ENLIGHTN_SKIP_ENVIRONMENT_SPECIFIC', false), - - /* - |-------------------------------------------------------------------------- - | Guest URL - |-------------------------------------------------------------------------- - | - | Specify any guest url or path (preferably your app's login url) here. This - | would be used by Enlightn to inspect your application HTTP headers. - | Example: '/login'. - | - */ - 'guest_url' => null, - - /* - |-------------------------------------------------------------------------- - | Exclusions From Reporting - |-------------------------------------------------------------------------- - | - | Specify the analyzer classes that you wish to exclude from reporting. This - | means that if any of these analyzers fail, they will not be counted - | towards the exit status of the Enlightn command. This is useful - | if you wish to run the command in your CI/CD pipeline. - | Example: [\Enlightn\Enlightn\Analyzers\Security\XSSAnalyzer::class]. - | - */ - 'dont_report' => [ - Enlightn\Enlightn\Analyzers\Performance\QueueDriverAnalyzer::class, - Enlightn\Enlightn\Analyzers\Security\CSRFAnalyzer::class, - Enlightn\Enlightn\Analyzers\Security\StableDependencyAnalyzer::class, - Enlightn\Enlightn\Analyzers\Security\FrontendVulnerableDependencyAnalyzer::class, - Enlightn\EnlightnPro\Analyzers\Reliability\DeadRouteAnalyzer::class, - Enlightn\Enlightn\Analyzers\Performance\UnusedGlobalMiddlewareAnalyzer::class, - ], - - /* - |-------------------------------------------------------------------------- - | Ignoring Errors - |-------------------------------------------------------------------------- - | - | Use this config option to ignore specific errors. The key of this array - | would be the analyzer class and the value would be an associative - | array with path and details. Run php artisan enlightn:baseline - | to auto-generate this. Patterns are supported in details. - | - */ - 'ignore_errors' => [ - Enlightn\Enlightn\Analyzers\Performance\EnvCallAnalyzer::class => [ - ['path' => 'app/Listeners/Update/V20/Version207.php', 'details' => '*'], - ['path' => 'app/Console/Commands/InstallRefresh.php', 'details' => '*'], - ['path' => 'app/Console/Commands/InstallRefresh.php', 'details' => '*'], - ['path' => 'app/Http/Middleware/CanInstall.php', 'details' => '*'], - ] - ], - - /* - |-------------------------------------------------------------------------- - | Analyzer Configurations - |-------------------------------------------------------------------------- - | - | The following configuration options pertain to individual analyzers. - | These are recommended options but feel free to customize them based - | on your application needs. - | - */ - 'license_whitelist' => [ - 'Apache-2.0', 'Apache2', 'BSD-2-Clause', 'BSD-3-Clause', 'LGPL-2.1-only', 'LGPL-2.1', - 'LGPL-2.1-or-later', 'LGPL-3.0', 'LGPL-3.0-only', 'LGPL-3.0-or-later', 'MIT', 'ISC', - 'CC0-1.0', 'Unlicense', 'WTFPL', 'GPL-3.0-only', 'GPL-3.0-or-later', 'GPL-3.0+', - ], - - /* - |-------------------------------------------------------------------------- - | Credentials - |-------------------------------------------------------------------------- - | - | The following credentials are used to share your Enlightn report with - | the Enlightn Github Bot. This allows the bot to compile the report - | and add review comments on your pull requests. - | - */ - 'credentials' => [ - 'username' => env('ENLIGHTN_USERNAME'), - 'api_token' => env('ENLIGHTN_API_TOKEN'), - ], - - // Set this value to your Github repo for integrating with the Enlightn Github Bot - // Format: "myorg/myrepo" like "laravel/framework". - 'github_repo' => env('ENLIGHTN_GITHUB_REPO'), - - // Set to true to restrict the max number of files displayed in the enlightn - // command for each check. Set to false to display all files. - 'compact_lines' => true, - - // List your commercial packages (licensed by you) below, so that they are not - // flagged by the License Analyzer. - 'commercial_packages' => [ - 'enlightn/enlightnpro', - ], - - 'allowed_permissions' => [ - base_path() => '775', - app_path() => '775', - resource_path() => '775', - storage_path() => '775', - public_path() => '775', - config_path() => '775', - database_path() => '775', - base_path('routes') => '775', - app()->bootstrapPath() => '775', - app()->bootstrapPath('cache') => '775', - app()->bootstrapPath('app.php') => '664', - base_path('artisan') => '775', - public_path('index.php') => '664', - public_path('server.php') => '664', - ], - - 'writable_directories' => [ - storage_path(), - app()->bootstrapPath('cache'), - ], - - 'debug_blacklist' => [ - 'var_dump', 'dump', 'dd', 'print_r', 'var_export', 'debug_print_backtrace', 'debug_zval_dump', - ], - -]; diff --git a/config/read-only.php b/config/read-only.php new file mode 100644 index 000000000..6e1040424 --- /dev/null +++ b/config/read-only.php @@ -0,0 +1,50 @@ + env('READ_ONLY_ENABLED', false), + + /* + |-------------------------------------------------------------------------- + | Enable read-only mode but still allow users to login + |-------------------------------------------------------------------------- + */ + 'allow_login' => env('READ_ONLY_LOGIN', true), + + /* + |-------------------------------------------------------------------------- + | The login/logout routes to allow if allow_login=true + |-------------------------------------------------------------------------- + */ + 'login_route' => 'login.store', + 'logout_route' => 'logout', + + /* + |-------------------------------------------------------------------------- + | The request methods that you want to block + |-------------------------------------------------------------------------- + */ + 'methods' => explode(',', env('READ_ONLY_METHODS', 'post,put,patch,delete')), + + /* + |-------------------------------------------------------------------------- + | Whitelist certain request methods to certain routes + |-------------------------------------------------------------------------- + */ + 'whitelist' => [ + // 'post' => 'dashboard', + ], + + /* + |-------------------------------------------------------------------------- + | Skip livewire paths + |-------------------------------------------------------------------------- + */ + 'livewire' => explode(',', env('READ_ONLY_LIVEWIRE', 'common.search')), + +]; diff --git a/resources/lang/en-GB/maintenance.php b/resources/lang/en-GB/maintenance.php index 49b62ed72..645b59fed 100644 --- a/resources/lang/en-GB/maintenance.php +++ b/resources/lang/en-GB/maintenance.php @@ -6,4 +6,6 @@ return [ 'message' => 'Sorry, we\'re down for maintenance. Please, try again later!', + 'read_only' => 'Read-only mode is enabled. You are allowed to view but not change anything!', + ]; diff --git a/resources/views/partials/read-only.blade.php b/resources/views/partials/read-only.blade.php new file mode 100644 index 000000000..17cd9e41c --- /dev/null +++ b/resources/views/partials/read-only.blade.php @@ -0,0 +1,3 @@ +