Add captcha to support requests
This commit is contained in:
parent
a9a65adb8c
commit
a1dcc36dab
4 changed files with 69 additions and 4 deletions
32
lib/recycledcloud/captcha.ex
Normal file
32
lib/recycledcloud/captcha.ex
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
defmodule RecycledCloud.Captcha do
|
||||||
|
# We can't use a graphical-only captcha library such as
|
||||||
|
# https://hex.pm/packages/captcha since it would break accessibility for
|
||||||
|
# blind people. Let's make it simple maths instead!
|
||||||
|
|
||||||
|
def validate(changeset, expected_result) do
|
||||||
|
captcha_result = Ecto.Changeset.get_field(changeset, :captcha)
|
||||||
|
if captcha_result == expected_result do
|
||||||
|
changeset
|
||||||
|
else
|
||||||
|
Ecto.Changeset.add_error(changeset, :captcha, "does not match")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate do
|
||||||
|
terms = 1..6
|
||||||
|
operands = ["+", "-", "*"]
|
||||||
|
|
||||||
|
left = terms |> Enum.random
|
||||||
|
right = terms |> Enum.random
|
||||||
|
operation = operands |> Enum.random
|
||||||
|
|
||||||
|
text = "#{left} #{operation} #{right}"
|
||||||
|
value = case operation do
|
||||||
|
"+" -> left + right
|
||||||
|
"-" -> left - right
|
||||||
|
"*" -> left * right
|
||||||
|
end
|
||||||
|
|
||||||
|
{text, value}
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,19 +5,24 @@ defmodule RecycledCloud.SupportRequest do
|
||||||
|
|
||||||
alias RecycledCloud.SupportRequest
|
alias RecycledCloud.SupportRequest
|
||||||
alias RecycledCloud.Mailer
|
alias RecycledCloud.Mailer
|
||||||
|
alias RecycledCloud.Captcha
|
||||||
|
|
||||||
embedded_schema do
|
embedded_schema do
|
||||||
field :name, :string
|
field :name, :string
|
||||||
field :email, :string
|
field :email, :string
|
||||||
field :message, :string
|
field :message, :string
|
||||||
|
field :captcha, :integer
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(key, attrs) do
|
def changeset(key, attrs) do
|
||||||
|
expected_captcha_result = attrs |> Map.get("expected_captcha")
|
||||||
|
|
||||||
key
|
key
|
||||||
|> cast(attrs, [:name, :email, :message])
|
|> cast(attrs, [:name, :email, :message, :captcha])
|
||||||
|> validate_required([:name, :email, :message])
|
|> validate_required([:name, :email, :message, :captcha])
|
||||||
|
|> Captcha.validate(expected_captcha_result)
|
||||||
end
|
end
|
||||||
|
|
||||||
def send(%SupportRequest{} = request) do
|
def send(%SupportRequest{} = request) do
|
||||||
|
|
|
@ -2,18 +2,26 @@ defmodule RecycledCloudWeb.SupportController do
|
||||||
use RecycledCloudWeb, :controller
|
use RecycledCloudWeb, :controller
|
||||||
|
|
||||||
alias RecycledCloud.SupportRequest
|
alias RecycledCloud.SupportRequest
|
||||||
|
alias RecycledCloud.Captcha
|
||||||
|
|
||||||
def new(conn, _params) do
|
def new(conn, _params) do
|
||||||
|
{captcha_text, captcha_result} = Captcha.generate()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> assign(:request_changeset, SupportRequest.changeset(%SupportRequest{}, %{}))
|
|> assign(:request_changeset, SupportRequest.changeset(%SupportRequest{}, %{}))
|
||||||
|
|> assign(:captcha, captcha_text)
|
||||||
|
|> put_session(:captcha, captcha_result)
|
||||||
|> render("new.html")
|
|> render("new.html")
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(conn, %{"support_request" => request} = params) do
|
def create(conn, %{"support_request" => raw} = params) do
|
||||||
action = SupportRequest.changeset(%SupportRequest{}, request)
|
attrs = raw |> Map.put("expected_captcha", get_session(conn, :captcha))
|
||||||
|
action = SupportRequest.changeset(%SupportRequest{}, attrs)
|
||||||
|> Ecto.Changeset.apply_action(:update)
|
|> Ecto.Changeset.apply_action(:update)
|
||||||
|
|
||||||
case action do
|
case action do
|
||||||
{:ok, request} ->
|
{:ok, request} ->
|
||||||
|
# FIXME: check for error?
|
||||||
SupportRequest.send(request)
|
SupportRequest.send(request)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -21,8 +29,12 @@ defmodule RecycledCloudWeb.SupportController do
|
||||||
|> redirect(to: Routes.page_path(conn, :index))
|
|> redirect(to: Routes.page_path(conn, :index))
|
||||||
|
|
||||||
{:error, changeset} ->
|
{:error, changeset} ->
|
||||||
|
{captcha_text, captcha_result} = Captcha.generate()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> assign(:request_changeset, changeset)
|
|> assign(:request_changeset, changeset)
|
||||||
|
|> assign(:captcha, captcha_text)
|
||||||
|
|> put_session(:captcha, captcha_result)
|
||||||
|> render("new.html")
|
|> render("new.html")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,22 @@
|
||||||
<%= textarea f, :message, placeholder: "Your message" %>
|
<%= textarea f, :message, placeholder: "Your message" %>
|
||||||
<%= error_tag f, :message %>
|
<%= error_tag f, :message %>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Can you answer the following expression to confirm that you are not basic
|
||||||
|
robot?
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<b><%= @captcha %> = </b>
|
||||||
|
|
||||||
|
<%= text_input f, :captcha, placeholder: "Captcha" %>
|
||||||
|
<%= error_tag f, :captcha %>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<%= submit "Send" %>
|
<%= submit "Send" %>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue