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