hCaptcha (#19)
|
@ -1,33 +1,20 @@
|
|||
/**
|
||||
* Fetch captchas.
|
||||
*/
|
||||
import { $$, hideEl } from './utils/dom';
|
||||
import { fetchJson, handleError } from './utils/requests';
|
||||
import { delegate, leftClick } from './utils/events';
|
||||
import { clearEl, makeEl } from './utils/dom';
|
||||
|
||||
function insertCaptcha(checkbox) {
|
||||
// Also hide any associated labels
|
||||
checkbox.checked = false;
|
||||
hideEl(checkbox);
|
||||
hideEl($$(`label[for="${checkbox.id}"]`));
|
||||
function insertCaptcha(_event, target) {
|
||||
const { parentNode, dataset: { sitekey } } = target;
|
||||
|
||||
fetchJson('POST', '/captchas')
|
||||
.then(handleError)
|
||||
.then(r => r.text())
|
||||
.then(r => {
|
||||
checkbox.insertAdjacentHTML('afterend', r);
|
||||
checkbox.parentElement.removeChild(checkbox);
|
||||
}).catch(() => {
|
||||
checkbox.insertAdjacentHTML('afterend', '<p class="block block--danger">Failed to fetch challenge from server!</p>');
|
||||
checkbox.parentElement.removeChild(checkbox);
|
||||
});
|
||||
const script = makeEl('script', {src: 'https://hcaptcha.com/1/api.js', async: true, defer: true});
|
||||
const frame = makeEl('div', {className: 'h-captcha'});
|
||||
|
||||
frame.dataset.sitekey = sitekey;
|
||||
|
||||
clearEl(parentNode);
|
||||
|
||||
parentNode.insertAdjacentElement('beforeend', frame);
|
||||
parentNode.insertAdjacentElement('beforeend', script);
|
||||
}
|
||||
|
||||
function bindCaptchaLinks() {
|
||||
document.addEventListener('click', event => {
|
||||
if (event.target && event.target.closest('.js-captcha')) {
|
||||
insertCaptcha(event.target.closest('.js-captcha'));
|
||||
export function bindCaptchaLinks() {
|
||||
delegate(document, 'click', {'.js-captcha': leftClick(insertCaptcha)});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { bindCaptchaLinks };
|
||||
|
|
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.2 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.9 KiB |
Before Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 5.4 KiB |
|
@ -27,6 +27,8 @@ config :philomena,
|
|||
channel_image_file_root: "priv/static/system/images",
|
||||
channel_banner_file_root: "priv/static/system/images",
|
||||
tag_file_root: "priv/static/system/images",
|
||||
hcaptcha_site_key: "10000000-ffff-ffff-ffff-000000000001",
|
||||
hcaptcha_secret_key: "0x0000000000000000000000000000000000000000",
|
||||
cdn_host: "",
|
||||
proxy_host: nil,
|
||||
app_dir: File.cwd!()
|
||||
|
|
|
@ -17,6 +17,8 @@ config :philomena,
|
|||
channel_image_file_root: System.fetch_env!("CHANNEL_IMAGE_FILE_ROOT"),
|
||||
channel_banner_file_root: System.fetch_env!("CHANNEL_BANNER_FILE_ROOT"),
|
||||
anonymous_name_salt: System.fetch_env!("ANONYMOUS_NAME_SALT"),
|
||||
hcaptcha_secret_key: System.fetch_env!("HCAPTCHA_SECRET_KEY"),
|
||||
hcaptcha_site_key: System.fetch_env!("HCAPTCHA_SITE_KEY"),
|
||||
elasticsearch_url: System.get_env("ELASTICSEARCH_URL") || "http://localhost:9200",
|
||||
advert_file_root: System.fetch_env!("ADVERT_FILE_ROOT"),
|
||||
avatar_file_root: System.fetch_env!("AVATAR_FILE_ROOT"),
|
||||
|
|
|
@ -1,154 +0,0 @@
|
|||
defmodule Philomena.Captcha do
|
||||
defstruct [:image_base64, :solution, :solution_id]
|
||||
|
||||
@numbers ~W(1 2 3 4 5 6)
|
||||
@images ~W(1 2 3 4 5 6)
|
||||
@base_path File.cwd!() <> "/assets/static/images/captcha"
|
||||
|
||||
@number_files %{
|
||||
"1" => @base_path <> "/1.png",
|
||||
"2" => @base_path <> "/2.png",
|
||||
"3" => @base_path <> "/3.png",
|
||||
"4" => @base_path <> "/4.png",
|
||||
"5" => @base_path <> "/5.png",
|
||||
"6" => @base_path <> "/6.png"
|
||||
}
|
||||
|
||||
@image_files %{
|
||||
"1" => @base_path <> "/i1.png",
|
||||
"2" => @base_path <> "/i2.png",
|
||||
"3" => @base_path <> "/i3.png",
|
||||
"4" => @base_path <> "/i4.png",
|
||||
"5" => @base_path <> "/i5.png",
|
||||
"6" => @base_path <> "/i6.png"
|
||||
}
|
||||
|
||||
@background_file @base_path <> "/background.png"
|
||||
|
||||
@geometry %{
|
||||
1 => "+0+0",
|
||||
2 => "+120+0",
|
||||
3 => "+240+0",
|
||||
4 => "+0+120",
|
||||
5 => "+120+120",
|
||||
6 => "+240+120",
|
||||
7 => "+0+240",
|
||||
8 => "+120+240",
|
||||
9 => "+240+240"
|
||||
}
|
||||
|
||||
@distortion_1 [
|
||||
~W"-implode .1",
|
||||
~W"-implode -.1"
|
||||
]
|
||||
|
||||
@distortion_2 [
|
||||
~W"-swirl 10",
|
||||
~W"-swirl -10",
|
||||
~W"-swirl 20",
|
||||
~W"-swirl -20"
|
||||
]
|
||||
|
||||
@distortion_3 [
|
||||
~W"-wave 5x180",
|
||||
~W"-wave 5x126",
|
||||
~W"-wave 10x180",
|
||||
~W"-wave 10x126"
|
||||
]
|
||||
|
||||
def create do
|
||||
solution =
|
||||
Enum.zip(@numbers, Enum.shuffle(@images))
|
||||
|> Map.new()
|
||||
|
||||
# 3x3 render grid
|
||||
grid = Enum.shuffle(@numbers ++ [nil, nil, nil])
|
||||
|
||||
# Base arguments
|
||||
args = [
|
||||
"-page",
|
||||
"360x360",
|
||||
@background_file
|
||||
]
|
||||
|
||||
# Individual grid files
|
||||
files =
|
||||
grid
|
||||
|> Enum.with_index()
|
||||
|> Enum.flat_map(fn {num, index} ->
|
||||
if num do
|
||||
[
|
||||
"(",
|
||||
@image_files[solution[num]],
|
||||
")",
|
||||
"-geometry",
|
||||
@geometry[index + 1],
|
||||
"-composite",
|
||||
"(",
|
||||
@number_files[num],
|
||||
")",
|
||||
"-geometry",
|
||||
@geometry[index + 1],
|
||||
"-composite"
|
||||
]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end)
|
||||
|
||||
# Distortions for more unpredictability
|
||||
distortions =
|
||||
[
|
||||
Enum.random(@distortion_1),
|
||||
Enum.random(@distortion_2),
|
||||
Enum.random(@distortion_3)
|
||||
]
|
||||
|> Enum.shuffle()
|
||||
|> List.flatten()
|
||||
|
||||
jpeg = ~W"-quality 8 jpeg:-"
|
||||
|
||||
{image, 0} = System.cmd("convert", args ++ files ++ distortions ++ jpeg)
|
||||
image = image |> Base.encode64()
|
||||
|
||||
# Store solution in redis to prevent reuse
|
||||
# Solutions are valid for 10 minutes
|
||||
solution_id =
|
||||
:crypto.strong_rand_bytes(12)
|
||||
|> Base.encode16(case: :lower)
|
||||
|
||||
solution_id = "cp_" <> solution_id
|
||||
|
||||
{:ok, _ok} = Redix.command(:redix, ["SET", solution_id, Jason.encode!(solution)])
|
||||
{:ok, _ok} = Redix.command(:redix, ["EXPIRE", solution_id, 600])
|
||||
|
||||
%Philomena.Captcha{
|
||||
image_base64: image,
|
||||
solution: solution,
|
||||
solution_id: solution_id
|
||||
}
|
||||
end
|
||||
|
||||
def valid_solution?(<<"cp_", _rest::binary>> = solution_id, solution) when is_map(solution) do
|
||||
# Delete key immediately. This may race, but should
|
||||
# have minimal impact if the race succeeds.
|
||||
with {:ok, sol} <- Redix.command(:redix, ["GET", solution_id]),
|
||||
{:ok, _del} <- Redix.command(:redix, ["DEL", solution_id]),
|
||||
{:ok, sol} <- Jason.decode(to_string(sol)) do
|
||||
Map.equal?(solution, sol)
|
||||
else
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def valid_solution?(_solution_id, _solution),
|
||||
do: false
|
||||
|
||||
def valid_solution?(%{"captcha" => %{"id" => id, "sln" => solution}}) do
|
||||
valid_solution?(id, solution)
|
||||
end
|
||||
|
||||
def valid_solution?(_params),
|
||||
do: false
|
||||
end
|
|
@ -7,6 +7,10 @@ defmodule Philomena.Http do
|
|||
Tesla.head(client(headers), url, opts: [adapter: adapter_opts(options)])
|
||||
end
|
||||
|
||||
def post(url, body, headers \\ [], options \\ []) do
|
||||
Tesla.post(client(headers), url, body, opts: [adapter: adapter_opts(options)])
|
||||
end
|
||||
|
||||
defp adapter_opts(opts) do
|
||||
opts = Keyword.merge(opts, max_body: 100_000_000)
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
defmodule PhilomenaWeb.CaptchaController do
|
||||
use PhilomenaWeb, :controller
|
||||
|
||||
alias Philomena.Captcha
|
||||
|
||||
def create(conn, _params) do
|
||||
captcha = Captcha.create()
|
||||
|
||||
render(conn, "create.html", captcha: captcha, layout: false)
|
||||
end
|
||||
end
|
|
@ -3,7 +3,8 @@ defmodule PhilomenaWeb.ConfirmationController do
|
|||
|
||||
alias Philomena.Users
|
||||
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
|
||||
def new(conn, _params) do
|
||||
render(conn, "new.html")
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule PhilomenaWeb.Conversation.ReportController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CanaryMapPlug, new: :show, create: :show
|
||||
|
||||
plug :load_and_authorize_resource,
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule PhilomenaWeb.Gallery.ReportController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CanaryMapPlug, new: :show, create: :show
|
||||
|
||||
plug :load_and_authorize_resource,
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule PhilomenaWeb.Image.Comment.ReportController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, new: :show, create: :show
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule PhilomenaWeb.Image.ReportController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CanaryMapPlug, new: :show, create: :show
|
||||
|
||||
plug :load_and_authorize_resource,
|
||||
|
|
|
@ -14,6 +14,7 @@ defmodule PhilomenaWeb.Image.SourceController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CanaryMapPlug, update: :edit_metadata
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", preload: [:tags, :user]
|
||||
|
|
|
@ -16,6 +16,7 @@ defmodule PhilomenaWeb.Image.TagController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CanaryMapPlug, update: :edit_metadata
|
||||
plug :load_and_authorize_resource, model: Image, id_name: "image_id", preload: [:tags, :user]
|
||||
|
|
|
@ -27,7 +27,8 @@ defmodule PhilomenaWeb.ImageController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug when action in [:new, :create]
|
||||
plug PhilomenaWeb.UserAttributionPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:new, :show, :create]
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
|
||||
plug PhilomenaWeb.ScraperPlug,
|
||||
[params_name: "image", params_key: "image"] when action in [:create]
|
||||
|
|
|
@ -3,7 +3,8 @@ defmodule PhilomenaWeb.PasswordController do
|
|||
|
||||
alias Philomena.Users
|
||||
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:new, :create]
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CompromisedPasswordCheckPlug when action in [:update]
|
||||
plug :get_user_by_reset_password_token when action in [:edit, :update]
|
||||
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule PhilomenaWeb.Profile.Commission.ReportController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CanaryMapPlug, new: :show, create: :show
|
||||
|
||||
plug :load_resource,
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule PhilomenaWeb.Profile.ReportController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CanaryMapPlug, new: :show, create: :show
|
||||
|
||||
plug :load_and_authorize_resource,
|
||||
|
|
|
@ -4,7 +4,8 @@ defmodule PhilomenaWeb.RegistrationController do
|
|||
alias Philomena.Users
|
||||
alias Philomena.Users.User
|
||||
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:new, :create]
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CompromisedPasswordCheckPlug when action in [:create]
|
||||
plug :assign_email_and_password_changesets when action in [:edit]
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ defmodule PhilomenaWeb.ReportController do
|
|||
#
|
||||
# plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
# plug PhilomenaWeb.UserAttributionPlug
|
||||
# plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
# plug PhilomenaWeb.CaptchaPlug
|
||||
# plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
# plug :load_and_authorize_resource, model: Image, id_name: "image_id", persisted: true
|
||||
|
||||
def create(conn, action, reportable, reportable_type, %{"report" => report_params}) do
|
||||
|
|
|
@ -9,7 +9,8 @@ defmodule PhilomenaWeb.Topic.Post.ReportController do
|
|||
|
||||
plug PhilomenaWeb.FilterBannedUsersPlug
|
||||
plug PhilomenaWeb.UserAttributionPlug
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
|
||||
plug PhilomenaWeb.CanaryMapPlug, new: :show, create: :show
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ defmodule PhilomenaWeb.UnlockController do
|
|||
|
||||
alias Philomena.Users
|
||||
|
||||
plug PhilomenaWeb.CaptchaPlug when action in [:create]
|
||||
plug PhilomenaWeb.CaptchaPlug
|
||||
plug PhilomenaWeb.CheckCaptchaPlug when action in [:create]
|
||||
|
||||
def new(conn, _params) do
|
||||
render(conn, "new.html")
|
||||
|
|
|
@ -1,53 +1,29 @@
|
|||
defmodule PhilomenaWeb.CaptchaPlug do
|
||||
alias Philomena.Captcha
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
alias PhilomenaWeb.ContentSecurityPolicyPlug
|
||||
|
||||
def init([]), do: false
|
||||
@hcaptcha_url ["https://hcaptcha.com", "https://*.hcaptcha.com"]
|
||||
|
||||
def init(_opts) do
|
||||
[]
|
||||
end
|
||||
|
||||
# Set CSP headers for serving captchas.
|
||||
# Only holepunch CSP if the user is not signed in.
|
||||
@spec call(Plug.Conn.t(), any()) :: Plug.Conn.t()
|
||||
def call(conn, _opts) do
|
||||
case captcha_enabled?() do
|
||||
true -> maybe_check_captcha(conn, conn.assigns.current_user)
|
||||
false -> conn
|
||||
end
|
||||
user = conn.assigns.current_user
|
||||
|
||||
maybe_assign_csp_headers(conn, user)
|
||||
end
|
||||
|
||||
defp maybe_check_captcha(conn, nil) do
|
||||
case Captcha.valid_solution?(conn.params) do
|
||||
true ->
|
||||
defp maybe_assign_csp_headers(conn, nil) do
|
||||
conn
|
||||
|> ContentSecurityPolicyPlug.permit_source(:script_src, @hcaptcha_url)
|
||||
|> ContentSecurityPolicyPlug.permit_source(:frame_src, @hcaptcha_url)
|
||||
|> ContentSecurityPolicyPlug.permit_source(:style_src, @hcaptcha_url)
|
||||
end
|
||||
|
||||
false ->
|
||||
defp maybe_assign_csp_headers(conn, _user) do
|
||||
conn
|
||||
|> put_flash(
|
||||
:error,
|
||||
"There was an error verifying you're not a robot. Please try again."
|
||||
)
|
||||
|> do_failure_response(ajax?(conn))
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_check_captcha(conn, _user), do: conn
|
||||
|
||||
defp do_failure_response(conn, true) do
|
||||
conn
|
||||
|> put_status(:multiple_choices)
|
||||
|> text("")
|
||||
end
|
||||
|
||||
defp do_failure_response(conn, _false) do
|
||||
redirect(conn, external: conn.assigns.referrer)
|
||||
end
|
||||
|
||||
def ajax?(conn) do
|
||||
case get_req_header(conn, "x-requested-with") do
|
||||
[value] -> String.downcase(value) == "xmlhttprequest"
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
def captcha_enabled? do
|
||||
Application.get_env(:philomena, :captcha) != false
|
||||
end
|
||||
end
|
||||
|
|
71
lib/philomena_web/plugs/check_captcha_plug.ex
Normal file
|
@ -0,0 +1,71 @@
|
|||
defmodule PhilomenaWeb.CheckCaptchaPlug do
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller
|
||||
|
||||
def init([]), do: false
|
||||
|
||||
def call(conn, _opts) do
|
||||
case captcha_enabled?() do
|
||||
true -> maybe_check_captcha(conn, conn.assigns.current_user)
|
||||
false -> conn
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_check_captcha(conn, nil) do
|
||||
case valid_solution?(conn.params) do
|
||||
true ->
|
||||
conn
|
||||
|
||||
false ->
|
||||
conn
|
||||
|> put_flash(
|
||||
:error,
|
||||
"There was an error verifying you're not a robot. Please try again."
|
||||
)
|
||||
|> do_failure_response(ajax?(conn))
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_check_captcha(conn, _user), do: conn
|
||||
|
||||
defp valid_solution?(%{"h-captcha-response" => captcha_token}) do
|
||||
{:ok, %{body: body, status: 200}} =
|
||||
Philomena.Http.post(
|
||||
"https://hcaptcha.com/siteverify",
|
||||
URI.encode_query(%{"response" => captcha_token, "secret" => hcaptcha_secret_key()}),
|
||||
[{"Content-Type", "application/x-www-form-urlencoded"}]
|
||||
)
|
||||
|
||||
body
|
||||
|> Jason.decode!()
|
||||
|> Map.get("success", false)
|
||||
end
|
||||
|
||||
defp valid_solution?(_params), do: false
|
||||
|
||||
defp do_failure_response(conn, true) do
|
||||
conn
|
||||
|> put_status(:multiple_choices)
|
||||
|> text("")
|
||||
end
|
||||
|
||||
defp do_failure_response(conn, _false) do
|
||||
redirect(conn, external: conn.assigns.referrer)
|
||||
end
|
||||
|
||||
def ajax?(conn) do
|
||||
case get_req_header(conn, "x-requested-with") do
|
||||
[value] -> String.downcase(value) == "xmlhttprequest"
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
def captcha_enabled? do
|
||||
Application.get_env(:philomena, :captcha) != false
|
||||
end
|
||||
|
||||
def hcaptcha_secret_key do
|
||||
Application.get_env(:philomena, :hcaptcha_secret_key)
|
||||
end
|
||||
end
|
|
@ -47,7 +47,7 @@ defmodule PhilomenaWeb.ContentSecurityPolicyPlug do
|
|||
def permit_source(conn, key, value) when key in @allowed_sources do
|
||||
conn
|
||||
|> get_config()
|
||||
|> Keyword.update(key, [], &[value | &1])
|
||||
|> Keyword.update(key, value, &(value ++ &1))
|
||||
|> set_config(conn)
|
||||
end
|
||||
|
||||
|
|
|
@ -486,8 +486,6 @@ defmodule PhilomenaWeb.Router do
|
|||
resources "/source_changes", Profile.SourceChangeController, only: [:index]
|
||||
end
|
||||
|
||||
resources "/captchas", CaptchaController, only: [:create]
|
||||
|
||||
scope "/posts", Post, as: :post do
|
||||
resources "/preview", PreviewController, only: [:create]
|
||||
end
|
||||
|
|
8
lib/philomena_web/templates/captcha/_captcha.html.slime
Normal file
|
@ -0,0 +1,8 @@
|
|||
= if is_nil(@conn.assigns.current_user) do
|
||||
- challenge = challenge_name(@name)
|
||||
|
||||
.field.js-captcha-box
|
||||
br
|
||||
=> checkbox :captcha, challenge, class: "js-captcha", value: 0, autocomplete: "off", data: [sitekey: hcaptcha_site_key()]
|
||||
= label :captcha, challenge, "I am not a robot!"
|
||||
br
|
|
@ -1,20 +0,0 @@
|
|||
elixir:
|
||||
options = [
|
||||
"Applejack": 1,
|
||||
"Fluttershy": 2,
|
||||
"Pinkie Pie": 3,
|
||||
"Rainbow Dash": 4,
|
||||
"Rarity": 5,
|
||||
"Twilight Sparkle": 6
|
||||
]
|
||||
|
||||
div
|
||||
= hidden_input :captcha, :id, value: @captcha.solution_id
|
||||
img src="data:image/jpeg;base64,#{@captcha.image_base64}"
|
||||
|
||||
= for i <- (1..6) do
|
||||
.field
|
||||
label> for="captcha_sln[#{i}]"
|
||||
| Name of pony with cutie mark #
|
||||
= i
|
||||
= select :captcha, "sln[#{i}]", options, class: "input", name: "captcha[sln][#{i}]"
|
|
@ -4,9 +4,7 @@ h1 Resend confirmation instructions
|
|||
.field
|
||||
= email_input f, :email, placeholder: "Email", class: "input", required: true
|
||||
|
||||
.field
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "confirmation", conn: @conn
|
||||
|
||||
div
|
||||
= submit "Resend confirmation instructions", class: "button"
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
button.button.button--separate-left type="reset" data-click-hide="#source-form" data-click-show="#image-source"
|
||||
' Cancel
|
||||
|
||||
= if !@conn.assigns.current_user do
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "source", conn: @conn
|
||||
|
||||
- else
|
||||
p
|
||||
|
|
|
@ -33,9 +33,7 @@
|
|||
a> href="/pages/tags" tagging guidelines
|
||||
' before editing tags.
|
||||
|
||||
.field
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "tags", conn: @conn
|
||||
|
||||
ul.horizontal-list
|
||||
li
|
||||
|
|
|
@ -9,15 +9,6 @@
|
|||
' Don't post content the artist doesn't want here (or shared in general),
|
||||
strong including commercial content.
|
||||
|
||||
= if !@conn.assigns.current_user do
|
||||
p
|
||||
strong<> Sorry, but due to spam, anonymous uploaders need to fill this out.
|
||||
| If you're logged in, you can still post anonymously and won't have to deal with captchas!
|
||||
|
||||
.field
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
|
||||
p
|
||||
strong
|
||||
' Please check it isn't already here with
|
||||
|
@ -90,5 +81,7 @@
|
|||
= label f, :anonymous, "Post anonymously"
|
||||
= checkbox f, :anonymous, class: "checkbox", value: anonymous_by_default?(@conn)
|
||||
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "image", conn: @conn
|
||||
|
||||
.actions
|
||||
= submit "Upload", class: "button", autocomplete: "off", data: [disable_with: "Please wait..."]
|
||||
|
|
|
@ -7,8 +7,6 @@ p
|
|||
.field
|
||||
= email_input f, :email, class: "input", placeholder: "Email", required: true
|
||||
|
||||
.field
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "password", conn: @conn
|
||||
|
||||
= submit "Send instructions to reset password", class: "button"
|
||||
|
|
|
@ -28,9 +28,7 @@ h1 Register
|
|||
= password_input f, :password_confirmation, class: "input", placeholder: "Confirm password", required: true
|
||||
= error_tag f, :password_confirmation
|
||||
|
||||
.field
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "registration", conn: @conn
|
||||
|
||||
br
|
||||
|
||||
|
|
|
@ -60,11 +60,6 @@ p
|
|||
.block__tab.communication-edit__tab.hidden data-tab="preview"
|
||||
' [Loading preview...]
|
||||
|
||||
= if !@conn.assigns.current_user do
|
||||
.field
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
p
|
||||
' This helps stop bot spam; log in if you don't want to deal with captchas.
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "report", conn: @conn
|
||||
|
||||
= submit "Send Report", class: "button"
|
|
@ -4,9 +4,7 @@ h1 Resend unlock instructions
|
|||
.field
|
||||
= email_input f, :email, placeholder: "Email", class: "input", required: true
|
||||
|
||||
.field
|
||||
= checkbox f, :captcha, class: "js-captcha", value: 0
|
||||
= label f, :captcha, "I am not a robot!"
|
||||
= render PhilomenaWeb.CaptchaView, "_captcha.html", name: "unlock", conn: @conn
|
||||
|
||||
div
|
||||
= submit "Resend unlock instructions", class: "button"
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
defmodule PhilomenaWeb.CaptchaView do
|
||||
use PhilomenaWeb, :view
|
||||
|
||||
# Prevent ID collisions if multiple forms are on the page.
|
||||
def challenge_name(name) do
|
||||
"#{name}_challenge"
|
||||
end
|
||||
|
||||
def hcaptcha_site_key do
|
||||
Application.get_env(:philomena, :hcaptcha_site_key)
|
||||
end
|
||||
end
|
||||
|
|