Initial commit.

This commit is contained in:
Marcus Olsson 2015-10-16 12:15:46 +02:00
commit 768720a83a
10 changed files with 519 additions and 0 deletions

15
.editorconfig Normal file
View File

@ -0,0 +1,15 @@
; This file is for unifying the coding style for different editors and IDEs.
; More information at http://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

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/vendor
composer.lock

8
.travis.yml Normal file
View File

@ -0,0 +1,8 @@
language: php
php:
- 5.5
- 5.6
- 7.0
- nightly
before_script: composer install

21
LICENSE.md Normal file
View File

@ -0,0 +1,21 @@
# The MIT License (MIT)
Copyright (c) 2015 Marcus Olsson <hello@marcusolsson.me>
> Permission is hereby granted, free of charge, to any person obtaining a copy
> of this software and associated documentation files (the "Software"), to deal
> in the Software without restriction, including without limitation the rights
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> copies of the Software, and to permit persons to whom the Software is
> furnished to do so, subject to the following conditions:
>
> The above copyright notice and this permission notice shall be included in
> all copies or substantial portions of the Software.
>
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> THE SOFTWARE.

152
README.md Normal file
View File

@ -0,0 +1,152 @@
# Zxcvbn for Laravel 5
[![Latest Version on Packagist][ico-version]][link-packagist]
[![Software License][ico-license]](LICENSE.md)
[![Build Status][ico-travis]][link-travis]
A simple implementation of zxcvbn for Laravel 5. This package allows you to access "zxcvbn-related" data on a passphrase in the application, but also use zxcvbn as a standard validator.
Uses [Zxcvbn-PHP](https://github.com/bjeavons/zxcvbn-php) by [@bjeavons](https://github.com/bjeavons), which in turn is inspired by [zxcvbn](https://github.com/dropbox/zxcvbn) by [@dropbox](https://github.com/dropbox).
## Install
Via Composer
``` bash
$ composer require olssonm/l5-zxcvbn
```
Add the package to your providers array:
``` php
'providers' => [
Olssonm\Zxcvbn\ZxcvbnServiceProvider::class,
]
```
If you wish to have the ability to use `Zxcvbn` via dependency injection, or just have a quick way to access the class add an alias to the facades:
``` php
'aliases' => [
'Zxcvbn' => Olssonm\Zxcvbn\Facades\Zxcvbn::class
]
```
## Usage
If you've added `Olssonm\Zxcvbn` as an alias, your can access Zxcvbn easily from anywhere in your application:
### "In app"
``` php
<?php
use Zxcvbn;
class MyClass
{
public function myFunction()
{
$zxcvbn = Zxcvbn::passwordStrength('password');;
dd($zxcvbn->passwordStrength('password'));
// array:6 [
// "crack_time" => 5.0E-5
// "calc_time" => 0.1857271194458
// "password" => "password"
// "entropy" => 0.0
// "match_sequence" => array:1 []
// "score" => 0
// ]
}
}
?>
```
Play around with different passwords and phrases, the results may surprise you. Check out [Zxcvbn-PHP](https://github.com/bjeavons/zxcvbn-php) for more uses and examples.
### As a validator
The package gives you two different validation rules that you may use; `zxcvbn_min` and `zxcvbn_dictionary`.
#### zxcvbn_min
`zxcvbn_min` allows you to set up a rule for minimum score that the value beeing tested should adhere to.
***Syntax***
`'input' => 'zxcvbn_min:min_value'`
``` php
<?php
$data = ['password' => 'password'];
$validator = Validator::make($data, [
'password' => 'zxcvbn_min:3|required',
], [
'password.zxcvbn_min' => 'Your password is not strong enough!'
]);
```
In this example the password should at least have a "score" of three (3) to pass the validation. Of course, you should probably use the zxcvbn-library on the front-end too to allow the user to know this before posting the form...
#### zxcvbn_dictionary
This is a bit more interesting. `zxcvbn_dictionary` allows you to input both the users username and/or email, and their password. The validator checks that the password doesn't exist in the username, or that they are too similar.
***Syntax***
`'input' => 'xcvbn_dictionary:username,email'`
``` php
<?php
/**
* Example 1, pass
*/
$password = '31??2sa//"dhjd2askjd19sad19!!&!#"';
$data = [
'username' => 'user',
'email' => 'trash@thedumpster.com'
];
$validator = Validator::make($password, [
'password' => 'zxcvbn_dictionary:' . $data['username'] . ',' . $data['email'] . '|required',
]);
dd($validator->passes());
// true
/**
* Example 2, fail
*/
$password = 'mycomplicatedphrase';
$data = [
'username' => 'mycomplicatedphrase',
'email' => 'mycomplicatedphrase@thedumpster.com'
];
$validator = Validator::make($password, [
'password' => 'zxcvbn_dictionary:' . $data['username'] . ',' . $data['email'] . '|required',
]);
dd($validator->passes());
// false
```
## Testing
``` bash
$ composer test
```
or
``` bash
$ phpunit
```
## License
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
© 2015 [Marcus Olsson](https://marcusolsson.me).
[ico-version]: https://img.shields.io/packagist/v/olssonm/l5-zxcvbn.svg?style=flat-square
[ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
[ico-travis]: https://img.shields.io/travis/olssonm/l5-zxcvbn/master.svg?style=flat-square
[link-packagist]: https://packagist.org/packages/olssonm/l5-zxcvbn
[link-travis]: https://travis-ci.org/olssonm/l5-zxcvbn

50
composer.json Normal file
View File

@ -0,0 +1,50 @@
{
"name": "olssonm/l5-zxcvbn",
"description": "Implementation of the zxcvbn project by @dropbox for Laravel 5. Uses zxcvbn-php by @bjeavons.",
"keywords": [
"olssonm",
"zxcvbn",
"staple horse battery",
"passwords",
"validation",
"laravel"
],
"homepage": "https://github.com/olssonm/l5-zxcvbn",
"license": "MIT",
"authors": [
{
"name": "Marcus Olsson",
"email": "hello@marcusolsson.me",
"homepage": "https://marcusolsson.me"
}
],
"require": {
"php" : ">=5.3.0",
"illuminate/support": "~5.1",
"bjeavons/zxcvbn-php": "0.1.4"
},
"require-dev": {
"phpunit/phpunit" : "4.*",
"orchestra/testbench": "~3.0",
"scrutinizer/ocular": "~1.1"
},
"autoload": {
"psr-4": {
"Olssonm\\Zxcvbn\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Olssonm\\Zxcvbn\\Test\\": "tests/"
}
},
"scripts": {
"test": "phpunit"
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"minimum-stability": "stable"
}

18
phpunit.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./tests/</directory>
</testsuite>
</testsuites>
</phpunit>

18
src/Facades/Zxcvbn.php Normal file
View File

@ -0,0 +1,18 @@
<?php
namespace Olssonm\Zxcvbn\Facades;
use Illuminate\Support\Facades\Facade;
class Zxcvbn extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'zxcvbn';
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace Olssonm\Zxcvbn;
use ZxcvbnPhp\Zxcvbn as ZxcvbnPhp;
use Illuminate\Support\ServiceProvider;
use Validator;
class ZxcvbnServiceProvider extends ServiceProvider
{
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
/**
* Extend the Laravel Validator with the "zxcvbn_min" rule
*/
Validator::extend('zxcvbn_min', function($attribute, $value, $parameters, $validator) {
$zxcvbn = new ZxcvbnPhp();
$zxcvbn = $zxcvbn->passwordStrength($value);
$target = 5;
if(isset($parameters[0])) {
$target = $parameters[0];
}
return ($zxcvbn['score'] >= $target);
}, 'Your :input is not secure enough.');
Validator::replacer('zxcvbn_min', function($message, $attribute, $rule, $parameters) {
$message = str_replace(':input', $attribute, $message);
return $message;
});
/**
* Extend the Laravel Validator with the "zxcvbn_min" rule
*/
Validator::extend('zxcvbn_dictionary', function($attribute, $value, $parameters, $validator) {
$email = null;
$username = null;
if(isset($parameters[0])) {
$email = $parameters[0];
$username = $parameters[1];
}
$zxcvbn = new ZxcvbnPhp();
$zxcvbn = $zxcvbn->passwordStrength($value, [$username, $email]);
if(isset($zxcvbn['match_sequence'][0])) {
$dictionary = $zxcvbn['match_sequence'][0];
if(isset($dictionary->dictionaryName)) {
return false;
}
}
return true;
}, 'Your :input is insecure. It either matches a commonly used password, or you have used a similar username/password combination.');
Validator::replacer('zxcvbn_dictionary', function($message, $attribute, $rule, $parameters) {
$message = str_replace(':input', $attribute, $message);
return $message;
});
}
/**
* Register any package services.
*
* @return void
*/
public function register()
{
$this->app['zxcvbn'] = $this->app->share(function ($app) {
return new ZxcvbnPhp($this->app['config']);
});
}
}

154
tests/ZxcvbnTest.php Normal file
View File

@ -0,0 +1,154 @@
<?php namespace Olssonm\Zxcvbn\Tests;
use Validator;
use Zxcvbn;
class ZxcvbnTest extends \Orchestra\Testbench\TestCase {
public function setUp() {
parent::setUp();
}
/**
* Load the package
* @return array the packages
*/
protected function getPackageProviders($app)
{
return [
'Olssonm\Zxcvbn\ZxcvbnServiceProvider'
];
}
/**
* Load the alias
* @return array the aliases
*/
protected function getPackageAliases($app)
{
return [
'Zxcvbn' => 'Olssonm\Zxcvbn\Facades\Zxcvbn'
];
}
/**
* Just run som standard tests to see that Zxcvbn is up to snuff and working
* @test
*/
public function test_zxcvbn_basics()
{
$testVar1 = Zxcvbn::passwordStrength('test');
// Check keys
$this->assertArrayHasKey('score', $testVar1);
$this->assertArrayHasKey('match_sequence', $testVar1);
$this->assertArrayHasKey('entropy', $testVar1);
$this->assertArrayHasKey('password', $testVar1);
$this->assertArrayHasKey('calc_time', $testVar1);
$this->assertArrayHasKey('crack_time', $testVar1);
// Check score-value
$this->assertEquals(0, $testVar1['score']);
// Run some more tests
$testVar2 = Zxcvbn::passwordStrength('dadaurka');
$testVar3 = Zxcvbn::passwordStrength('staple horse battery');
$testVar4 = Zxcvbn::passwordStrength('7E6k9axB*gwGHa&aZTohmD9Wr&NVs[b4'); //<-- 32
// Check score-value
$this->assertEquals(1, $testVar2['score']);
$this->assertEquals(4, $testVar3['score']);
$this->assertEquals(4, $testVar4['score']);
}
/** @test */
public function test_password_strength()
{
// Standard tests
$this->assertEquals(true, $this->validate_without_message_min('test', 0));
$this->assertEquals(false, $this->validate_without_message_min('test', 4));
$this->assertEquals(true, $this->validate_without_message_min('staple horse battery', 3));
$this->assertEquals(true, $this->validate_without_message_min('staple horse battery', 4));
$this->assertEquals(false, $this->validate_without_message_min('staple horse battery', 5));
}
/** @test */
public function test_password_strength_with_message()
{
// Standard message
$this->assertEquals('Your password is not secure enough.', $this->validate_with_message_min('staple horse battery', 5, null));
$this->assertEquals('Just a message', $this->validate_with_message_min('test', 4, 'Just a message'));
}
/** @test */
public function test_password_dictionary()
{
// Standard tests
$this->assertEquals(false, $this->validate_without_message_dictionary('password', 'test@test.com', 'test'));
$this->assertEquals(false, $this->validate_without_message_dictionary('test', 'test@test.com', 'test'));
$this->assertEquals(false, $this->validate_without_message_dictionary('721ahsa!', '721ahsa@test.com', '721ahsa'));
$this->assertEquals(true, $this->validate_without_message_dictionary('721ahsa!', 'dadaurka@test.com', 'dadaurka'));
$this->assertEquals(true, $this->validate_without_message_dictionary('asd912j!', 'myemail@test.com', 'username'));
$this->assertEquals(true, $this->validate_without_message_dictionary('asd912j!', 'trash@thedumpster.com', 'username'));
$this->assertEquals(true, $this->validate_without_message_dictionary('asd912j!', null, 'username'));
$this->assertEquals(true, $this->validate_without_message_dictionary('asd912j!', null, null));
}
/** @test */
public function test_password_dictionary_with_message()
{
// Standard message
$this->assertEquals('Your password is insecure. It either matches a commonly used password, or you have used a similar username/password combination.', $this->validate_with_message_dictionary('password', 'test@test.com', 'test', null));
$this->assertEquals('Just a message', $this->validate_with_message_dictionary('test', 'test@test.com', 'test', 'Just a message'));
}
private function validate_without_message_min($password, $min)
{
$data = ['password' => $password];
$validator = Validator::make($data, [
'password' => 'zxcvbn_min:' . $min . '|required',
]);
return $validator->passes();
}
private function validate_with_message_min($password, $min, $message)
{
$data = ['password' => $password];
$validator = Validator::make($data, [
'password' => 'zxcvbn_min:' . $min . '|required',
], [
'password.zxcvbn_min' => $message
]);
$errors = $validator->errors();
return $errors->first('password');
}
private function validate_without_message_dictionary($password, $email, $username)
{
$data = ['password' => $password];
$validator = Validator::make($data, [
'password' => 'zxcvbn_dictionary:' . $username . ',' . $email . '|required',
]);
return $validator->passes();
}
private function validate_with_message_dictionary($password, $email, $username, $message)
{
$data = ['password' => $password];
$validator = Validator::make($data, [
'password' => 'zxcvbn_dictionary:' . $username . ',' . $email . '|required',
], [
'password.zxcvbn_dictionary' => $message
]);
$errors = $validator->errors();
return $errors->first('password');
}
}