-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Recaptcha service. Add specs. * Add js recaptcha_controller #validate method * Rework submit button to first call recaptcha validations. * Obfuscate recaptcha_token in logs * Add specs for pledges#create request * Add recaptcha script to <head> * Modify RuboCop string literal cop to include spec directory * Run cops on spec directory * Update example env file and README. * Add redis to GitHub CI workflow. * Update test command in GitHub CI workflow. * Remove screenshot storage in GitHub CI workflow.
- Loading branch information
Showing
13 changed files
with
186 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,13 @@ | ||
# Omakase Ruby styling for Rails | ||
inherit_gem: { rubocop-rails-omakase: rubocop.yml } | ||
|
||
# Overwrite or add rules to create your own house style | ||
# | ||
# # Use `[a, [b, c]]` not `[ a, [ b, c ] ]` | ||
# Layout/SpaceInsideArrayLiteralBrackets: | ||
# Enabled: false | ||
Style/StringLiterals: | ||
Enabled: true | ||
EnforcedStyle: double_quotes | ||
Include: | ||
- "app/**/*" | ||
- "config/**/*" | ||
- "lib/**/*" | ||
- "test/**/*" | ||
- "Gemfile" | ||
- "spec/**/*" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { Controller } from "@hotwired/stimulus"; | ||
|
||
export default class extends Controller { | ||
validate(event) { | ||
event.preventDefault(); | ||
|
||
const siteKey = document | ||
.getElementById("recaptcha-initlializer") | ||
.src.split("?render=") | ||
.slice(-1)[0]; | ||
const form = this.element; | ||
const tokenInput = form.querySelector("input[name='g_recaptcha_response']"); | ||
const submitButton = form.querySelector("button[type='submit']"); | ||
|
||
grecaptcha.ready(function () { | ||
grecaptcha | ||
.execute(siteKey, { | ||
action: "submit", | ||
}) | ||
.then(function (token) { | ||
tokenInput.value = token; | ||
form.requestSubmit(submitButton); | ||
}); | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
require "net/http" | ||
require "uri" | ||
|
||
class Recaptcha | ||
def initialize(token:) | ||
@token = token | ||
end | ||
|
||
def call | ||
endpoint = "https://www.google.com/recaptcha/api/siteverify" | ||
data = { | ||
secret: ENV["G_RECAPTCHA_SECRET_KEY"], | ||
response: @token | ||
} | ||
response = Net::HTTP.post_form URI(endpoint), data | ||
payload = JSON.parse(response.body) | ||
threshold = ENV.fetch("G_RECAPTCHA_SCORE_THRESHOLD", "0.5").to_f | ||
|
||
if payload["success"] && payload["score"] > threshold | ||
Rails.logger.info "reCAPTCHA evaluated as safe: payload => #{payload}" | ||
true | ||
else | ||
Rails.logger.warn "reCAPTCHA evaluated as unsafe: payload => #{payload}" | ||
false | ||
end | ||
|
||
rescue StandardError => exception | ||
Rails.logger.error "Form response failed to validate with reCAPTCHA: exception => #{exception}, payload => #{payload}" | ||
false | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
require "rails_helper" | ||
|
||
describe PledgesController do | ||
include ActionMailer::TestHelper | ||
include ActiveJob::TestHelper | ||
|
||
describe "#create" do | ||
let(:first_name) { "Isaac" } | ||
let(:last_name) { "Newton" } | ||
let(:email) { "inewton@example.net" } | ||
let(:token) { "motion_token" } | ||
let(:params) do | ||
{ | ||
pledge: { | ||
first_name: first_name, | ||
last_name: last_name, | ||
email: email, | ||
g_recaptcha_response: token | ||
} | ||
} | ||
end | ||
let(:recaptcha_stub) { double call: true } | ||
|
||
before do | ||
allow(Recaptcha).to receive(:new).and_return recaptcha_stub | ||
post "/pledges", params: params | ||
end | ||
|
||
it "creates a new pledge" do | ||
expect(Pledge.count).to eq 1 | ||
end | ||
|
||
it "enqueues an email job" do | ||
expect(enqueued_jobs.first[:job]).to eq ActionMailer::MailDeliveryJob | ||
end | ||
|
||
it "redirects to the root" do | ||
expect(response.code).to eq "302" | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
require "rails_helper" | ||
|
||
describe Recaptcha do | ||
subject { described_class.new(token: double) } | ||
|
||
describe "#call" do | ||
let(:response) { double body: "{\n \"success\": true,\n \"challenge_ts\": \"2024-11-25T00:03:52Z\",\n \"hostname\": \"example.net\",\n \"score\": 0.9,\n \"action\": \"submit\"\n}" } | ||
|
||
before do | ||
allow(Rails.logger).to receive_messages(info: nil, warn: nil, error: nil) | ||
allow(Net::HTTP).to receive(:post_form).and_return response | ||
end | ||
|
||
context "safe form submission" do | ||
it "logs an info message and returns true" do | ||
expect(subject.call).to eq true | ||
expect(Rails.logger).to have_received(:info) | ||
end | ||
end | ||
|
||
context "unsafe form submission" do | ||
let(:response) { double body: "{\n \"success\": false,\n \"challenge_ts\": \"2024-11-25T00:03:52Z\",\n \"hostname\": \"example.net\",\n \"score\": 0.1,\n \"action\": \"submit\"\n}" } | ||
|
||
it "logs a warning message and returns false" do | ||
expect(subject.call).to eq false | ||
expect(Rails.logger).to have_received(:warn) | ||
end | ||
end | ||
|
||
context "invalid reCAPTCHA call" do | ||
let(:response) { double body: "{\n \"success\": false,\n \"error-codes\": [\n \"invalid-input-response\"\n ]\n}" } | ||
|
||
it "logs a warning message and returns false" do | ||
expect(subject.call).to eq false | ||
expect(Rails.logger).to have_received(:warn) | ||
end | ||
end | ||
|
||
context "StandardError" do | ||
before do | ||
allow(Net::HTTP).to receive(:post_form).and_raise(StandardError) | ||
end | ||
|
||
it "logs an error message and returns false" do | ||
expect(subject.call).to eq false | ||
expect(Rails.logger).to have_received(:error) | ||
end | ||
end | ||
end | ||
end |