Skip to content

Commit

Permalink
Merge pull request #54 from NoahNxT/main
Browse files Browse the repository at this point in the history
Implement Key Generation Command for Enhanced Security
  • Loading branch information
freekmurze authored May 14, 2024
2 parents a98ea10 + a0d8650 commit 5917499
Show file tree
Hide file tree
Showing 9 changed files with 226 additions and 12 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ jobs:
matrix:
os: [ubuntu-latest, windows-latest]
php: [8.2, 8.1]
laravel: ['9.*', '10.*', '11.*']
laravel: ['10.*', '11.*']
stability: [prefer-stable]
include:
- laravel: 10.*
testbench: 8.*
- laravel: 9.*
testbench: 7.*
- laravel: 11.*
testbench: 9.*
exclude:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ composer.lock
docs
vendor
.php-cs-fixer.cache

.phpunit.cache/
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ composer require spatie/laravel-url-signer
You must set an environment variable called `URL_SIGNER_SIGNATURE_KEY` and set it to a long secret value. This value will be used to sign and validate signed URLs.

```
# in your .env file
URL_SIGNER_SIGNATURE_KEY=some_random_value
php artisan generate:url-signer-signature-key
{--s|show : Display the key instead of modifying files.}
{--always-no : Skip generating key if it already exists.}
{--f|force : Skip confirmation when overwriting an existing key.}
```

The configuration file can optionally be published via:
Expand Down
9 changes: 5 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
],
"require": {
"php": "^8.1",
"spatie/url-signer": "^2.0",
"illuminate/support": "^9.0|^10.0|^11.0",
"spatie/laravel-package-tools": "^1.13.6"
"illuminate/support": "^10.0|^11.0",
"illuminate/console": "^10.10.0|^11.0",
"spatie/laravel-package-tools": "^1.13.6",
"spatie/url-signer": "^2.0"
},
"require-dev": {
"orchestra/testbench": "^7.12.1|^8.0|^9.0",
"orchestra/testbench": "^8.0|^9.0",
"pestphp/pest": "^1.22.2|^1.22|^2.34"
},
"autoload": {
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<testsuites>
<testsuite name="Package Test Suite">
<directory>./tests/</directory>
<directory suffix=".php">./tests/Commands/</directory>
</testsuite>
</testsuites>
<php>
Expand Down
115 changes: 115 additions & 0 deletions src/Commands/GenerateUrlSignerSignatureKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

namespace Spatie\UrlSigner\Laravel\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Str;

class GenerateUrlSignerSignatureKey extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'generate:url-signer-signature-key
{--s|show : Display the key instead of modifying files.}
{--always-no : Skip generating key if it already exists.}
{--f|force : Skip confirmation when overwriting an existing key.}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a new URL signer signature key.';

/**
* Execute the console command.
* @return void
*/
public function handle(): void
{
$key = Str::random(64);

if ($this->option('show')) {
$this->comment($key);

return;
}

if (file_exists($path = $this->envPath()) === false) {
$this->displayKey($key);
return;
}

if (Str::contains(file_get_contents($path), 'URL_SIGNER_SIGNATURE_KEY') === false) {
// create new entry
file_put_contents($path, PHP_EOL."URL_SIGNER_SIGNATURE_KEY=$key".PHP_EOL, FILE_APPEND);
} else {
if ($this->option('always-no')) {
$this->comment('Secret key already exists. Skipping...');

return;
}

if ($this->isConfirmed() === false) {
$this->comment('Phew... No changes were made to your secret key.');

return;
}

// update existing entry
file_put_contents($path, str_replace(
'URL_SIGNER_SIGNATURE_KEY='.$this->laravel['config']['url-signer.signature_key'],
'URL_SIGNER_SIGNATURE_KEY='.$key,
file_get_contents($path)
));
}

$this->displayKey($key);

return;
}

/**
* Display the key.
*
* @param string $key
* @return void
*/
protected function displayKey(string $key): void
{
$this->laravel['config']['url-signer.signature_key'] = $key;

$this->info("Url-signer key [$key] set successfully.");

return;
}

/**
* Check if the modification is confirmed.
*
* @return bool
*/
protected function isConfirmed(): bool
{
return $this->option('force') || $this->confirm(
'This will invalidate all existing tokens. Are you sure you want to override the secret key?'
);
}

/**
* Get the .env file path.
*
* @return string
*/
protected function envPath(): string
{
if (method_exists($this->laravel, 'environmentFilePath')) {
return $this->laravel->environmentFilePath();
}

return $this->laravel->basePath('.env');
}
}
8 changes: 6 additions & 2 deletions src/UrlSignerServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Spatie\LaravelPackageTools\Package;
use Spatie\LaravelPackageTools\PackageServiceProvider;
use Spatie\UrlSigner\Laravel\Commands\GenerateUrlSignerSignatureKey;
use Spatie\UrlSigner\UrlSigner as BaseUrlSigner;

class UrlSignerServiceProvider extends PackageServiceProvider
Expand All @@ -12,10 +13,13 @@ public function configurePackage(Package $package): void
{
$package
->name('laravel-url-signer')
->hasConfigFile();
->hasConfigFile()
->hasCommands([
GenerateUrlSignerSignatureKey::class
]);;
}

public function registeringPackage()
public function registeringPackage(): void
{
$this->app->singleton(BaseUrlSigner::class, function () {
$config = config('url-signer');
Expand Down
27 changes: 27 additions & 0 deletions tests/Commands/GenerateUrlSignerSignatureKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

use Illuminate\Support\Env;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;

beforeEach(function () {
File::copy(__DIR__.'/../Stubs/.env', base_path('.env'));
});

test('it can generate a URL signer signature key', function () {
Artisan::call('generate:url-signer-signature-key');

$env = trim(File::get(base_path('.env')));

expect($env)->toContain('URL_SIGNER_SIGNATURE_KEY=');
});

test('it can display a URL signer signature key', function () {
Artisan::call('generate:url-signer-signature-key --show');

$output = trim(Artisan::output());

expect($output)
->toBeString()
->toHaveLength(64);
});
65 changes: 65 additions & 0 deletions tests/Stubs/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# .env.example from Laravel/Laravel repository (commit: 27907e0)
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_TIMEZONE=UTC
APP_URL=http://localhost

APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US

APP_MAINTENANCE_DRIVER=file
APP_MAINTENANCE_STORE=database

BCRYPT_ROUNDS=12

LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=

SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database

CACHE_STORE=database
CACHE_PREFIX=

MEMCACHED_HOST=127.0.0.1

REDIS_CLIENT=phpredis
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379

MAIL_MAILER=log
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="[email protected]"
MAIL_FROM_NAME="${APP_NAME}"

AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

0 comments on commit 5917499

Please sign in to comment.