Add captcha to registrations (fixes #9)
This commit is contained in:
parent
a1dcc36dab
commit
3dfc9115a1
6 changed files with 52 additions and 16 deletions
|
@ -17,6 +17,7 @@ defmodule RecycledCloud.Accounts.User do
|
|||
field :email, :string, virtual: true
|
||||
field :confirmed_at, :naive_datetime
|
||||
field :partner_id, :integer
|
||||
field :captcha, :integer, virtual: :true
|
||||
|
||||
has_many :keys, Accounts.Key
|
||||
|
||||
|
@ -151,8 +152,13 @@ defmodule RecycledCloud.Accounts.User do
|
|||
also be very expensive to hash for certain algorithms.
|
||||
"""
|
||||
def registration_changeset(user, attrs) do
|
||||
expected_captcha_result = attrs |> Map.get("expected_captcha")
|
||||
|
||||
user
|
||||
|> insertion_changeset(attrs)
|
||||
|> cast(attrs, [:username, :password, :email, :dn, :captcha])
|
||||
|> RecycledCloud.Captcha.validate(expected_captcha_result)
|
||||
|> validate_username()
|
||||
|> validate_email()
|
||||
|> validate_password()
|
||||
end
|
||||
|
||||
|
@ -160,6 +166,7 @@ defmodule RecycledCloud.Accounts.User do
|
|||
user
|
||||
|> cast(attrs, [:username, :password, :email, :dn])
|
||||
|> validate_email()
|
||||
|> validate_username()
|
||||
end
|
||||
|
||||
defp validate_email(changeset) do
|
||||
|
@ -178,6 +185,11 @@ defmodule RecycledCloud.Accounts.User do
|
|||
#|> validate_format(:password, ~r/[!?@#$%^&*_0-9]/, message: "at least one digit or punctuation character")
|
||||
end
|
||||
|
||||
defp validate_username(changeset) do
|
||||
changeset
|
||||
|> validate_required([:username])
|
||||
end
|
||||
|
||||
@doc """
|
||||
A user changeset for changing the email.
|
||||
|
||||
|
|
|
@ -3,9 +3,13 @@ defmodule RecycledCloud.Captcha do
|
|||
# https://hex.pm/packages/captcha since it would break accessibility for
|
||||
# blind people. Let's make it simple maths instead!
|
||||
|
||||
# Captcha expression tuning:
|
||||
@terms 1..6
|
||||
@operands ["+", "-", "*"]
|
||||
|
||||
def validate(changeset, expected_result) do
|
||||
captcha_result = Ecto.Changeset.get_field(changeset, :captcha)
|
||||
if captcha_result == expected_result do
|
||||
result = Ecto.Changeset.get_field(changeset, :captcha)
|
||||
if result == expected_result do
|
||||
changeset
|
||||
else
|
||||
Ecto.Changeset.add_error(changeset, :captcha, "does not match")
|
||||
|
@ -13,12 +17,9 @@ defmodule RecycledCloud.Captcha do
|
|||
end
|
||||
|
||||
def generate do
|
||||
terms = 1..6
|
||||
operands = ["+", "-", "*"]
|
||||
|
||||
left = terms |> Enum.random
|
||||
right = terms |> Enum.random
|
||||
operation = operands |> Enum.random
|
||||
left = @terms |> Enum.random
|
||||
right = @terms |> Enum.random
|
||||
operation = @operands |> Enum.random
|
||||
|
||||
text = "#{left} #{operation} #{right}"
|
||||
value = case operation do
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule RecycledCloudWeb.UserRegistrationController do
|
||||
use RecycledCloudWeb, :controller
|
||||
|
||||
alias RecycledCloud.Captcha
|
||||
alias RecycledCloud.Accounts
|
||||
alias RecycledCloud.Accounts.User
|
||||
alias RecycledCloudWeb.UserAuth
|
||||
|
@ -11,6 +12,7 @@ defmodule RecycledCloudWeb.UserRegistrationController do
|
|||
|
||||
def new(conn, _params) do
|
||||
changeset = Accounts.change_user_registration(%User{})
|
||||
{captcha_text, captcha_result} = Captcha.generate()
|
||||
|
||||
with_notice = unless is_registration_enabled?() do
|
||||
conn
|
||||
|
@ -20,17 +22,20 @@ defmodule RecycledCloudWeb.UserRegistrationController do
|
|||
end
|
||||
|
||||
with_notice
|
||||
|> assign(:captcha, captcha_text)
|
||||
|> put_session(:captcha, captcha_result)
|
||||
|> render("new.html", changeset: changeset)
|
||||
end
|
||||
|
||||
def create(conn, %{"user" => user_params}) do
|
||||
changeset = Accounts.change_user_registration(%User{})
|
||||
|
||||
unless is_registration_enabled?() do
|
||||
conn
|
||||
|> redirect(to: Routes.user_registration_path(conn, :new))
|
||||
else
|
||||
case Accounts.register_user(user_params) do
|
||||
attrs = user_params
|
||||
|> Map.put("expected_captcha", get_session(conn, :captcha))
|
||||
|
||||
case Accounts.register_user(attrs) do
|
||||
{:ok, user} ->
|
||||
{:ok, _} =
|
||||
Accounts.deliver_user_confirmation_instructions(
|
||||
|
@ -43,7 +48,12 @@ defmodule RecycledCloudWeb.UserRegistrationController do
|
|||
|> UserAuth.log_in_user(user)
|
||||
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
render(conn, "new.html", changeset: changeset)
|
||||
{captcha_text, captcha_result} = Captcha.generate()
|
||||
|
||||
conn
|
||||
|> assign(:captcha, captcha_text)
|
||||
|> put_session(:captcha, captcha_result)
|
||||
|> render("new.html", changeset: changeset)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<br />
|
||||
|
||||
<p>
|
||||
Can you answer the following expression to confirm that you are not basic
|
||||
Can you answer the following expression to confirm you are not basic
|
||||
robot?
|
||||
|
||||
<br />
|
||||
|
|
|
@ -22,6 +22,18 @@
|
|||
|
||||
<br />
|
||||
|
||||
<p>
|
||||
Can you answer the following expression to confirm you are not basic robot?
|
||||
|
||||
<br />
|
||||
|
||||
<b><%= @captcha %> = </b>
|
||||
|
||||
<%= text_input f, :captcha, placeholder: "Captcha" %>
|
||||
<%= error_tag f, :captcha %>
|
||||
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<%= submit "Register" %>
|
||||
</div>
|
||||
|
|
|
@ -95,15 +95,16 @@ defmodule RecycledCloud.AccountsTest do
|
|||
describe "change_user_registration/2" do
|
||||
test "returns a changeset" do
|
||||
assert %Ecto.Changeset{} = changeset = Accounts.change_user_registration(%User{})
|
||||
assert changeset.required == [:password, :email]
|
||||
assert changeset.required == [:password, :email, :username]
|
||||
end
|
||||
|
||||
test "allows fields to be set" do
|
||||
username = unique_user_name()
|
||||
email = unique_user_email()
|
||||
password = valid_user_password()
|
||||
|
||||
changeset =
|
||||
Accounts.change_user_registration(%User{}, %{"email" => email, "password" => password})
|
||||
Accounts.change_user_registration(%User{}, %{"username" => username, "email" => email, "password" => password})
|
||||
|
||||
assert changeset.valid?
|
||||
assert get_change(changeset, :email) == email
|
||||
|
|
Loading…
Reference in a new issue