aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoramn <amn@em70.ru>2015-08-27 11:21:15 +0600
committeramn <amn@em70.ru>2015-08-27 11:21:15 +0600
commitabb4c9ae95ac83aee91cdee69d6606ff137a0786 (patch)
tree16955002d39ba162c82b605d34161ea00514d8fb
downloadrecaptcha-abb4c9ae95ac83aee91cdee69d6606ff137a0786.tar.gz
recaptcha-abb4c9ae95ac83aee91cdee69d6606ff137a0786.tar.xz
Initial commit
-rw-r--r--.gitignore2
-rw-r--r--LICENSE22
-rw-r--r--README.md100
-rw-r--r--config/config.exs7
-rw-r--r--lib/recaptcha.ex54
-rw-r--r--lib/template.html.eex35
-rw-r--r--mix.exs37
-rw-r--r--mix.lock3
8 files changed, 260 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..47df665
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/_build
+erl_crash.dump
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a492b9b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2015 Mikhail Alekseev
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9c31255
--- /dev/null
+++ b/README.md
@@ -0,0 +1,100 @@
+# Recaptcha
+
+A simple Elixir package for implementing [ReCaptcha] v2 in [Phoenix] applications.
+
+[ReCaptcha]: http://www.google.com/recaptcha
+[Phoenix]: http://www.phoenixframework.org/
+
+## Installation
+
+Set as a dependency in the and ensure it is running with your app:
+
+1) Add as a dependency to the mix.exs file
+
+```elixir
+ defp deps do
+ [
+ # other deps
+ {:recaptcha, "~> 0.0.1"},
+ {:ibrowse, github: "cmullaparthi/ibrowse", tag: "v4.1.2"},
+ # other deps
+ ]
+ end
+```
+
+2) Add to your application list
+
+```elixir
+def application do
+ [
+ # ...
+ applications: [:phoenix, :recaptcha]
+ # ...
+ ]
+end
+```
+
+Get your project's dependencies:
+
+```bash
+$ mix deps.get
+```
+
+## Config
+
+In your application's config.exs :
+
+```elixir
+config :recaptcha,
+ api_config: %{ verify_url: "https://www.google.com/recaptcha/api/siteverify",
+ public_key: "YOUR_PUBLIC_KEY",
+ private_key: "YOUR_PRIVATE_KEY" }
+```
+
+## Usage
+
+### View
+
+In a template
+
+```html
+<form name="someform" method="post" action="/somewhere">
+ ...
+ <%= raw Recaptcha.display %>
+ ...
+</form>
+```
+
+Display method accepts a keyword list with 2 possible options:
+
+* `noscript` -> Set to true to render noscript code
+* `public_key` -> The key put in the `data-sitekey` reCaptcha div attribute, default is the public key set in the config file
+
+Pass these parameters like this:
+
+```elixir
+...
+<%= raw Recaptcha.display(noscript: true, public_key: "Public key") %>
+...
+```
+
+### Controller
+
+In a controller
+
+```elixir
+
+def create(conn, params) do
+ # some code
+ case Recaptcha.verify(conn.remote_ip, params["g-recaptcha-response"]) do
+ :ok -> do_something
+ :error -> handle_error
+ end
+end
+
+```
+
+`verify` method also accepts a keyword list as the third parameter with also 2 possible options:
+
+* `public_key` -> A key to use in the http request to the recaptcha api, default is the private key set in the config file
+* `timeout` -> Time period in ms to wait for a response from google api, default is 3000
diff --git a/config/config.exs b/config/config.exs
new file mode 100644
index 0000000..3698024
--- /dev/null
+++ b/config/config.exs
@@ -0,0 +1,7 @@
+use Mix.Config
+
+config :recaptcha, :api_config,
+ %{ verify_url: "https://www.google.com/recaptcha/api/siteverify",
+ public_key: "YOUR PUBLIC KEY",
+ private_key: "YOUR PRIVATE KEY"
+ }
diff --git a/lib/recaptcha.ex b/lib/recaptcha.ex
new file mode 100644
index 0000000..c9abf38
--- /dev/null
+++ b/lib/recaptcha.ex
@@ -0,0 +1,54 @@
+defmodule Recaptcha do
+ require Elixir.EEx
+
+ @secret_key_errors ~w(missing-input-secret invalid-input-secret)
+
+ EEx.function_from_file :defp, :render_template, "lib/template.html.eex", [:assigns]
+
+ def display(options \\ []) do
+ public_key = options[:public_key] || config.public_key
+ render_template(public_key: public_key, options: options)
+ end
+
+ def verify(remote_ip, response, options \\ [])
+
+ def verify(remote_ip, response, options) when is_tuple(remote_ip) do
+ verify(:inet_parse.ntoa(remote_ip), response, options)
+ end
+
+ def verify(remote_ip, response, options) do
+ case api_response(remote_ip, response, options) do
+ %{"success" => true} ->
+ :ok
+ %{"success" => false, "error-codes" => error_codes} ->
+ handle_error_codes(error_codes)
+ %{"success" => false} ->
+ :error
+ end
+ end
+
+ defp api_response(remote_ip, response, options) do
+ private_key = options[:private_key] || config.private_key
+ timeout = options[:timeout] || 3000
+ body_content = URI.encode_query(%{"remoteip" => to_string(remote_ip),
+ "response" => response,
+ "secret" => private_key})
+ headers = ["Content-type": "application/x-www-form-urlencoded"]
+ options = [body: body_content, headers: headers, timeout: timeout]
+ HTTPotion.post(config.verify_url, options).body |> Poison.decode!
+ end
+
+ defp config do
+ Application.get_env(:recaptcha, :api_config)
+ end
+
+ defp handle_error_codes(error_codes) do
+ if Enum.any?(error_codes, fn(code) -> Enum.member?(@secret_key_errors, code) end) do
+ raise RuntimeError,
+ message: "reCaptcha API has declined the private key. Please make sure you've set the correct private key"
+ else
+ :error
+ end
+ end
+
+end
diff --git a/lib/template.html.eex b/lib/template.html.eex
new file mode 100644
index 0000000..c9c5385
--- /dev/null
+++ b/lib/template.html.eex
@@ -0,0 +1,35 @@
+<script src="https://www.google.com/recaptcha/api.js" async defer></script>
+
+<div class="g-recaptcha"
+ data-sitekey="<%= @public_key %>"
+ data-theme="<%= @options[:theme]%>"
+ data-type="<%= @options[:type]%>"
+ data-tabindex="<%= @options[:tabindex]%>"
+ data-size="<%= @options[:size]%>">
+</div>
+
+<%= if @options[:noscript] do %>
+
+ <noscript>
+ <div style="width: 302px; height: 422px;">
+ <div style="width: 302px; height: 422px; position: relative;">
+ <div style="width: 302px; height: 422px; position: absolute;">
+ <iframe src="https://www.google.com/recaptcha/api/fallback?k=<%= @public_key %>"
+ frameborder="0" scrolling="no"
+ style="width: 302px; height:422px; border-style: none;">
+ </iframe>
+ </div>
+ <div style="width: 300px; height: 60px; border-style: none;
+ bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
+ background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
+ <textarea id="g-recaptcha-response" name="g-recaptcha-response"
+ class="g-recaptcha-response"
+ style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
+ margin: 10px 25px; padding: 0px; resize: none;" >
+ </textarea>
+ </div>
+ </div>
+ </div>
+ </noscript>
+
+<% end %>
diff --git a/mix.exs b/mix.exs
new file mode 100644
index 0000000..780ed45
--- /dev/null
+++ b/mix.exs
@@ -0,0 +1,37 @@
+defmodule Recaptcha.Mixfile do
+ use Mix.Project
+
+ def project do
+ [app: :recaptcha,
+ version: "0.0.1",
+ elixir: "~> 1.0.0",
+ description: description,
+ deps: deps,
+ package: package]
+ end
+
+ def application do
+ [applications: [:logger, :httpotion]]
+ end
+
+ defp description do
+ """
+ A simple reCaptcha package for Phoenix applications.
+ """
+ end
+
+ defp deps do
+ [
+ {:ibrowse, github: "cmullaparthi/ibrowse", tag: "v4.1.2"},
+ {:httpotion, "~> 2.1.0"},
+ {:poison, "~> 1.5"}
+ ]
+ end
+
+ defp package do
+ [files: ["lib", "mix.exs", "README.md", "LICENSE"],
+ contributors: ["Alekseev Mikhail"],
+ licenses: ["MIT"],
+ links: %{"GitHub" => "https://github.com/JustMikey/recaptcha"}]
+ end
+end
diff --git a/mix.lock b/mix.lock
new file mode 100644
index 0000000..1109ea8
--- /dev/null
+++ b/mix.lock
@@ -0,0 +1,3 @@
+%{"httpotion": {:hex, :httpotion, "2.1.0"},
+ "ibrowse": {:git, "git://github.com/cmullaparthi/ibrowse.git", "ea3305d21f37eced4fac290f64b068e56df7de80", [tag: "v4.1.2"]},
+ "poison": {:hex, :poison, "1.5.0"}}