/password-validator

A Elixir library to validate passwords, with built-in validators for password length as well as the character sets used

Primary LanguageElixirApache License 2.0Apache-2.0

PasswordValidator

Module Version Hex Docs Total Download License Last Updated

PasswordValidator is a library to validate passwords, makes sense doesn't it? By default two validators are built in, but it is also possible to create your own custom validator for more advanced usage.

Validators:

  • LengthValidator - validates the length of the password
  • CharacterSetValidator - validates the characters contained within the password, number of lower case, number of upper case, number of special characters, etc.
  • ZXCVBNValidator - Uses Dropbox's zxcvbn algorithm to rate passwords

The primary use case is validating an %Ecto.Changeset{}

Installation

PasswordValidator is available in Hex, the package can be installed by adding password_validator to your list of dependencies in mix.exs:

def deps do
  [
    {:password_validator, "~> 0.5"},
  ]
end

The docs can be found at https://hexdocs.pm/password_validator.

Usage

PasswordValidator will typically be used within the changeset function of an Ecto schema:

@password_opts [
  length: [min: 7, max: 30, messages: [too_short: "Password is too short!"]],
  character_set: [
    lower_case: 5,  # at least five lower case letters
    upper_case: [3, :infinity], # at least three upper case letters
    numbers: [1, 4],  # from 1 to 4 number characters
    special: [0, 0],  # no special characters allowed
  ]
]

def changeset(user, attrs) do
  user
  |> cast(attrs, [:name, :age, :password])
  |> validate_required([:name, :age, :password])
  # Add this to your changeset
  |> PasswordValidator.validate(:password, @password_opts)
end

Example interactive usage:

iex> opts = [
...>   length: [min: 12, max: 30],
...> ]
iex> changeset = Ecto.Changeset.change({%{password: "simple_pass"}, %{}}, %{})
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: %{password: "simple_pass"}, valid?: true>
iex> PasswordValidator.validate(changeset, :password, opts)
#Ecto.Changeset<action: nil, changes: %{}, errors: [password: {"String is too short. Only 11 characters instead of 12", [validator: PasswordValidator.Validators.LengthValidator, error_type: :too_short]}], data: %{password: "simple_pass"}, valid?: false>

Full example:

iex> opts = [
...>   length: [min: 5, max: 30],
...>   character_set: [
...>     lower_case: 1,  # at least one lower case letter
...>     upper_case: [3, :infinity], # at least three upper case letters
...>     numbers: [0, 4],  # at most 4 numbers
...>     special: [0, 0],  # no special characters allowed
...>   ]
...> ]
iex> changeset = Ecto.Changeset.change({%{password: "Simple_pass12345"}, %{}}, %{})
iex> changeset = PasswordValidator.validate(changeset, :password, opts)
iex> changeset.errors
[password: {"Too many special (1 but maximum is 0)", [
  {:validator, PasswordValidator.Validators.CharacterSetValidator},
  {:error_type, :too_many_special}
]},
password: {"Too many numbers (5 but maximum is 4)", [
  {:validator, PasswordValidator.Validators.CharacterSetValidator},
  {:error_type, :too_many_numbers}
]},
password: {"Not enough upper_case characters (only 1 instead of at least 3)", [
  {:validator, PasswordValidator.Validators.CharacterSetValidator},
  {:error_type, :too_few_upper_case}
]}]

If you want to check that a PasswordValidator error was added then in the changeset's errors field you can check that there is an error with the key :validator

PasswordValidator can also be run directly on a String:

iex> opts = [
...>   length: [max: 6],
...> ]
iex> PasswordValidator.validate_password("too_long", opts)
{:error, ["String is too long. 8 but maximum is 6"]}

Note: The CharacterSetValidator set of allowed special characters defaults to any character that is not lower case, upper case, or a number. If the CharacterSetValidator is passed allowed_special_characters (as a string) then just those characters will be considered as special characters and any other characters will be considered "other" and will fail the password check. For full details see the CharacterSetValidator docs.

Note: On an invalid configuration the library will raise an error.

Custom validators

Custom Validators need to implement the PasswordValidator.Validator behaviour. Currently the only callback is validate. They can then be supplied as options (to either PasswordValidator.validate/3 or PasswordValidator.validate_password/2)

Constraints

  • Doesn't deal well with non-latin characters
  • Currently always pulls in Ecto as a dependency

Contributing

To run the default test suite, run mix test

PR's and discussions welcome!

Copyright and License

Copyright (c) 2017 Jason Axelson

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.