Add captcha to registrations (fixes #9)

This commit is contained in:
Timothée Floure 2021-02-03 10:12:23 +01:00
parent a1dcc36dab
commit 3dfc9115a1
Signed by: tfloure
GPG key ID: 4502C902C00A1E12
6 changed files with 52 additions and 16 deletions

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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 />

View file

@ -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>

View file

@ -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