Skip to content
This repository has been archived by the owner on Apr 23, 2021. It is now read-only.

Commit

Permalink
Add Laravel 7 compatibility (#12)
Browse files Browse the repository at this point in the history
* Add Laravel 7 compatibility

Co-authored-by: Claudio Dekker <claudiodekker@users.noreply.github.com>
  • Loading branch information
claudiodekker and claudiodekker authored Apr 11, 2020
1 parent 3cdef56 commit 320ac87
Show file tree
Hide file tree
Showing 21 changed files with 291 additions and 137 deletions.
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at https://editorconfig.org

root = true

[*]
charset = utf-8
indent_size = 4
indent_style = space
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false
12 changes: 12 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Path-based git attributes
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html

# Ignore all test and documentation with "export-ignore".
/.editorconfig export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.styleci.yml export-ignore
/.scrutinizer.yml export-ignore
/.travis.yml export-ignore
/phpunit.xml.dist export-ignore
/tests export-ignore
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/vendor/
phpunit.xml
composer.lock
.env
.phpunit.result.cache
composer.lock
phpunit.xml
/vendor
2 changes: 1 addition & 1 deletion .scrutinizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ build:
analysis:
environment:
php:
version: 7.2
version: 7.4
tests:
override:
- php-scrutinizer-run
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ language: php
php:
- 7.2
- 7.3
- 7.4

env:
matrix:
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

All notable changes to `ubient/laravel-pwned-passwords` will be documented in this file

## 2.0.1 - 2020-04-11
- Add support for Laravel 7
- Fixed a bug where an error might be thrown for not being able to reach the Pwned Passwords API.
Instead, the default behaviour now is to accept the password as non-pwned and send a warning to Laravel's Log.
If you would like to override this behaviour, you can [create your own implementation of the LookupErrorHandler and bind it in your application](README.md#handling-lookup-errors).

## 2.0.0 - 2019-09-03
- Drop support for Laravel 5.7 and older
- Add support Laravel 6.0
- Add support for Laravel 6

## 1.1.0 - 2019-02-27
- Drop support for PHP 7.1
Expand Down
29 changes: 21 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ In order to protect the value of the source password being searched for, Pwned P
This works by hashing the source password with SHA-1, and only sending the first 5 characters of that hash to the API.
By checking whether the rest of the SHA-1 hash occurs within the output, we can verify both whether the password was pwned previously, and how frequently.

### Usage
## Installation

You can install the package via composer:

```bash
composer require ubient/laravel-pwned-passwords
```

## Usage

Here's a few short examples of what you can do:

Expand Down Expand Up @@ -59,29 +67,34 @@ $request->validate([
]);
```

## Installation
#### Handling Lookup Errors
When the Pwned Passwords API cannot be queried, the default behavior is to accept the password as non-pwned and to send a warning message to the log.
While this doesn't add much value, it does allow you to be aware of when a pwned password was allowed, and to potentially manually act on this.

You can install the package via composer:
If you would like to automatically do something else based on this lookup error (such as marking the request as potentially pwned), or want to decline the password instead,
you may create your own implementation of the [LookupErrorHandler](src/Contracts/LookupErrorHandler.php) and overwrite the default binding in your application:

```bash
composer require ubient/laravel-pwned-passwords
```php
use Ubient\PwnedPasswords\Contracts\LookupErrorHandler;

$this->app->bind(LookupErrorHandler::class, MyCustomErrorHandler::class);
```

### Testing
## Testing

``` bash
composer test
```

### Changelog
## Changelog

Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.

## Contributing

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

### Security
## Security

If you discover any security related issues, please email claudio@ubient.net instead of using the issue tracker.

Expand Down
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
"require": {
"php": "^7.2",
"guzzlehttp/guzzle": "^6.3",
"illuminate/contracts": "^5.8|^6.0",
"illuminate/support": "^5.8|^6.0"
"illuminate/contracts": "^5.8|^6.0|^7.0",
"illuminate/support": "^5.8|^6.0|^7.0"
},
"require-dev": {
"orchestra/testbench": "^3.8",
"orchestra/testbench": "^3.8|^4.0|^5.0",
"phpunit/phpunit": "^8.0"
},
"autoload": {
Expand Down
7 changes: 6 additions & 1 deletion src/Api/FakeApiGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,22 @@

namespace Ubient\PwnedPasswords\Api;

use Ubient\PwnedPasswords\Contracts\ApiGateway;

class FakeApiGateway implements ApiGateway
{
/**
* Indicates how frequently a password was found to be pwned.
*
* @param string $password
* @throws \RuntimeException
* @return int
*/
public function search(string $password): int
{
if ($password === 'password1') {
throw new \RuntimeException('Simulated network connectivity issue.');
}

return collect([
'P@ssw0rd' => 49938,
'hammertime6' => 5,
Expand Down
17 changes: 9 additions & 8 deletions src/Api/PwnedPasswordsGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

namespace Ubient\PwnedPasswords\Api;

use RuntimeException;
use GuzzleHttp\Client as GuzzleClient;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use GuzzleHttp\Client as GuzzleClient;
use RuntimeException;
use Ubient\PwnedPasswords\Contracts\ApiGateway;

class PwnedPasswordsGateway implements ApiGateway
{
Expand All @@ -18,15 +19,15 @@ class PwnedPasswordsGateway implements ApiGateway
public function search(string $password): int
{
$hash = strtoupper(sha1($password));

$hashPrefix = substr($hash, 0, 5);
$hashSuffix = substr($hash, 5);

return Cache::remember("Ubient\PwnedPasswords::$hashPrefix", 7200, function () use ($hashPrefix, $hashSuffix) {
return $this
->fetchHashes($hashPrefix)
->get($hashSuffix, 0);
/** @var Collection $hashes */
$hashes = Cache::remember("Ubient\PwnedPasswords::$hashPrefix", 7200, function () use ($hashPrefix) {
return $this->fetchHashes($hashPrefix);
});

return $hashes->get($hashSuffix, 0);
}

/**
Expand All @@ -36,7 +37,7 @@ public function search(string $password): int
* and the value is the amount of times the password was pwned.
*
* @param string $hashPrefix
* @throws \RuntimeException
* @throws RuntimeException
* @return Collection
*/
protected function fetchHashes(string $hashPrefix): Collection
Expand Down
3 changes: 1 addition & 2 deletions src/Api/ApiGateway.php → src/Contracts/ApiGateway.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<?php

namespace Ubient\PwnedPasswords\Api;
namespace Ubient\PwnedPasswords\Contracts;

interface ApiGateway
{
/**
* Indicates how frequently a password was found to be pwned.
*
* @param string $password
* @throws \RuntimeException
* @return int
*/
public function search(string $password): int;
Expand Down
18 changes: 18 additions & 0 deletions src/Contracts/LookupErrorHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace Ubient\PwnedPasswords\Contracts;

use Throwable;

interface LookupErrorHandler
{
/**
* Handles errors that occur during the lookup of a potentially Pwned Password.
* Returns a boolean indicating whether the unverified password is accepted.
*
* @param Throwable $exception
* @param string $password
* @return bool
*/
public function handle(Throwable $exception, string $password): bool;
}
28 changes: 28 additions & 0 deletions src/Handler/LogHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Ubient\PwnedPasswords\Handler;

use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;
use Throwable;
use Ubient\PwnedPasswords\Contracts\LookupErrorHandler;

class LogHandler implements LookupErrorHandler
{
/**
* Handles errors that occur during the lookup of a potentially Pwned Password.
* Returns a boolean indicating whether the unverified password is accepted.
*
* @param Throwable $exception
* @param string $password
* @return bool
*/
public function handle(Throwable $exception, string $password): bool
{
Log::warning('A password was marked as non-pwned due to issues during lookup.', [
'hash' => Hash::make($password),
]);

return true;
}
}
9 changes: 6 additions & 3 deletions src/PwnedPasswordsServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace Ubient\PwnedPasswords;

use Ubient\PwnedPasswords\Rules\Pwned;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
use Ubient\PwnedPasswords\Api\ApiGateway;
use Illuminate\Support\ServiceProvider;
use Ubient\PwnedPasswords\Api\PwnedPasswordsGateway;
use Ubient\PwnedPasswords\Contracts\ApiGateway;
use Ubient\PwnedPasswords\Contracts\LookupErrorHandler;
use Ubient\PwnedPasswords\Handler\LogHandler;
use Ubient\PwnedPasswords\Rules\Pwned;

class PwnedPasswordsServiceProvider extends ServiceProvider
{
Expand All @@ -21,5 +23,6 @@ public function boot()
public function register()
{
$this->app->bind(ApiGateway::class, PwnedPasswordsGateway::class);
$this->app->bind(LookupErrorHandler::class, LogHandler::class);
}
}
17 changes: 14 additions & 3 deletions src/Rules/Pwned.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace Ubient\PwnedPasswords\Rules;

use Illuminate\Contracts\Validation\Rule;
use Ubient\PwnedPasswords\Api\ApiGateway;
use Throwable;
use Ubient\PwnedPasswords\Contracts\ApiGateway;
use Ubient\PwnedPasswords\Contracts\LookupErrorHandler;

class Pwned implements Rule
{
Expand All @@ -25,6 +27,7 @@ class Pwned implements Rule
/**
* Create a new rule instance.
*
* @param int $threshold
* @return void
*/
public function __construct(int $threshold = 1)
Expand All @@ -42,15 +45,23 @@ public function __construct(int $threshold = 1)
*/
public function passes($attribute, $value)
{
return $this->gateway->search($value) < $this->threshold;
try {
return $this->gateway->search($value) < $this->threshold;
} catch (Throwable $exception) {
return app(LookupErrorHandler::class)->handle($exception, $value);
}
}

/**
* Determine if the extended validation rule passes.
*
* @see https://laravel.com/docs/5.7/validation#using-extensions
* @param $attribute
* @param $value
* @param $parameters
* @return bool
*/
public function validate($attribute, $value, $parameters, $validator)
public function validate($attribute, $value, $parameters)
{
$this->threshold = (int) (array_shift($parameters) ?? 1);

Expand Down
8 changes: 8 additions & 0 deletions tests/Feature/PwnedPasswordsServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Ubient\PwnedPasswords\Tests\Feature;

use Illuminate\Support\Facades\Validator;
use Ubient\PwnedPasswords\Contracts\LookupErrorHandler;
use Ubient\PwnedPasswords\Handler\LogHandler;
use Ubient\PwnedPasswords\Tests\TestCase;

class PwnedPasswordsServiceProviderTest extends TestCase
Expand All @@ -17,4 +19,10 @@ public function it_should_register_the_validation_error_message_for_our_pwned_ru
$errorMessage
);
}

/** @test */
public function it_registers_the_log_handler_with_the_application()
{
$this->assertInstanceOf(LogHandler::class, app(LookupErrorHandler::class));
}
}
Loading

0 comments on commit 320ac87

Please sign in to comment.