[
  {
    "path": ".editorconfig",
    "content": "; This file is for unifying the coding style for different editors and IDEs.\n; More information at http://editorconfig.org\n\nroot = true\n\n[*]\ncharset = utf-8\nindent_size = 4\nindent_style = space\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.{yml,yaml}]\nindent_size = 2\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [olssonm]\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Run tests\n\non:\n  push:\n  pull_request:\n\njobs:\n  php-tests:\n    runs-on: ubuntu-latest\n\n    strategy:\n      matrix:\n        php: [8.5, 8.4, 8.3, 8.2]\n        laravel: ['12.*', '13.*']\n        exclude:\n          - laravel: 13.*\n            php: 8.2\n          - laravel: 12.*\n            php: 8.5\n\n    name: P${{ matrix.php }} - L${{ matrix.laravel }}\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Setup PHP\n        uses: shivammathur/setup-php@v2\n        with:\n          php-version: ${{ matrix.php }}\n          extensions: dom, curl, libxml, mbstring, zip\n          tools: composer:v2\n          coverage: none\n\n      - name: Install dependencies\n        run: |\n          composer require \"illuminate/support:${{ matrix.laravel }}\" --no-interaction --no-progress --no-suggest\n          composer update --prefer-dist --no-interaction --no-progress\n\n      - name: Execute tests\n        run: composer test\n"
  },
  {
    "path": ".gitignore",
    "content": "/vendor\ncomposer.lock\n.DS_Store\n.phpunit.result.cache\n.phpunit.cache\nphpunit-output\n"
  },
  {
    "path": "LICENSE.md",
    "content": "# The MIT License (MIT)\n\nCopyright (c) 2024 Marcus Olsson <contact@marcusolsson.me>\n\n> Permission is hereby granted, free of charge, to any person obtaining a copy\n> of this software and associated documentation files (the \"Software\"), to deal\n> in the Software without restriction, including without limitation the rights\n> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n> copies of the Software, and to permit persons to whom the Software is\n> furnished to do so, subject to the following conditions:\n>\n> The above copyright notice and this permission notice shall be included in\n> all copies or substantial portions of the Software.\n>\n> THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n> THE SOFTWARE.\n"
  },
  {
    "path": "README.jp.md",
    "content": "# Laravel Very Basic Auth\n\n[![Latest Version on Packagist][ico-version]][link-packagist]\n[![Total downloads][ico-downloads]][link-packagist]\n[![Software License][ico-license]](LICENSE.md)\n[![Build Status][ico-build]][link-build]\n\n![very-basic-auth](https://user-images.githubusercontent.com/907114/40575964-331559ce-60ef-11e8-8366-aba700fc5567.png)\n\n**利用可能なドキュメントは以下です:**\n\n🇬🇧 [English](README.md)  \n🇯🇵 [日本語](README.jp.md)\n\nLaravel 5 Very Basic AuthはLaravel標準の`auth.basic`とは違い、実際のデータベースの情報を使うことなくBasic認証を追加します。\n\n<img width=\"400\" alt=\"Screenshot\" src=\"https://user-images.githubusercontent.com/907114/29876493-3907afd8-8d9d-11e7-8068-f461855c493b.png\">\n\n例えば、開発中のサイトにユーザーをアクセスさせたい時や、まだデータベースやモデルを用意していない時に使うと便利です。あなたのサイトがデータベースを利用していない場合でも、アクセスを制御することができます。\n\n認証に失敗した場合には、\"401 Unauthorized\"のレスポンスを返します。\n\n#### 注意点\n\nBasic認証は望まないユーザーからのアクセスを排除することができますが、ブルートフォース攻撃に対しては厳密には安全ではありません。もしこのパッケージをセキュリティのために単独で利用するのであれば、ログインの試行回数を制限するために、少なくともApacheかNginxのrate-limitersを確認するべきです。\n\n## インストール\n\nComposer経由\n\n``` bash\n$ composer require olssonm/l5-very-basic-auth\n```\n\nこのパッケージのv4.* (for Laravel 5.5)以降では、サービスプロバイダーからパッケージを読み込むのに、パッケージのオートディスカバリーを使用しています。パッケージをインストールすると、以下のメッセージが表示されるはずです。\n\n```\nDiscovered Package: olssonm/l5-very-basic-auth\n```\n\nもしも手動でプロバイダーに追加したい場合は、composer.jsonファイルでオートディスカバリーを切って、\n\n``` json\n\"extra\": {\n    \"laravel\": {\n        \"dont-discover\": [\n            \"olssonm/l5-very-basic-auth\"\n        ]\n    }\n},\n```\n\n(`config/app.php`)のprovidersにプロバイダーを追加してください。\n\n``` php\n'providers' => [\n    Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider::class\n]\n```\n\n## 設定\n\n`$ php artisan vendor:publish`のコマンドを実行し、`Provider: Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider`を選んで設定ファイルを公開してください。`$ php artisan vendor:publish --provider=\"Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider\"`でも設定ファイルを公開することができます。\n\n`very_basic_auth.php`のファイルがあなたの`app/config`ディレクトリにコピーされます。ここにusernameやpasswordなどの幾つかの設定を置くことができます。\n\n### 注意\n\n**デフォルトのパスワードはありません。** セキュリティのために(誰もが同じパスワードになってしまわないように)、インストール時にランダムなパスワードが設定されます。個別のパスワードを設定するためにもパッケージ設定の公開をして下さい。\n\n#### ビューとメッセージ\n\n`very_basic_auth.php`ファイルでは、メッセージの代わりにカスタマイズしたビューを設定することができます。\n\n``` php\n// ユーザーがオプトアウトするか、キャンセルを押した場合に表示されるメッセージ\n'error_message'     => 'You have to supply your credentials to access this resource.',\n\n// エラーメッセージの代わりにviewを使いたい場合は\"error_view\"のコメントアウトを外して下さい。\n// この場合、あなたのデフォルトのレスポンスメッセージよりもエラービューが優先されます。\n// 'error_view'        => 'very_basic_auth::default'\n```\n\n`error_view`のコメントアウトを外した場合、ミドルウェアは指定されたviewを探そうとします。このビュー名は通常と同じように`.blade.php`の拡張子無しで設定してください。\n\n*以前のバージョンから2.1にアップグレードする場合、このkeyとvalueは公開された設定には存在しないので、自分自身で設定を追加する必要があります。*\n\n## 使い方\n\nこのミドルウェアはルートを保護するのに`auth.very_basic`の短縮キーを使います。`Route::group()`に適用して複数のルートを保護することもできますし、個別に保護するルートを選ぶこともできます。\n\n**グループを使う場合**\n``` php\nRoute::group(['middleware' => 'auth.very_basic'], function() {\n    Route::get('/', ['as' => 'start', 'uses' => 'StartController@index']);\n    Route::get('/page', ['as' => 'page', 'uses' => 'StartController@page']);\n});\n```\n\n**単独で使う場合**\n``` php\nRoute::get('/', [\n    'as' => 'start',\n    'uses' => 'StartController@index',\n    'middleware' => 'auth.very_basic'\n]);\n```\n\n認証情報をルート上に記述することもできます。\n\n``` php\nRoute::get('/', [\n    'as' => 'start',\n    'uses' => 'StartController@index',\n    'middleware' => 'auth.very_basic:username,password'\n]);\n```\n\n*Note:* 認証情報をルート上に記述した場合、設定ファイルの`very_basic_auth.php`より優先されます。\n\n\n## テスト\n\n``` bash\n$ composer test\n```\n\nまたは\n\n``` bash\n$ phpunit\n```\n\nテストを実行する際は、Laravelは常にenvironmentの値を\"testing\"にします。`testing`が`very_basic_auth.php`の`envs`配列内に存在することを確認して下さい。\n\n## ライセンス\n\nMITライセンスです。 詳しくはこちらを見てください。[License File](LICENSE.md)\n\n© 2024 [Marcus Olsson](https://marcusolsson.me).\n\n[ico-version]: https://img.shields.io/packagist/v/olssonm/l5-very-basic-auth.svg?style=flat-square\n[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square\n[ico-build]: https://img.shields.io/github/workflow/status/olssonm/l5-very-basic-auth/Run%20tests.svg?style=flat-square&label=tests\n[ico-downloads]: https://img.shields.io/packagist/dt/olssonm/l5-very-basic-auth.svg?style=flat-square\n[link-packagist]: https://packagist.org/packages/olssonm/l5-very-basic-auth\n[link-build]: https://github.com/olssonm/l5-very-basic-auth/actions?query=workflow%3A%22Run+tests%22\n"
  },
  {
    "path": "README.md",
    "content": "# Laravel Very Basic Auth\n\n[![Latest Version on Packagist][ico-version]][link-packagist]\n[![Total downloads][ico-downloads]][link-packagist]\n[![Software License][ico-license]](LICENSE.md)\n[![Build Status][ico-build]][link-build]\n\n![very-basic-auth](https://user-images.githubusercontent.com/907114/40575964-331559ce-60ef-11e8-8366-aba700fc5567.png)\n\n**Documentation available in:**\n\n🇬🇧 [English](README.md)  \n🇯🇵 [日本語](README.jp.md)\n\nThis package allows you to add a HTTP Basic Auth filter on your routes, without the need to use a database – which the Laravel default `auth.basic`-middleware relies on.\n\n<img width=\"400\" alt=\"Screenshot\" src=\"https://user-images.githubusercontent.com/907114/29876493-3907afd8-8d9d-11e7-8068-f461855c493b.png\">\n\nPerfect when you want to give your clients access to your development site before you have yet to set up your database and/or models. Or perhaps your site doesn't even use a database and you still wish to keep it protected.\n\nOn failed authentication the user will get a \"401 Unauthorized\" response.\n\n#### A thing to note\n\nWhile HTTP Basic Auth does give you a protection layer against unwanted visitors, it is still not strictly safe from brute-force attacks. If you are solely using this package for security, you should at least consider looking into Apache or Nginx rate-limiters to limit login attempts.\n\n## Installation\n\nVia Composer\n\n``` bash\n$ composer require olssonm/l5-very-basic-auth\n```\n\nSince v4.* (for Laravel 5.5) this package uses Package Auto-Discovery for loading the service provider. Once installed you should see the message\n\n```\nDiscovered Package: olssonm/l5-very-basic-auth\n```\n\nIf you would like to manually add the provider, turn off Auto-Discovery for the package in your composer.json-file:\n\n``` json\n\"extra\": {\n    \"laravel\": {\n        \"dont-discover\": [\n            \"olssonm/l5-very-basic-auth\"\n        ]\n    }\n},\n```\n\nAnd then add the provider in the providers array (`config/app.php`).\n\n``` php\n'providers' => [\n    Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider::class\n]\n```\n\n## Configuration\n\nRun the command `$ php artisan vendor:publish` and select `Provider: Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider` to publish the configuration. You could also type `$ php artisan vendor:publish --provider=\"Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider\"` to directly publish the files.\n\nThe file `very_basic_auth.php` will then be copied to your `app/config`-folder – here you can set various options such as username and password.\n\n#### Note\n\n**There is no default password**. Upon installation you will need to set your own username and password. Please publish the packages configuration to have the ability to set these. **If left empty, basic auth will not be active**.\n\n### Environments\n\nYou may set the environments that the package should be applied for. You may simply use \"`*`\" to use in all environments (this is also the default).\n\n``` php\n'envs' => [\n    '*'\n],\n```\n\nOr\n\n``` php\n'envs' => [\n    'production',\n    'development',\n    'local'\n],\n```\n\n### Response handlers\n\nWhen the authentication fails the response handler sends out an error response (see \"Views and messages\" for more about these options). By default the handler will be `\\Olssonm\\VeryBasicAuth\\Handlers\\DefaultResponseHandler` (see `response_handler` in `very_basic_auth.php`). You may however write your own response-logic if you so choose. The only requirement is that it implements the `\\Olssonm\\VeryBasicAuth\\Handlers\\ResponseHandler`-interface, and has an `__invoke`-method that accepts a request-object, like so:\n\n``` php\nuse Illuminate\\Http\\Request;\nuse Olssonm\\VeryBasicAuth\\Handlers\\ResponseHandler;\n\nclass CustomResponseHandler implements ResponseHandler\n{\n    public function __invoke(Request $request)\n    {\n        // Do some stuff\n        return response('Custom response', 401);\n    }\n}\n```\n\n\n### Views and messages\n\nIn the `very_basic_auth.php`-configuration you have the ability to set a custom view instead of a message.\n\n``` php\n// Message to display if the user \"opts out\"/clicks \"cancel\"\n'error_message'     => 'You have to supply your credentials to access this resource.',\n\n// If you prefer to use a view with your error message you can uncomment \"error_view\".\n// This will supersede your default response message\n// 'error_view'        => 'very_basic_auth::default'\n```\n\nIf you uncomment `error_view`, the middleware will try to find your specified view. You supply this value as usual (without the `.blade.php`-extention).\n\n## Usage\n\nThe middleware uses the `auth.very_basic`-filter to protect routes. You can either use `Route::group()` to protect multiple routes, or chose just to protect them individually.\n\n**Group**\n``` php\nRoute::group(['middleware' => 'auth.very_basic'], function() {\n    Route::get('/', ['as' => 'start', 'uses' => 'StartController@index']);\n    Route::get('/page', ['as' => 'page', 'uses' => 'StartController@page']);\n});\n```\n\n**Single**\n``` php\nRoute::get('/', [\n    'as' => 'start',\n    'uses' => 'StartController@index',\n    'middleware' => 'auth.very_basic'\n]);\n```\n\nYou may also set the credentials inline;\n\n``` php\nRoute::get('/', [\n    'as' => 'start',\n    'uses' => 'StartController@index',\n    'middleware' => 'auth.very_basic:username,password'\n]);\n```\n\n*Note:* inline credentials always take president over the `very_basic_auth.php`-configuration file.\n\n### Generating hash for password\n\nIf you want to generate a hash for your password, you can use the `artisan`-command:\n\n``` bash\nphp artisan very-basic-auth:generate-password\n```\n\nIt will ask you for a password, and then automatically insert the hash into your `.env`-file\n\n\n## Testing\n\n``` bash\n$ composer test\n```\n\nor\n\n``` bash\n$ phpunit\n```\n\nLaravel always runs in the \"testing\" environment while running tests. Make sure that `testing` is set in the `envs`-array in `very_basic_auth.php`.\n\n## Thank you\n\nA big thank you to the people who has contributed to this package, among others:\n\n**[kazuhei](https://github.com/kazuhei)** – for providing the awesome Japanese translation  \n**[freekmurze](https://github.com/freekmurze)** – for additional information on package/vendor installations  \n**[faiare](https://github.com/faiare)** – for pointing out and implementing the `realm`-attribute ([RFC7235](https://tools.ietf.org/html/rfc7235#section-2.2))\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n\n© 2024 [Marcus Olsson](https://marcusolsson.me).\n\n[ico-version]: https://img.shields.io/packagist/v/olssonm/l5-very-basic-auth.svg?style=flat-square\n[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square\n[ico-build]: https://img.shields.io/github/actions/workflow/status/olssonm/l5-very-basic-auth/test.yml?branch=master&style=flat-square\n[ico-downloads]: https://img.shields.io/packagist/dt/olssonm/l5-very-basic-auth.svg?style=flat-square\n[link-packagist]: https://packagist.org/packages/olssonm/l5-very-basic-auth\n[link-build]: https://github.com/olssonm/l5-very-basic-auth/actions?query=workflow%3A%22Run+tests%22\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"olssonm/l5-very-basic-auth\",\n    \"description\": \"Laravel stateless HTTP basic auth without the need for a database\",\n    \"license\": \"MIT\",\n    \"keywords\": [\n        \"olssonm\",\n        \"laravel\",\n        \"authentication\",\n        \"http basic auth\"\n    ],\n    \"authors\": [\n        {\n            \"name\": \"Marcus Olsson\",\n            \"email\": \"contact@marcusolsson.me\",\n            \"homepage\": \"https://marcusolsson.me\"\n        }\n    ],\n    \"homepage\": \"https://github.com/olssonm/l5-very-basic-auth\",\n    \"require\": {\n        \"php\": \"^8.2\",\n        \"illuminate/support\": \"^12.0 || ^13.0\"\n    },\n    \"require-dev\": {\n        \"laravel/helpers\": \"^1.1\",\n        \"orchestra/testbench\": \"^10.0 || ^11.0\",\n        \"pestphp/pest\": \"^3.7 || ^4.4\",\n        \"pestphp/pest-plugin-laravel\": \"^3.1 || ^4.1\",\n        \"phpstan/phpstan\": \"^2.0\",\n        \"phpunit/phpunit\": \"^11.5.3 || ^12.5.12\",\n        \"squizlabs/php_codesniffer\": \"^3.5 || ^4.0\"\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"autoload\": {\n        \"psr-4\": {\n            \"Olssonm\\\\VeryBasicAuth\\\\\": \"src\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Olssonm\\\\VeryBasicAuth\\\\Tests\\\\\": \"tests\"\n        }\n    },\n    \"config\": {\n        \"allow-plugins\": {\n            \"pestphp/pest-plugin\": true\n        },\n        \"sort-packages\": true\n    },\n    \"extra\": {\n        \"branch-alias\": {\n            \"dev-master\": \"11.x-dev\"\n        },\n        \"laravel\": {\n            \"providers\": [\n                \"Olssonm\\\\VeryBasicAuth\\\\VeryBasicAuthServiceProvider\"\n            ]\n        }\n    },\n    \"scripts\": {\n        \"phpfix\": \"vendor/bin/phpcbf --standard=\\\"PSR12\\\" ./src --ignore=./src/resources/*\",\n        \"phpsniff\": \"vendor/bin/phpcs --standard=\\\"PSR12\\\" ./src --ignore=./src/resources/*\",\n        \"phpstan\": \"./vendor/bin/phpstan\",\n        \"test\": \"vendor/bin/pest\"\n    }\n}\n"
  },
  {
    "path": "phpstan.neon",
    "content": "parameters:\n    level: 8\n    paths:\n      - src\n    ignoreErrors:\n      - identifier: missingType.generics\n"
  },
  {
    "path": "phpunit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit\n    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n    xsi:noNamespaceSchemaLocation=\"https://schema.phpunit.de/10.5/phpunit.xsd\"\n    bootstrap=\"vendor/autoload.php\"\n    backupGlobals=\"false\"\n    colors=\"true\"\n    processIsolation=\"false\"\n    stopOnFailure=\"false\"\n    cacheDirectory=\".phpunit.cache\"\n    backupStaticProperties=\"false\"\n>\n    <testsuites>\n        <testsuite name=\"Package Test Suite\">\n            <directory>tests</directory>\n        </testsuite>\n    </testsuites>\n    <source>\n        <include>\n            <directory>src</directory>\n        </include>\n        <exclude>\n            <directory suffix=\".blade.php\">src</directory>\n        </exclude>\n    </source>\n</phpunit>\n"
  },
  {
    "path": "src/Console/GeneratePassword.php",
    "content": "<?php\n\nnamespace Olssonm\\VeryBasicAuth\\Console;\n\nuse Illuminate\\Console\\Command;\nuse Symfony\\Component\\Console\\Attribute\\AsCommand;\n\n#[AsCommand('very-basic-auth:generate-password')]\nclass GeneratePassword extends Command\n{\n    /**\n     * The name and signature of the console command.\n     *\n     * @var string\n     */\n    protected $signature = 'very-basic-auth:generate-password';\n\n    /**\n     * The console command description.\n     *\n     * @var string\n     */\n    protected $description = 'Allows to securely set the `BASIC_AUTH_PASSWORD` in your Laravel `.env` file.';\n\n    /**\n     * Execute the console command.\n     *\n     * @return int\n     */\n    public function handle(): int\n    {\n        $password = $this->promptForValidPassword();\n\n        if ($this->writeNewEnvironmentFileWith($this->hashPassword($password))) {\n            $this->info('The password has been set successfully.');\n        }\n\n        return static::SUCCESS;\n    }\n\n    /**\n     * Keep asking for a password until a valid one is provided.\n     */\n    protected function promptForValidPassword(): string\n    {\n        while (true) {\n            $password = (string) $this->secret('Please enter a password for the very basic auth');\n\n            if (!$this->isPasswordAccepted($password)) {\n                $this->error('The password must be at least 8 characters.');\n                continue;\n            }\n\n            if (!$this->passwordsMatch($password)) {\n                $this->error('The passwords do not match. Please try again.');\n                continue;\n            }\n\n            return $password;\n        }\n    }\n\n    /**\n     * Determine if the provided password fulfills minimum requirements.\n     */\n    protected function isPasswordAccepted(?string $password): bool\n    {\n        return !empty($password) && strlen($password) >= 8;\n    }\n\n    /**\n     * Ask for confirmation and ensure it matches.\n     */\n    protected function passwordsMatch(string $password): bool\n    {\n        return $password === (string) $this->secret('Please confirm your password');\n    }\n\n    /**\n     * Hash the password using Laravel's configured hasher.\n     */\n    protected function hashPassword(string $password): string\n    {\n        return app()->make('hash')->make($password);\n    }\n\n    /**\n     * Write a new environment file with the given password.\n     *\n     * @param string $password\n     * @return bool\n     */\n    protected function writeNewEnvironmentFileWith(string $password): bool\n    {\n        $envPath = $this->laravel->environmentFilePath();\n        $input = file_get_contents($envPath);\n\n        $replaced = preg_replace_callback(\n            $this->passwordReplacementPattern(),\n            fn($matches) => 'BASIC_AUTH_PASSWORD=\"' . $password . '\"',\n            $input\n        );\n\n        if ($replaced === $input || $replaced === null) {\n            $this->error('Unable to set password. No BASIC_AUTH_PASSWORD variable was found in the .env file. Please add it first');\n            return false;\n        }\n\n        file_put_contents($envPath, $replaced);\n\n        return true;\n    }\n\n    /**\n     * Get a regex pattern that will match env BASIC_AUTH_PASSWORD with any password.\n     *\n     * @return string\n     */\n    protected function passwordReplacementPattern(): string\n    {\n        return '/^BASIC_AUTH_PASSWORD=.*$/m';\n    }\n}\n"
  },
  {
    "path": "src/Handlers/DefaultResponseHandler.php",
    "content": "<?php\n\nnamespace Olssonm\\VeryBasicAuth\\Handlers;\n\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\n\nclass DefaultResponseHandler implements ResponseHandler\n{\n    public function __invoke(Request $request): Response|JsonResponse\n    {\n        // Build header\n        $header = [\n            'WWW-Authenticate' => sprintf(\n                'Basic realm=\"%s\", charset=\"UTF-8\"',\n                config('very_basic_auth.realm', 'Basic Auth')\n            ),\n        ];\n\n        // View\n        $view = config('very_basic_auth.error_view');\n\n        // If the request want's JSON, else view\n        if ($request->wantsJson()) {\n            return response()->json([\n                'message' => config('very_basic_auth.error_message'),\n            ], 401, $header);\n        } elseif (isset($view)) {\n            return response()->view($view, [], 401)\n                ->withHeaders($header);\n        }\n\n        // Return default message\n        return response()->make(config('very_basic_auth.error_message'), 401, $header);\n    }\n}\n"
  },
  {
    "path": "src/Handlers/ResponseHandler.php",
    "content": "<?php\n\nnamespace Olssonm\\VeryBasicAuth\\Handlers;\n\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\n\ninterface ResponseHandler\n{\n    public function __invoke(Request $request): Response|JsonResponse;\n}\n"
  },
  {
    "path": "src/Http/Middleware/VeryBasicAuth.php",
    "content": "<?php\n\nnamespace Olssonm\\VeryBasicAuth\\Http\\Middleware;\n\nuse Closure;\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Hash;\nuse Olssonm\\VeryBasicAuth\\Handlers\\ResponseHandler;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass VeryBasicAuth\n{\n    protected ResponseHandler $responseHandler;\n\n    public function __construct(ResponseHandler $responseHandler)\n    {\n        $this->responseHandler = $responseHandler;\n    }\n\n    /**\n     * Handle an incoming request\n     *\n     * @param mixed $username\n     * @param mixed $password\n     */\n    public function handle(Request $request, Closure $next, $username = null, $password = null): Response\n    {\n        $active = (count(array_intersect([\n                '*',\n                app()->environment(),\n            ], config('very_basic_auth.envs'))) > 0);\n\n        // Check if middleware is in use in current environment\n        if ($active) {\n            $authUsername = $username ?? config('very_basic_auth.user');\n            $authPassword = $password ?? config('very_basic_auth.password');\n\n            if (! $authUsername && ! $authPassword) {\n                return $next($request);\n            }\n\n            $plainPassword = $request->getPassword();\n\n            $isCorrectPassword = $plainPassword === $authPassword;\n\n            if (! $isCorrectPassword) {\n                try {\n                    $isCorrectPassword = Hash::check($plainPassword, $authPassword);\n                } catch (\\Throwable $e) {\n                    // If the password is not hashed, we will just return false\n                    $isCorrectPassword = false;\n                }\n            }\n\n            if ($request->getUser() !== $authUsername || !$isCorrectPassword) {\n                return $this->deniedResponse($request);\n            }\n        }\n\n        return $next($request);\n    }\n\n    /**\n     * Return a error response\n     *\n     * @return \\Illuminate\\Http\\Response|\\Illuminate\\Http\\JsonResponse\n     */\n    private function deniedResponse(Request $request): Response|JsonResponse\n    {\n        return ($this->responseHandler)($request);\n    }\n}\n"
  },
  {
    "path": "src/VeryBasicAuthServiceProvider.php",
    "content": "<?php\n\nnamespace Olssonm\\VeryBasicAuth;\n\nuse Illuminate\\Support\\ServiceProvider;\nuse Olssonm\\VeryBasicAuth\\Console\\GeneratePassword;\nuse Olssonm\\VeryBasicAuth\\Handlers\\DefaultResponseHandler;\nuse Olssonm\\VeryBasicAuth\\Handlers\\ResponseHandler;\n\nclass VeryBasicAuthServiceProvider extends ServiceProvider\n{\n    /**\n     * Path to config-file\n     *\n     * @var string\n     */\n    protected $config;\n\n    /**\n     * Path to stub\n     *\n     * @var string\n     */\n    protected $stub;\n\n    /**\n     * Constructor\n     *\n     * @param \\Illuminate\\Contracts\\Foundation\\Application $app\n     * @return void\n     */\n    public function __construct($app)\n    {\n        $this->config = __DIR__ . '/config.php';\n\n        parent::__construct($app);\n    }\n\n    /**\n     * Perform post-registration booting of services.\n     *\n     * @return void\n     */\n    public function boot(\\Illuminate\\Routing\\Router $router)\n    {\n        // Publishing of configuration\n        $this->publishes([\n            $this->config => config_path('very_basic_auth.php'),\n        ]);\n\n        // Load default view/s\n        $this->loadViewsFrom(__DIR__ . '/resources/views', 'very_basic_auth');\n\n        // Register middleware\n        $router->aliasMiddleware('auth.very_basic', \\Olssonm\\VeryBasicAuth\\Http\\Middleware\\VeryBasicAuth::class);\n\n        // Register commands\n        if ($this->app->runningInConsole()) {\n            $this->commands([\n                GeneratePassword::class,\n            ]);\n        }\n    }\n\n    /**\n     * Register any package services.\n     *\n     * @return void\n     */\n    public function register()\n    {\n        // If the user doesn't set their own config, load default\n        $this->mergeConfigFrom(\n            $this->config,\n            'very_basic_auth'\n        );\n\n        $this->app->bind(\n            ResponseHandler::class,\n            config('very_basic_auth.response_handler', DefaultResponseHandler::class)\n        );\n    }\n}\n"
  },
  {
    "path": "src/config.php",
    "content": "<?php\n\n/**\n * Configuration for the \"HTTP Very Basic Auth\"-middleware\n */\n\nreturn [\n    // Username\n    'user' => env('BASIC_AUTH_USERNAME', ''),\n\n    // Password\n    'password' => env('BASIC_AUTH_PASSWORD', ''),\n\n    // Environments where the middleware is active. Use \"*\" to protect all envs\n    'envs' => [\n        '*',\n    ],\n\n    // Response handler for the error responses\n    'response_handler' => \\Olssonm\\VeryBasicAuth\\Handlers\\DefaultResponseHandler::class,\n\n    // Message to display if the user \"opts out\"/clicks \"cancel\"\n    'error_message' => 'You have to supply your credentials to access this resource.',\n\n    // Message to display in the auth dialog in some browsers (mainly Internet Explorer).\n    // Realm is also used to define a \"space\" that should share credentials.\n    'realm' => 'Basic Auth',\n\n    // If you prefer to use a view with your error message you can uncomment \"error_view\".\n    // This will supersede your default response message\n    // 'error_view'        => 'very_basic_auth::default'\n];\n"
  },
  {
    "path": "src/resources/views/default.blade.php",
    "content": "<!DOCTYPE html>\n<html>\n    <head>\n        <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n        <title>Error</title>\n        <style media=\"screen\">\n            html {\n                font-family: 'helvetica neue', helvetica, arial, sans-serif;\n                background-color: #ecf0f1;\n            }\n\n            .box {\n                width: 50%;\n                min-width: 400px;\n                margin: 0 auto;\n                padding: 30px;\n                background-color: #fff;\n            }\n\n            code {\n                font-size: 110%;\n                color: #e74c3c;\n                background-color: #ecf0f1;\n                padding: 1px 4px;\n            }\n        </style>\n    </head>\n    <body style=\"margin-bottom: 38px;\">\n        <div class=\"box\">\n            <h3>This is the default view for the olssonm/l5-very-basic-auth-package.</h3>\n            <p>\n                To customize this, simply edit your <code>very_basic_auth</code>-configuration file and set\n                <code>error_view</code> to your own custom blade-template (the default is <code>very_basic_auth::default</code>).\n            </p>\n        </div>\n    </body>\n</html>\n"
  },
  {
    "path": "tests/Fixtures/CustomResponseHandler.php",
    "content": "<?php\n\nnamespace Olssonm\\VeryBasicAuth\\Tests\\Fixtures;\n\nuse Illuminate\\Http\\JsonResponse;\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Response;\nuse Olssonm\\VeryBasicAuth\\Handlers\\ResponseHandler;\n\nclass CustomResponseHandler implements ResponseHandler\n{\n    public function __invoke(Request $request): Response|JsonResponse\n    {\n        return response('Custom response', 401);\n    }\n}\n"
  },
  {
    "path": "tests/Pest.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Facades\\Route;\nuse Olssonm\\VeryBasicAuth\\Http\\Middleware\\VeryBasicAuth;\nuse Olssonm\\VeryBasicAuth\\Tests\\TestCase;\n\nuses(TestCase::class)\n    ->beforeEach(function () {\n        // Set default config for testing\n        config()->set('very_basic_auth.user', 'test');\n        config()->set('very_basic_auth.password', 'test');\n    \n        Route::get('/', fn () => 'ok')->middleware(VeryBasicAuth::class)->name('default');\n        Route::get('/test', fn () => 'ok')->middleware(VeryBasicAuth::class);\n        Route::get('/inline', fn () => 'ok')->middleware(\n            sprintf('auth.very_basic:%s,%s', config('very_basic_auth.user'), config('very_basic_auth.password'))\n        )->name('inline');\n    })\n    ->in(__DIR__);\n"
  },
  {
    "path": "tests/TestCase.php",
    "content": "<?php\n\nnamespace Olssonm\\VeryBasicAuth\\Tests;\n\nuse Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider;\nuse Orchestra\\Testbench\\TestCase as OrchestraTestCase;\n\nabstract class TestCase extends OrchestraTestCase\n{\n    protected function setUp(): void\n    {\n        parent::setUp();\n    }\n\n    protected function getPackageProviders($app)\n    {\n        return [\n            VeryBasicAuthServiceProvider::class,\n        ];\n    }\n\n    public static function tearDownAfterClass(): void\n    {\n        parent::tearDownAfterClass();\n    }\n}\n"
  },
  {
    "path": "tests/VeryBasicAuthTest.php",
    "content": "<?php\n\nuse Illuminate\\Support\\Str;\nuse Olssonm\\VeryBasicAuth\\Handlers\\DefaultResponseHandler;\nuse Olssonm\\VeryBasicAuth\\Handlers\\ResponseHandler;\nuse Olssonm\\VeryBasicAuth\\Http\\Middleware\\VeryBasicAuth;\nuse Olssonm\\VeryBasicAuth\\Tests\\Fixtures\\CustomResponseHandler;\n\nuse function Pest\\Laravel\\get;\nuse function Pest\\Laravel\\withHeaders;\n\ntest('basic auth filter is set', function () {\n    expect(in_array(VeryBasicAuth::class, $this->app->router->getMiddleware()))->toBeTrue();\n    expect(array_key_exists('auth.very_basic', $this->app->router->getMiddleware()));\n});\n\ntest('config file is installed', function () {\n    expect(file_exists(__DIR__.'/../src/config.php'))->toBeTrue();\n});\n\ntest('install package', function () {\n    $this->artisan('vendor:publish', [\n        '--provider' => 'Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider',\n    ])->assertExitCode(0);\n\n    expect(file_exists(config_path('very_basic_auth.php')))->toBeTrue();\n});\n\ntest('request with no credentials and no config passes', function () {\n    config()->set('very_basic_auth.user', '');\n    config()->set('very_basic_auth.password', '');\n\n    $response = get('/');\n\n    expect($response->getStatusCode())->toEqual(200);\n    expect($response->headers->get('WWW-Authenticate'))->toEqual(null);\n});\n\ntest('request with no credentials fails', function () {\n    $response = get('/');\n\n    expect($response->getStatusCode())->toEqual(401);\n    expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm=\"%s\", charset=\"UTF-8\"', config('very_basic_auth.realm')));\n    expect($response->getContent())->toEqual(config('very_basic_auth.error_message'));\n});\n\ntest('request with incorrect credentials fails - text/html', function () {\n    $response = withHeaders([\n        'PHP_AUTH_USER' => str_random(20),\n        'PHP_AUTH_PW' => str_random(20),\n    ])->get('/');\n\n    expect($response->getStatusCode())->toEqual(401);\n    expect(requestHeader($response))->toEqual('text/html; charset=utf-8');\n    expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm=\"%s\", charset=\"UTF-8\"', config('very_basic_auth.realm')));\n    expect($response->getContent())->toEqual(config('very_basic_auth.error_message'));\n});\n\ntest('request with incorrect credentials fails - hashed password', function () {\n\n    config()->set('very_basic_auth.user', 'test');\n    config()->set('very_basic_auth.password', app()->make('hash')->make('test'));\n\n    $response = withHeaders([\n        'PHP_AUTH_USER' => str_random(20),\n        'PHP_AUTH_PW' => str_random(20),\n    ])->get('/');\n\n    expect($response->getStatusCode())->toEqual(401);\n});\n\ntest('request with incorrect credentials fails - json', function () {\n    $response = withHeaders([\n        'PHP_AUTH_USER' => str_random(20),\n        'PHP_AUTH_PW' => str_random(20),\n        'Accept' => 'application/json',\n    ])->get('/');\n\n    $content = json_decode($response->getContent());\n\n    expect($response->getStatusCode())->toEqual(401);\n    expect($response->headers->get('content-type'))->toEqual('application/json');\n    expect(json_last_error())->toEqual(JSON_ERROR_NONE);\n    expect($content->message)->toEqual(config('very_basic_auth.error_message'));\n    expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm=\"%s\", charset=\"UTF-8\"', config('very_basic_auth.realm')));\n});\n\ntest('request with incorrect credentials fails - view', function () {\n    config()->set('very_basic_auth.error_view', 'very_basic_auth::default');\n\n    $response = withHeaders([\n        'PHP_AUTH_USER' => str_random(20),\n        'PHP_AUTH_PW' => str_random(20),\n    ])->get('/');\n\n    expect($response->getStatusCode())->toEqual(401);\n    expect(requestHeader($response))->toEqual('text/html; charset=utf-8');\n    expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm=\"%s\", charset=\"UTF-8\"', config('very_basic_auth.realm')));\n\n    $this->assertStringContainsStringIgnoringCase('This is the default view for the olssonm/l5-very-basic-auth-package', $response->getContent());\n});\n\ntest('request with correct credentials passes', function () {\n    $response = withHeaders([\n        'PHP_AUTH_USER' => config('very_basic_auth.user'),\n        'PHP_AUTH_PW' => config('very_basic_auth.password'),\n    ])->get('/');\n\n    expect($response->getStatusCode())->toEqual(200);\n    expect($response->getContent())->toEqual('ok');\n});\n\ntest('request with correct credentials passes - hashed password', function () {\n    config()->set('very_basic_auth.user', 'test');\n    config()->set('very_basic_auth.password', app()->make('hash')->make('test'));\n\n    $response = withHeaders([\n        'PHP_AUTH_USER' => 'test',\n        'PHP_AUTH_PW' => 'test',\n    ])->get('/');\n\n    expect($response->getStatusCode())->toEqual(200);\n    expect($response->getContent())->toEqual('ok');\n});\n\ntest('environments', function () {\n    config()->set('very_basic_auth.envs', ['production']);\n    get('/')->assertStatus(200);\n\n    config()->set('very_basic_auth.envs', ['local']);\n    get('/')->assertStatus(200);\n\n    config()->set('very_basic_auth.envs', ['*']);\n    get('/')->assertStatus(401);\n\n    config()->set('very_basic_auth.envs', ['testing']);\n    get('/')->assertStatus(401);\n});\n\ntest('request with incorrect inline credentials fails', function () {\n    $response = withHeaders([\n        'PHP_AUTH_USER' => str_random(20),\n        'PHP_AUTH_PW' => str_random(20),\n    ])->get('/inline');\n\n    expect($response->getStatusCode())->toEqual(401);\n    expect($response->getContent())->toEqual(config('very_basic_auth.error_message'));\n});\n\ntest('request with correct inline credentials passes', function () {\n    $response = withHeaders([\n        'PHP_AUTH_USER' => config('very_basic_auth.user'),\n        'PHP_AUTH_PW' => config('very_basic_auth.password'),\n    ])->get('/inline');\n\n    expect($response->getStatusCode())->toEqual(200);\n    expect($response->getContent())->toEqual('ok');\n});\n\ntest('test response handlers', function () {\n    // Custom response handler\n    app()->bind(\n        ResponseHandler::class,\n        CustomResponseHandler::class\n    );\n\n    $response = get('/test');\n\n    expect($response->getStatusCode())->toEqual(401);\n    expect($response->getContent())->toEqual('Custom response');\n\n    // Default response handler\n    app()->bind(\n        ResponseHandler::class,\n        DefaultResponseHandler::class\n    );\n\n    $response = get('/test');\n\n    expect($response->getStatusCode())->toEqual(401);\n    expect($response->getContent())->toEqual(config('very_basic_auth.error_message'));\n});\n\n// Test for the console command PasswordGenerateCommand\ntest('console command sets password in .env file', function () {\n    $envPath = base_path('.env');\n\n    // Clean up any existing .env before the test\n    if (file_exists($envPath)) {\n        unlink($envPath);\n    }\n\n    // Create a fresh .env\n    file_put_contents($envPath, \"APP_NAME=Laravel\\nBASIC_AUTH_PASSWORD=test\");\n\n    $password = 'password' . uniqid();\n\n    // Simulate user input for the console command\n    $this->artisan('very-basic-auth:generate-password')\n        ->expectsQuestion('Please enter a password for the very basic auth', $password)\n        ->expectsQuestion('Please confirm your password', $password)\n        ->assertExitCode(0);\n\n    // Reload env-variables to make sure the newest value is available\n    $this->artisan('config:cache');\n    $hashedPassword = config('very_basic_auth.password');\n\n    expect($hashedPassword)->not->toBeNull();\n    expect(app()->make('hash')->check($password, $hashedPassword))->toBeTrue();\n    expect(config('app.name'))->toEqual('Laravel');\n});\n\nfunction requestHeader($response): string\n{\n    return Str::lower($response->headers->get('content-type'));\n}\n"
  }
]