Last active
May 25, 2019 13:21
-
-
Save freddyheppell/935b96276f651e8d0e1acf88b816d5ca to your computer and use it in GitHub Desktop.
Laravel Rule for valid Discourse usernames
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Rules; | |
use Illuminate\Contracts\Validation\Rule; | |
/** | |
* PHP implementation of | |
* https://github.com/discourse/discourse/blob/888e68a1637ca784a7bf51a6bbb524dcf7413b13/app/models/username_validator.rb | |
* @package App\Rules | |
*/ | |
class DiscourseUsernameRule implements Rule | |
{ | |
private $value; | |
// usernames must consist of a-z A-Z 0-9 _ - . | |
private const ASCII_INVALID_CHARACTERS = "/[^\w.-]/"; | |
// usernames can start with a-z A-Z 0-0 _ | |
private const VALID_LEADING_CHARACTERS = "/^[a-zA-Z0-9_]/"; | |
// usernames must end with a-z A-Z 0-9 | |
private const VALID_TRAILING_CHARACTERS = "/[a-zA-Z0-9]$/"; | |
// underscores, dashes and dots can't be repeated consecutively | |
private const REPEATING_CONFUSING_CHARACTERS = "/[-_.]{2,}/"; | |
private const CONFUSING_EXTENSIONS = "/\.(js|json|css|htm|html|xml|jpg|jpeg|png|gif|bmp|ico|tif|tiff|woff)$/i"; | |
/** | |
* Create a new rule instance. | |
* | |
* @return void | |
*/ | |
public function __construct() | |
{ | |
// | |
} | |
private function minimumLength() | |
{ | |
return strlen($this->value) > 3; | |
} | |
private function maximumLength() | |
{ | |
return strlen($this->value) <= 20; | |
} | |
private function charactersValid() | |
{ | |
return !preg_match(self::ASCII_INVALID_CHARACTERS, $this->value); | |
} | |
private function firstCharacterValid() | |
{ | |
return preg_match(self::VALID_LEADING_CHARACTERS, $this->value); | |
} | |
private function lastCharacterValid() | |
{ | |
return preg_match(self::VALID_TRAILING_CHARACTERS, $this->value); | |
} | |
private function noDoubleSpecial() | |
{ | |
return !preg_match(self::REPEATING_CONFUSING_CHARACTERS, $this->value); | |
} | |
private function noConfusingExtension() | |
{ | |
return !preg_match(self::CONFUSING_EXTENSIONS, $this->value); | |
} | |
/** | |
* Determine if the validation rule passes. | |
* | |
* @param string $attribute | |
* @param mixed $value | |
* @return bool | |
*/ | |
public function passes($attribute, $value) | |
{ | |
$this->value = $value; | |
return $this->minimumLength() && | |
$this->maximumLength() && | |
$this->charactersValid() && | |
$this->firstCharacterValid() && | |
$this->lastCharacterValid() && | |
$this->noDoubleSpecial() && | |
$this->noConfusingExtension(); | |
} | |
/** | |
* Get the validation error message. | |
* | |
* @return string | |
*/ | |
public function message() | |
{ | |
return 'The validation error message.'; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Tests\Rules; | |
use App\Rules\DiscourseUsernameRule; | |
use Tests\TestCase; | |
class DiscourseUsernameRule_Test extends TestCase | |
{ | |
private $rule; | |
protected function setUp(): void | |
{ | |
parent::setUp(); | |
$this->rule = new DiscourseUsernameRule(); | |
} | |
public function testBlankUsernameIsInvalid() | |
{ | |
$this->assertFalse($this->rule->passes("username", "")); | |
} | |
public function testUsernameTooShort() | |
{ | |
$this->assertFalse($this->rule->passes("username", "abc")); | |
} | |
public function testUsernameWithMinLength() | |
{ | |
$this->assertTrue($this->rule->passes("username", "abcd")); | |
} | |
public function testUsernameTooLong() | |
{ | |
$this->assertFalse($this->rule->passes("username", "abcdefgjijklmnopqrstu")); | |
} | |
public function testUsernameWithMaxLength() | |
{ | |
$this->assertTrue($this->rule->passes("username", "abcdefgjijklmnopqrst")); | |
} | |
public function testValidCharacters() | |
{ | |
$this->assertTrue($this->rule->passes("username", "ab-cd.123_ABC-xYz")); | |
} | |
public function testInvalidCharacters() | |
{ | |
$this->assertFalse($this->rule->passes("username", "abc|")); | |
$this->assertFalse($this->rule->passes("username", "a#bc")); | |
$this->assertFalse($this->rule->passes("username", "abc xyz")); | |
} | |
public function testStartingWithAlphanumericOrUnderscore() | |
{ | |
$this->assertTrue($this->rule->passes("username", "abcd")); | |
$this->assertTrue($this->rule->passes("username", "1abc")); | |
$this->assertTrue($this->rule->passes("username", "_abc")); | |
} | |
public function testStartingWithDotOrDash() | |
{ | |
$this->assertFalse($this->rule->passes("username", ".abc")); | |
$this->assertFalse($this->rule->passes("username", "-abc")); | |
} | |
public function testEndingWithAlphanumeric() | |
{ | |
$this->assertTrue($this->rule->passes("username", "abcd")); | |
$this->assertTrue($this->rule->passes("username", "abc9")); | |
} | |
public function testEndingWithInvalidCharacter() | |
{ | |
$this->assertFalse($this->rule->passes("username", "abc_")); | |
$this->assertFalse($this->rule->passes("username", "abc.")); | |
$this->assertFalse($this->rule->passes("username", "abc-")); | |
} | |
public function testConsecutiveSpecialCharacters() | |
{ | |
$this->assertFalse($this->rule->passes("username", "a__bc")); | |
$this->assertFalse($this->rule->passes("username", "a..bc")); | |
$this->assertFalse($this->rule->passes("username", "a--bc")); | |
} | |
public function testFileExtensionEnding() | |
{ | |
$this->assertFalse($this->rule->passes("username", "abc.json")); | |
$this->assertFalse($this->rule->passes("username", "abc.png")); | |
} | |
public function testUnicodeCharacters() | |
{ | |
$this->assertFalse($this->rule->passes("username", "abcö")); | |
$this->assertFalse($this->rule->passes("username", "abc象")); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment