And a commit that I don't know the reason of...
This commit is contained in:
14
.gitlab-ci.yml
Normal file
14
.gitlab-ci.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
stages:
|
||||
- deploy
|
||||
|
||||
deploy:
|
||||
stage: deploy
|
||||
image: alpine:latest
|
||||
only:
|
||||
- tags
|
||||
tags:
|
||||
- build
|
||||
script:
|
||||
- apk add curl
|
||||
- 'curl -iL --header "Job-Token: $CI_JOB_TOKEN" --data tag=${CI_COMMIT_TAG} "${CI_API_V4_URL}/projects/$CI_PROJECT_ID/packages/composer"'
|
||||
environment: production
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 siteworxpro
|
||||
|
||||
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.
|
139
README.md
Normal file
139
README.md
Normal file
@@ -0,0 +1,139 @@
|
||||
## Password Strength Tester
|
||||
|
||||
[](https://travis-ci.org/siteworxpro/password-strength-tool)
|
||||
|
||||
A simple and fast password strength tester inspired by WolframAlpha's Calculation
|
||||
|
||||
**Usage**
|
||||
|
||||
```php
|
||||
$scorer = \Siteworx\Passwords\Scorer::score('SuperPassword!');
|
||||
|
||||
echo json_encode($scorer->toArray());
|
||||
|
||||
```
|
||||
|
||||
will output and is great for an ajax endpoint
|
||||
|
||||
```json
|
||||
{
|
||||
"calculatedData": {
|
||||
"length": {
|
||||
"value": 56,
|
||||
"count": 14,
|
||||
"displayName": "Length"
|
||||
},
|
||||
"upperCase": {
|
||||
"value": 13,
|
||||
"count": 2,
|
||||
"displayName": "Uppercase Letters"
|
||||
},
|
||||
"lowerCase": {
|
||||
"value": 17,
|
||||
"count": 11,
|
||||
"displayName": "Lowercase Letters"
|
||||
},
|
||||
"numbers": {
|
||||
"value": 0,
|
||||
"count": 0,
|
||||
"displayName": "Numbers"
|
||||
},
|
||||
"specialChars": {
|
||||
"value": 13,
|
||||
"count": 1,
|
||||
"displayName": "Special Characters"
|
||||
},
|
||||
"numbersOnly": {
|
||||
"value": 0,
|
||||
"count": "no",
|
||||
"displayName": "Numbers Only"
|
||||
},
|
||||
"lettersOnly": {
|
||||
"value": -10,
|
||||
"count": "yes",
|
||||
"displayName": "Letters Only"
|
||||
},
|
||||
"repeatingChars": {
|
||||
"value": -1,
|
||||
"count": 1,
|
||||
"displayName": "Repeating Characters"
|
||||
},
|
||||
"reusingChars": {
|
||||
"value": -4,
|
||||
"count": 2,
|
||||
"displayName": "Reusing Characters"
|
||||
},
|
||||
"conscUpperCase": {
|
||||
"value": -8,
|
||||
"count": 4,
|
||||
"displayName": "Consecutive Upper Case"
|
||||
},
|
||||
"conscLowerCase": {
|
||||
"value": -26,
|
||||
"count": 13,
|
||||
"displayName": "Consecutive Lower Case"
|
||||
},
|
||||
"conscNumbers": {
|
||||
"value": 0,
|
||||
"count": 0,
|
||||
"displayName": "Consecutive Numbers"
|
||||
},
|
||||
"seqLetters": {
|
||||
"value": 0,
|
||||
"count": 0,
|
||||
"displayName": "Sequential Letters"
|
||||
},
|
||||
"seqNumbers": {
|
||||
"value": 0,
|
||||
"count": 0,
|
||||
"displayName": "Sequential Numbers"
|
||||
}
|
||||
},
|
||||
"total": 50,
|
||||
"strength": {
|
||||
"text_value": "Poor",
|
||||
"int_value": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also access several helper methods
|
||||
|
||||
```php
|
||||
if ($scorer->isExcellent()) {
|
||||
echo 'Your password is excellent :)';
|
||||
}
|
||||
if ($scorer->isPoor()) {
|
||||
echo 'Your password is poor :(';
|
||||
}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```php
|
||||
echo 'Your Password is ' . $scorer->stringValue();
|
||||
```
|
||||
```
|
||||
Your Password is Poor
|
||||
```
|
||||
|
||||
**Bias**
|
||||
|
||||
You can also provide a bias to the scorer if you want your passwords to be scored higher or lower.
|
||||
|
||||
Pass in a value between -5 to 5.
|
||||
|
||||
```php
|
||||
$scorer = \Siteworx\Passwords\Scorer::score('N0wAStrongPassword!', -5);
|
||||
echo 'This password is ' . $scorer->stringValue();
|
||||
$scorer = \Siteworx\Passwords\Scorer::score('N0wAStrongPassword!', 0);
|
||||
echo 'This password is ' . $scorer->stringValue();
|
||||
$scorer = \Siteworx\Passwords\Scorer::score('N0wAStrongPassword!', 5);
|
||||
echo 'This password is ' . $scorer->stringValue();
|
||||
|
||||
```
|
||||
```
|
||||
This password is Poor
|
||||
This password is Fair
|
||||
This password is Very Strong
|
||||
```
|
15
composer.json
Normal file
15
composer.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "siteworxpro/password-score",
|
||||
"description": "Find a passwords strength using more complex methods",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Siteworx\\Passwords\\": "src/"
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
}
|
||||
}
|
22
phpunit.xml
Normal file
22
phpunit.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
|
||||
backupGlobals="false"
|
||||
colors="true"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
|
||||
<php>
|
||||
<ini name="error_reporting" value="-1" />
|
||||
<ini name="intl.default_locale" value="en" />
|
||||
<ini name="intl.error_level" value="0" />
|
||||
<ini name="memory_limit" value="-1" />
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Default">
|
||||
<directory suffix='Test.php'>./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
576
src/Scorer.php
Normal file
576
src/Scorer.php
Normal file
@@ -0,0 +1,576 @@
|
||||
<?php
|
||||
|
||||
namespace Siteworx\Passwords;
|
||||
|
||||
/**
|
||||
* Class Scorer
|
||||
*
|
||||
* @package App\Library\Utilities
|
||||
*/
|
||||
class Scorer
|
||||
{
|
||||
|
||||
private const EXCELLENT_SCORE = 120;
|
||||
private const VERY_STRONG_SCORE = 100;
|
||||
private const STRONG_SCORE = 80;
|
||||
private const FAIR_SCORE = 55;
|
||||
private const POOR_SCORE = 25;
|
||||
|
||||
private const VERY_POOR = 0;
|
||||
private const POOR = 1;
|
||||
private const FAIR = 2;
|
||||
private const STRONG = 3;
|
||||
private const VERY_STRONG = 4;
|
||||
private const EXCELLENT = 5;
|
||||
|
||||
private $bias = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $password;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $passwordScore = [
|
||||
'calculatedData' => [],
|
||||
'total' => 0,
|
||||
'strength' => [
|
||||
'text_value' => '',
|
||||
'int_value' => 1
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* Scorer constructor.
|
||||
* @param null $p
|
||||
*/
|
||||
public function __construct($p = null)
|
||||
{
|
||||
$this->password = $p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $password
|
||||
* @param int -5 to 5 range of a bias to apply to passwords
|
||||
* @return Scorer
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function score(string $password, int $bias = 0): Scorer
|
||||
{
|
||||
$scorer = new static($password);
|
||||
$scorer->setBias($bias);
|
||||
$scorer->scorePassword();
|
||||
|
||||
return $scorer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->passwordScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getScore(): int
|
||||
{
|
||||
return $this->passwordScore['total'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function stringValue(): string
|
||||
{
|
||||
return $this->passwordScore['strength']['text_value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function intValue(): int
|
||||
{
|
||||
return $this->passwordScore['strength']['int_value'];
|
||||
}
|
||||
|
||||
public function isExcellent(): bool
|
||||
{
|
||||
return $this->passwordScore['strength']['int_value'] === self::EXCELLENT;
|
||||
}
|
||||
|
||||
public function isVeryStrong(): bool
|
||||
{
|
||||
return $this->passwordScore['strength']['int_value'] === self::VERY_STRONG;
|
||||
}
|
||||
|
||||
public function isStrong(): bool
|
||||
{
|
||||
return $this->passwordScore['strength']['int_value'] === self::STRONG;
|
||||
}
|
||||
|
||||
public function isFair(): bool
|
||||
{
|
||||
return $this->passwordScore['strength']['int_value'] === self::FAIR;
|
||||
}
|
||||
|
||||
public function isPoor(): bool
|
||||
{
|
||||
return $this->passwordScore['strength']['int_value'] === self::POOR;
|
||||
}
|
||||
|
||||
public function isVeryPoor(): bool
|
||||
{
|
||||
return $this->passwordScore['strength']['int_value'] === self::VERY_POOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $bias
|
||||
* @throws \LogicException
|
||||
*/
|
||||
private function setBias(int $bias): void
|
||||
{
|
||||
if ($bias > 5 || $bias < -5) {
|
||||
throw new \LogicException('Bias is a value of -5 to positive 5');
|
||||
}
|
||||
$this->bias = round(($bias ** 2) / 3 * $bias);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function scorePassword(): void
|
||||
{
|
||||
if ($this->password === null) {
|
||||
throw new \InvalidArgumentException('Password Data Not Set!');
|
||||
}
|
||||
|
||||
$this->checkLength();
|
||||
$this->countUpperCase();
|
||||
$this->countLowerCase();
|
||||
$this->countNumbers();
|
||||
$this->countSpecialChars();
|
||||
$this->checkNumbersOnly();
|
||||
$this->checkLettersOnly();
|
||||
$this->checkRepeatingChars();
|
||||
$this->checkReusingChars();
|
||||
$this->checkConsecUpperCase();
|
||||
$this->checkConscLowerCase();
|
||||
$this->checkConsecNumbers();
|
||||
$this->checkSeqLetters();
|
||||
$this->checkSeqNumbers();
|
||||
|
||||
$total = $this->bias;
|
||||
foreach ($this->passwordScore['calculatedData'] as $score) {
|
||||
$total += $score['value'];
|
||||
}
|
||||
$this->passwordScore['total'] = $total;
|
||||
$this->passwordScore['strength'] = $this->classifyPasswordScore($total);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the basic Very Poor -> Excellent value of the score
|
||||
*
|
||||
* @param int $score The value of the password score
|
||||
* @return array The int value and the text value of the score
|
||||
*/
|
||||
private function classifyPasswordScore($score): array
|
||||
{
|
||||
|
||||
if ($score >= self::EXCELLENT_SCORE) {
|
||||
return [
|
||||
'text_value' => 'Excellent',
|
||||
'int_value' => self::EXCELLENT
|
||||
];
|
||||
}
|
||||
|
||||
if ($score >= self::VERY_STRONG_SCORE && $score < self::EXCELLENT_SCORE) {
|
||||
return [
|
||||
'text_value' => 'Very Strong',
|
||||
'int_value' => self::VERY_STRONG
|
||||
];
|
||||
}
|
||||
|
||||
if ($score >= self::STRONG_SCORE && $score < self::VERY_STRONG_SCORE) {
|
||||
return [
|
||||
'text_value' => 'Strong',
|
||||
'int_value' => self::STRONG
|
||||
];
|
||||
}
|
||||
|
||||
if ($score >= self::FAIR_SCORE && $score < self::STRONG_SCORE) {
|
||||
return [
|
||||
'text_value' => 'Fair',
|
||||
'int_value' => self::FAIR
|
||||
];
|
||||
}
|
||||
|
||||
if ($score >= self::POOR_SCORE && $score < self::FAIR_SCORE) {
|
||||
return [
|
||||
'text_value' => 'Poor',
|
||||
'int_value' => self::POOR
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'text_value' => 'Very Poor',
|
||||
'int_value' => self::VERY_POOR
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for letters that are sequential
|
||||
* ie .. abcd <- BAD
|
||||
* bdca <- GOOD
|
||||
* Liner Grading
|
||||
* -2 points per incident
|
||||
*/
|
||||
private function checkSeqLetters(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['seqLetters']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['seqLetters']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['seqLetters']['displayName'] = 'Sequential Letters';
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$lastChar = $this->password[$i - 1];
|
||||
if ($char !== '' && !is_numeric($char) && !is_numeric($lastChar)) {
|
||||
$diff = \ord($char) - \ord($lastChar);
|
||||
if ($diff === 1 || $diff === -1) {
|
||||
$this->passwordScore['calculatedData']['seqLetters']['value'] -= 2;
|
||||
$this->passwordScore['calculatedData']['seqLetters']['count'] += 1;
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for numbers that are sequential
|
||||
* ie .. 1234 <- BAD
|
||||
* 4213 <- GOOD
|
||||
* Liner Grading
|
||||
* -2 points per incident
|
||||
*/
|
||||
private function checkSeqNumbers(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['seqNumbers']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['seqNumbers']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['seqNumbers']['displayName'] = 'Sequential Numbers';
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$lastChar = $this->password[$i - 1];
|
||||
if ($char !== '' && is_numeric($char) && is_numeric($lastChar)) {
|
||||
$diff = \ord($char) - \ord($lastChar);
|
||||
if ($diff === 1 || $diff === -1) {
|
||||
$this->passwordScore['calculatedData']['seqNumbers']['value'] -= 2;
|
||||
$this->passwordScore['calculatedData']['seqNumbers']['count'] += 1;
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for consecutive use of numbers
|
||||
* ie ...
|
||||
* adg552 <- 55 is BAD
|
||||
* adb526 <- GOOD
|
||||
* Liner Grading
|
||||
* -2 per incident
|
||||
*/
|
||||
private function checkConsecNumbers(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['conscNumbers']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['conscNumbers']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['conscNumbers']['displayName'] = 'Consecutive Numbers';
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$lastChar = $this->password[$i - 1];
|
||||
if (is_numeric($char)) {
|
||||
if (is_numeric($lastChar)) {
|
||||
$this->passwordScore['calculatedData']['conscNumbers']['value'] -= 2;
|
||||
$this->passwordScore['calculatedData']['conscNumbers']['count'] += 1;
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for consecutive use of lower case
|
||||
* ie ...
|
||||
* AdvRffT <- ff is BAD
|
||||
* AdvRf4T <- GOOD
|
||||
* Liner Grading
|
||||
* -2 per incident
|
||||
*/
|
||||
private function checkConscLowerCase(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['conscLowerCase']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['conscLowerCase']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['conscLowerCase']['displayName'] = 'Consecutive Lower Case';
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$charDec = \ord($char);
|
||||
$lastChar = $this->password[$i - 1];
|
||||
$charDecLast = \ord($lastChar);
|
||||
if (($charDec <= 122 && $charDec >= 97) || ($charDecLast <= 122 && $charDecLast >= 97)) {
|
||||
$this->passwordScore['calculatedData']['conscLowerCase']['value'] -= 2;
|
||||
$this->passwordScore['calculatedData']['conscLowerCase']['count'] += 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for consecutive use of lower case
|
||||
* ie ...
|
||||
* AdvRRfT <- RR is BAD
|
||||
* AdvRf4T <- GOOD
|
||||
* Liner Grading
|
||||
* -2 per incident
|
||||
*/
|
||||
private function checkConsecUpperCase(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['conscUpperCase']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['conscUpperCase']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['conscUpperCase']['displayName'] = 'Consecutive Upper Case';
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$charDec = \ord($char);
|
||||
$lastChar = $this->password[$i - 1];
|
||||
$charDecLast = \ord($lastChar);
|
||||
if (($charDec <= 90 && $charDec >= 65) || ($charDecLast <= 90 && $charDecLast >= 65)) {
|
||||
$this->passwordScore['calculatedData']['conscUpperCase']['value'] += -2;
|
||||
$this->passwordScore['calculatedData']['conscUpperCase']['count'] += 1;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks entire password for chars used twice or more in a row
|
||||
* ie ...
|
||||
* AdvRffT <- ff is BAD
|
||||
* AdvRf4T <- GOOD
|
||||
* Exponential Grading
|
||||
* -1 per incident to the square
|
||||
*/
|
||||
private function checkRepeatingChars(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['repeatingChars']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['repeatingChars']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['repeatingChars']['displayName'] = 'Repeating Characters';
|
||||
$iterations = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$charDec = \ord($char);
|
||||
$lastChar = $this->password[$i - 1];
|
||||
$charDecLast = \ord($lastChar);
|
||||
if ($charDec === $charDecLast) {
|
||||
$this->passwordScore['calculatedData']['repeatingChars']['value'] -= (ceil(($iterations ** 2) / 5) + 1);
|
||||
$this->passwordScore['calculatedData']['repeatingChars']['count'] += 1;
|
||||
$iterations++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for consecutive RE-use of chars
|
||||
* ie ...
|
||||
* AdvRffT <- ff is BAD
|
||||
* AdvRf4T <- GOOD
|
||||
* Exponential Grading
|
||||
* kla;sdjlfgkd <- 'd' and 'l' would be docked for being used twice
|
||||
*/
|
||||
private function checkReusingChars(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['reusingChars']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['reusingChars']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['reusingChars']['displayName'] = 'Reusing Characters';
|
||||
while ($i < \strlen($this->password)) {
|
||||
foreach (count_chars($this->password, 1) as $i => $val) {
|
||||
if ($val > 1) {
|
||||
$this->passwordScore['calculatedData']['reusingChars']['value'] -= $val;
|
||||
$this->passwordScore['calculatedData']['reusingChars']['count'] += 1;
|
||||
}
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if PW is only numbers
|
||||
* ie ..
|
||||
* 123498774 <- BAD
|
||||
*
|
||||
* Flat dock of -10
|
||||
*/
|
||||
private function checkNumbersOnly(): void
|
||||
{
|
||||
$this->passwordScore['calculatedData']['numbersOnly']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['numbersOnly']['count'] = 'no';
|
||||
$this->passwordScore['calculatedData']['numbersOnly']['displayName'] = 'Numbers Only';
|
||||
|
||||
if (is_numeric($this->password)) {
|
||||
$this->passwordScore['calculatedData']['numbersOnly']['value'] = -10;
|
||||
$this->passwordScore['calculatedData']['numbersOnly']['count'] = 'yes';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if PW is only letters upper or lower
|
||||
* ie ..
|
||||
* DFsdffwd <- BAD
|
||||
*
|
||||
* Flat dock of -10
|
||||
*/
|
||||
private function checkLettersOnly(): void
|
||||
{
|
||||
$this->passwordScore['calculatedData']['lettersOnly']['value'] = -10;
|
||||
$this->passwordScore['calculatedData']['lettersOnly']['count'] = 'yes';
|
||||
$this->passwordScore['calculatedData']['lettersOnly']['displayName'] = 'Letters Only';
|
||||
|
||||
$i = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
if (is_numeric($this->password[$i])) {
|
||||
$this->passwordScore['calculatedData']['lettersOnly']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['lettersOnly']['count'] = 'no';
|
||||
return;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
private function countUpperCase(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['upperCase']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['upperCase']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['upperCase']['displayName'] = 'Uppercase Letters';
|
||||
$iterations = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$charDec = \ord($char);
|
||||
if ($charDec <= 90 && $charDec >= 65) {
|
||||
$this->passwordScore['calculatedData']['upperCase']['value'] += ceil(8 * (.6 ** $iterations));
|
||||
$this->passwordScore['calculatedData']['upperCase']['count'] += 1;
|
||||
$iterations++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts and scores lower case
|
||||
*
|
||||
* Scored by and exponential decay
|
||||
* starts at 3 points per char and goes down by .7 to the iterations
|
||||
*/
|
||||
private function countLowerCase(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['lowerCase']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['lowerCase']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['lowerCase']['displayName'] = 'Lowercase Letters';
|
||||
$iterations = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$charDec = \ord($char);
|
||||
if ($charDec <= 122 && $charDec >= 97) {
|
||||
$this->passwordScore['calculatedData']['lowerCase']['value'] += ceil(3 * (.7 ** $iterations));
|
||||
$this->passwordScore['calculatedData']['lowerCase']['count'] += 1;
|
||||
$iterations++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
private function countNumbers(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['numbers']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['numbers']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['numbers']['displayName'] = 'Numbers';
|
||||
$iterations = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
if (is_numeric($char)) {
|
||||
$this->passwordScore['calculatedData']['numbers']['value'] += ceil(8 * (.5 ** $iterations));
|
||||
$this->passwordScore['calculatedData']['numbers']['count'] += 1;
|
||||
$iterations++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts and scores lower case
|
||||
*
|
||||
* Scored by and exponential decay
|
||||
* starts at 13 points per char and goes down by .5^iterations
|
||||
*/
|
||||
private function countSpecialChars(): void
|
||||
{
|
||||
$i = 0;
|
||||
$this->passwordScore['calculatedData']['specialChars']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['specialChars']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['specialChars']['displayName'] = 'Special Characters';
|
||||
$iterations = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
$char = $this->password[$i];
|
||||
$charDec = \ord($char);
|
||||
if (($charDec <= 47 && $charDec >= 33) || ($charDec <= 96 && $charDec >= 91) || ($charDec <= 126 && $charDec >= 123)) {
|
||||
$this->passwordScore['calculatedData']['specialChars']['value'] += ceil(13 * (.5 ** $iterations));
|
||||
$this->passwordScore['calculatedData']['specialChars']['count'] += 1;
|
||||
$iterations++;
|
||||
}
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scores password length
|
||||
*
|
||||
* Passwords < the 8 are automatically docked -15 and scored on a lower scale
|
||||
*
|
||||
* Liner scale +2 for < 8
|
||||
* +4 for >= 8
|
||||
*/
|
||||
private function checkLength(): void
|
||||
{
|
||||
$this->passwordScore['calculatedData']['length']['value'] = 0;
|
||||
$this->passwordScore['calculatedData']['length']['count'] = 0;
|
||||
$this->passwordScore['calculatedData']['length']['displayName'] = 'Length';
|
||||
if (\strlen($this->password) < 8) {
|
||||
$this->passwordScore['calculatedData']['length']['value'] = -15;
|
||||
$i = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
$this->passwordScore['calculatedData']['length']['value'] += 2;
|
||||
$this->passwordScore['calculatedData']['length']['count'] += 1;
|
||||
$i++;
|
||||
}
|
||||
} else {
|
||||
$this->passwordScore['calculatedData']['length']['value'] = 0;
|
||||
$i = 0;
|
||||
while ($i < \strlen($this->password)) {
|
||||
$this->passwordScore['calculatedData']['length']['value'] += 4;
|
||||
$this->passwordScore['calculatedData']['length']['count'] += 1;
|
||||
$i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
237
tests/PasswordTest.php
Normal file
237
tests/PasswordTest.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
final class PasswordTest extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
|
||||
public function testReturnsPasswordClass(): void
|
||||
{
|
||||
$password = Siteworx\Passwords\Scorer::score('APassword');
|
||||
|
||||
$this->assertInstanceOf(Siteworx\Passwords\Scorer::class, $password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testThrowsExceptionPositive(): void
|
||||
{
|
||||
$password = Siteworx\Passwords\Scorer::score('APassword', 6);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \LogicException
|
||||
*/
|
||||
public function testThrowsExceptionNegative(): void
|
||||
{
|
||||
$password = Siteworx\Passwords\Scorer::score('APassword', -6);
|
||||
}
|
||||
|
||||
public function testVeryPoorCommonPasswords(): void
|
||||
{
|
||||
$veryPoorPasswords = [
|
||||
'aaaaaaaaaaaaaabbbbbcccccddddeeeeeeeeeeeeeffffffffffffggggggggg',
|
||||
'asdfgzxcv',
|
||||
'P@ssword',
|
||||
'111111111111222222222223333333333444455556666777888899999'
|
||||
];
|
||||
|
||||
foreach ($veryPoorPasswords as $poorPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($poorPassword);
|
||||
|
||||
$this->assertTrue($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function testPoorPasswords(): void
|
||||
{
|
||||
$poorPasswords = [
|
||||
'P@ssword!',
|
||||
'qwerasdfzxcv',
|
||||
'1qaz2wsx3edc'
|
||||
];
|
||||
|
||||
foreach ($poorPasswords as $poorPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($poorPassword);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertTrue($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
}
|
||||
|
||||
public function testFairPasswords(): void
|
||||
{
|
||||
$fairPasswords = [
|
||||
'p2vNGcbnjq79',
|
||||
'N2GLVE8TwMh3',
|
||||
'W4JR5Y21eDgS'
|
||||
];
|
||||
|
||||
foreach ($fairPasswords as $fairPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($fairPassword);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertTrue($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
}
|
||||
|
||||
public function testStrongPasswords(): void
|
||||
{
|
||||
$strongPasswords = [
|
||||
'0}UVsHlMwWF^21.Q',
|
||||
'9]y.GqzxsoaX8142',
|
||||
'~Va.^i(,\FLt=eurHzg@W'
|
||||
];
|
||||
|
||||
foreach ($strongPasswords as $strongPasswords) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($strongPasswords);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertTrue($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
}
|
||||
|
||||
public function testVeryStrongPasswords(): void
|
||||
{
|
||||
$veryStrongPasswords = [
|
||||
'3+gk~X7m!aUe6JG=chzI',
|
||||
',=45Dxv#M)n(\}uLpsF.',
|
||||
'y7eJ5dDIH$N)#3}KMsT%'
|
||||
];
|
||||
|
||||
foreach ($veryStrongPasswords as $veryStrongPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($veryStrongPassword);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertTrue($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
}
|
||||
|
||||
public function testExcellentPasswords(): void
|
||||
{
|
||||
$excellentPasswords = [
|
||||
'8B^2IFjN[n&ryOETRA4#1!tHe0=',
|
||||
'cxbt0[YQsaw%!k#+)2LUgu?drBD',
|
||||
'{9eOSN$JB!`\K3sH7*8m]rRU&xf'
|
||||
];
|
||||
|
||||
foreach ($excellentPasswords as $excellentPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($excellentPassword);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertTrue($password->isExcellent());
|
||||
}
|
||||
}
|
||||
|
||||
public function testBiasUp(): void
|
||||
{
|
||||
$fairPasswords = [
|
||||
'p2vNGacbfnjq79',
|
||||
'N2GLVwE8TwMdh3',
|
||||
'W4dJR5Y21eaDgS'
|
||||
];
|
||||
|
||||
foreach ($fairPasswords as $fairPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($fairPassword);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertTrue($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
|
||||
foreach ($fairPasswords as $strongNowPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($strongNowPassword, 4);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertTrue($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
|
||||
foreach ($fairPasswords as $veryStrongNowPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($veryStrongNowPassword, 5);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertTrue($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function testBiasDown(): void
|
||||
{
|
||||
$fairPasswords = [
|
||||
'p2vNGacbfnjq79',
|
||||
'N2GLVwE8TwMdh3',
|
||||
'W4dJR5Y21eaDgS'
|
||||
];
|
||||
|
||||
foreach ($fairPasswords as $fairPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($fairPassword);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertTrue($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
|
||||
foreach ($fairPasswords as $poorNowPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($poorNowPassword, -4);
|
||||
|
||||
$this->assertFalse($password->isVeryPoor());
|
||||
$this->assertTrue($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
|
||||
foreach ($fairPasswords as $veryPoorNowPassword) {
|
||||
$password = \Siteworx\Passwords\Scorer::score($veryPoorNowPassword, -5);
|
||||
|
||||
$this->assertTrue($password->isVeryPoor());
|
||||
$this->assertFalse($password->isPoor());
|
||||
$this->assertFalse($password->isFair());
|
||||
$this->assertFalse($password->isStrong());
|
||||
$this->assertFalse($password->isVeryStrong());
|
||||
$this->assertFalse($password->isExcellent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user