Skip to content

Commit abb4c9a

Browse files
author
amn
committed
Initial commit
0 parents  commit abb4c9a

File tree

8 files changed

+260
-0
lines changed

8 files changed

+260
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/_build
2+
erl_crash.dump

LICENSE

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Copyright (c) 2015 Mikhail Alekseev
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Recaptcha
2+
3+
A simple Elixir package for implementing [ReCaptcha] v2 in [Phoenix] applications.
4+
5+
[ReCaptcha]: http://www.google.com/recaptcha
6+
[Phoenix]: http://www.phoenixframework.org/
7+
8+
## Installation
9+
10+
Set as a dependency in the and ensure it is running with your app:
11+
12+
1) Add as a dependency to the mix.exs file
13+
14+
```elixir
15+
defp deps do
16+
[
17+
# other deps
18+
{:recaptcha, "~> 0.0.1"},
19+
{:ibrowse, github: "cmullaparthi/ibrowse", tag: "v4.1.2"},
20+
# other deps
21+
]
22+
end
23+
```
24+
25+
2) Add to your application list
26+
27+
```elixir
28+
def application do
29+
[
30+
# ...
31+
applications: [:phoenix, :recaptcha]
32+
# ...
33+
]
34+
end
35+
```
36+
37+
Get your project's dependencies:
38+
39+
```bash
40+
$ mix deps.get
41+
```
42+
43+
## Config
44+
45+
In your application's config.exs :
46+
47+
```elixir
48+
config :recaptcha,
49+
api_config: %{ verify_url: "https://www.google.com/recaptcha/api/siteverify",
50+
public_key: "YOUR_PUBLIC_KEY",
51+
private_key: "YOUR_PRIVATE_KEY" }
52+
```
53+
54+
## Usage
55+
56+
### View
57+
58+
In a template
59+
60+
```html
61+
<form name="someform" method="post" action="/somewhere">
62+
...
63+
<%= raw Recaptcha.display %>
64+
...
65+
</form>
66+
```
67+
68+
Display method accepts a keyword list with 2 possible options:
69+
70+
* `noscript` -> Set to true to render noscript code
71+
* `public_key` -> The key put in the `data-sitekey` reCaptcha div attribute, default is the public key set in the config file
72+
73+
Pass these parameters like this:
74+
75+
```elixir
76+
...
77+
<%= raw Recaptcha.display(noscript: true, public_key: "Public key") %>
78+
...
79+
```
80+
81+
### Controller
82+
83+
In a controller
84+
85+
```elixir
86+
87+
def create(conn, params) do
88+
# some code
89+
case Recaptcha.verify(conn.remote_ip, params["g-recaptcha-response"]) do
90+
:ok -> do_something
91+
:error -> handle_error
92+
end
93+
end
94+
95+
```
96+
97+
`verify` method also accepts a keyword list as the third parameter with also 2 possible options:
98+
99+
* `public_key` -> A key to use in the http request to the recaptcha api, default is the private key set in the config file
100+
* `timeout` -> Time period in ms to wait for a response from google api, default is 3000

config/config.exs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use Mix.Config
2+
3+
config :recaptcha, :api_config,
4+
%{ verify_url: "https://www.google.com/recaptcha/api/siteverify",
5+
public_key: "YOUR PUBLIC KEY",
6+
private_key: "YOUR PRIVATE KEY"
7+
}

lib/recaptcha.ex

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
defmodule Recaptcha do
2+
require Elixir.EEx
3+
4+
@secret_key_errors ~w(missing-input-secret invalid-input-secret)
5+
6+
EEx.function_from_file :defp, :render_template, "lib/template.html.eex", [:assigns]
7+
8+
def display(options \\ []) do
9+
public_key = options[:public_key] || config.public_key
10+
render_template(public_key: public_key, options: options)
11+
end
12+
13+
def verify(remote_ip, response, options \\ [])
14+
15+
def verify(remote_ip, response, options) when is_tuple(remote_ip) do
16+
verify(:inet_parse.ntoa(remote_ip), response, options)
17+
end
18+
19+
def verify(remote_ip, response, options) do
20+
case api_response(remote_ip, response, options) do
21+
%{"success" => true} ->
22+
:ok
23+
%{"success" => false, "error-codes" => error_codes} ->
24+
handle_error_codes(error_codes)
25+
%{"success" => false} ->
26+
:error
27+
end
28+
end
29+
30+
defp api_response(remote_ip, response, options) do
31+
private_key = options[:private_key] || config.private_key
32+
timeout = options[:timeout] || 3000
33+
body_content = URI.encode_query(%{"remoteip" => to_string(remote_ip),
34+
"response" => response,
35+
"secret" => private_key})
36+
headers = ["Content-type": "application/x-www-form-urlencoded"]
37+
options = [body: body_content, headers: headers, timeout: timeout]
38+
HTTPotion.post(config.verify_url, options).body |> Poison.decode!
39+
end
40+
41+
defp config do
42+
Application.get_env(:recaptcha, :api_config)
43+
end
44+
45+
defp handle_error_codes(error_codes) do
46+
if Enum.any?(error_codes, fn(code) -> Enum.member?(@secret_key_errors, code) end) do
47+
raise RuntimeError,
48+
message: "reCaptcha API has declined the private key. Please make sure you've set the correct private key"
49+
else
50+
:error
51+
end
52+
end
53+
54+
end

lib/template.html.eex

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
2+
3+
<div class="g-recaptcha"
4+
data-sitekey="<%= @public_key %>"
5+
data-theme="<%= @options[:theme]%>"
6+
data-type="<%= @options[:type]%>"
7+
data-tabindex="<%= @options[:tabindex]%>"
8+
data-size="<%= @options[:size]%>">
9+
</div>
10+
11+
<%= if @options[:noscript] do %>
12+
13+
<noscript>
14+
<div style="width: 302px; height: 422px;">
15+
<div style="width: 302px; height: 422px; position: relative;">
16+
<div style="width: 302px; height: 422px; position: absolute;">
17+
<iframe src="https://www.google.com/recaptcha/api/fallback?k=<%= @public_key %>"
18+
frameborder="0" scrolling="no"
19+
style="width: 302px; height:422px; border-style: none;">
20+
</iframe>
21+
</div>
22+
<div style="width: 300px; height: 60px; border-style: none;
23+
bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
24+
background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
25+
<textarea id="g-recaptcha-response" name="g-recaptcha-response"
26+
class="g-recaptcha-response"
27+
style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
28+
margin: 10px 25px; padding: 0px; resize: none;" >
29+
</textarea>
30+
</div>
31+
</div>
32+
</div>
33+
</noscript>
34+
35+
<% end %>

mix.exs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
defmodule Recaptcha.Mixfile do
2+
use Mix.Project
3+
4+
def project do
5+
[app: :recaptcha,
6+
version: "0.0.1",
7+
elixir: "~> 1.0.0",
8+
description: description,
9+
deps: deps,
10+
package: package]
11+
end
12+
13+
def application do
14+
[applications: [:logger, :httpotion]]
15+
end
16+
17+
defp description do
18+
"""
19+
A simple reCaptcha package for Phoenix applications.
20+
"""
21+
end
22+
23+
defp deps do
24+
[
25+
{:ibrowse, github: "cmullaparthi/ibrowse", tag: "v4.1.2"},
26+
{:httpotion, "~> 2.1.0"},
27+
{:poison, "~> 1.5"}
28+
]
29+
end
30+
31+
defp package do
32+
[files: ["lib", "mix.exs", "README.md", "LICENSE"],
33+
contributors: ["Alekseev Mikhail"],
34+
licenses: ["MIT"],
35+
links: %{"GitHub" => "https://github.com/JustMikey/recaptcha"}]
36+
end
37+
end

mix.lock

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
%{"httpotion": {:hex, :httpotion, "2.1.0"},
2+
"ibrowse": {:git, "git://github.com/cmullaparthi/ibrowse.git", "ea3305d21f37eced4fac290f64b068e56df7de80", [tag: "v4.1.2"]},
3+
"poison": {:hex, :poison, "1.5.0"}}

0 commit comments

Comments
 (0)