Skip to content

Commit

Permalink
Add Altcha captcha to notifications submissions
Browse files Browse the repository at this point in the history
  • Loading branch information
crutch committed Apr 27, 2024
1 parent edd6e08 commit 29aa3de
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 10 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ gem 'omniauth'
gem 'omniauth-google-oauth2'
gem 'omniauth-rails_csrf_protection'

gem 'altcha-rails'

gem 'pry-rails'

gem 'aws-sdk-rails'
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ GEM
zeitwerk (~> 2.3)
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
altcha-rails (0.0.5)
aws-eventstream (1.2.0)
aws-partitions (1.734.0)
aws-record (2.10.1)
Expand Down Expand Up @@ -460,6 +461,7 @@ PLATFORMS
ruby

DEPENDENCIES
altcha-rails
aws-sdk-cloudwatch
aws-sdk-rails
aws-sdk-s3
Expand Down
5 changes: 5 additions & 0 deletions app/controllers/altcha_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AltchaController < ApplicationController
def new
render json: Altcha::Challenge.create.to_json
end
end
24 changes: 15 additions & 9 deletions app/controllers/notification_subscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ def index

def create
@group = NotificationSubscriptionGroup.new(notification_group_params.except(:more))
@group.user = current_user
@group.journey = Journey.find(params[:notification_subscription_group][:journey_id]) if params[:notification_subscription_group][:journey_id].present?

respond_to do |format|
if @group.save
format.html { redirect_to root_path }
format.js
else
format.js { render :new }
if AltchaSolution.verify_and_save(params.permit(:altcha)[:altcha])
@group.user = current_user
@group.journey = Journey.find(params[:notification_subscription_group][:journey_id]) if params[:notification_subscription_group][:journey_id].present?
respond_to do |format|
if @group.save
format.html { redirect_to root_path }
format.js
else
format.js { render :new }
end
end
else
respond_to do |format|
format.js { render :failure }
end
end

end

def confirm
Expand Down
28 changes: 28 additions & 0 deletions app/models/altcha_solution.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
class AltchaSolution < ApplicationRecord
validates :algorithm, :challenge, :salt, :signature, :number, presence: true
attr_accessor :took

def self.verify_and_save(base64encoded)
p = JSON.parse(Base64.decode64(base64encoded)) rescue nil
return false if p.nil?

submission = Altcha::Submission.new(p)
return false unless submission.valid?

solution = self.new(p)

begin
return solution.save
rescue ActiveRecord::RecordNotUnique
# Replay attack
return false
end
end

def self.cleanup
# Replay attacks are protected by the time stamp in the salt of the challenge for
# the duration configured in the timeout. All solutions in the database that older
# can be deleted.
AltchaSolution.where('created_at < ?', Time.now - Altcha.timeout).delete_all
end
end
1 change: 1 addition & 0 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<script defer data-domain="navody.digital" src="https://plausible.io/js/plausible.js"></script>
<script async defer src="https://cdn.jsdelivr.net/gh/altcha-org/altcha@main/dist/altcha.min.js" type="module"></script>
</head>

<body class="govuk-template__body">
Expand Down
2 changes: 2 additions & 0 deletions app/views/notification_subscriptions/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
<% if !form.journey.nil? and form.journey.blank? %>
<%= hidden_field_tag 'notification_subscription_group[journey_id]', form.journey.id %>
<% end %>
<altcha-widget hidelogo hidefooter strings="<%= JSON.dump({ 'label': 'Nie som robot', 'verifying': 'Overujem...', 'verified': 'Overené'}) %>" challengeurl="<%= altcha_url() %>"></altcha-widget>
<div class="altcha-error"></div>
<%= submit_tag 'Chcem dostávať tieto notifikácie', class: 'govuk-button' %>
</div>

Expand Down
1 change: 1 addition & 0 deletions app/views/notification_subscriptions/failure.js.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
$('.altcha-error').html('Nie sme si isti, či nie ste robot... zaškrtli ste, že nie ste?');
8 changes: 8 additions & 0 deletions config/initializers/altcha.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true

Altcha.setup do |config|
config.algorithm = 'SHA-256'
config.num_range = (50_000..300_000)
config.timeout = 5.minutes
config.hmac_key = 'dfa06d467a84fea13941f1c52c38c6458a67617a'
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Rails.application.routes.draw do

get '/altcha', to: 'altcha#new'
get :health, to: 'health#index'
get 'robots.:format', to: 'robots#index'

Expand Down
15 changes: 15 additions & 0 deletions db/migrate/20240427082705_create_altcha_solutions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
class CreateAltchaSolutions < ActiveRecord::Migration[6.1]
def change
create_table(:altcha_solutions) do |t|
t.string :algorithm
t.string :challenge
t.string :salt
t.string :signature
t.integer :number

t.timestamps
end

add_index :altcha_solutions, [ :algorithm, :challenge, :salt, :signature, :number ], unique: true, name: 'index_altcha_solutions'
end
end
53 changes: 52 additions & 1 deletion db/structure.sql
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,41 @@ CREATE SEQUENCE public.active_storage_variant_records_id_seq
ALTER SEQUENCE public.active_storage_variant_records_id_seq OWNED BY public.active_storage_variant_records.id;


--
-- Name: altcha_solutions; Type: TABLE; Schema: public; Owner: -
--

CREATE TABLE public.altcha_solutions (
id bigint NOT NULL,
algorithm character varying,
challenge character varying,
salt character varying,
signature character varying,
number integer,
created_at timestamp(6) without time zone NOT NULL,
updated_at timestamp(6) without time zone NOT NULL
);


--
-- Name: altcha_solutions_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--

CREATE SEQUENCE public.altcha_solutions_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;


--
-- Name: altcha_solutions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
--

ALTER SEQUENCE public.altcha_solutions_id_seq OWNED BY public.altcha_solutions.id;


--
-- Name: apps; Type: TABLE; Schema: public; Owner: -
--
Expand Down Expand Up @@ -1686,6 +1721,14 @@ ALTER TABLE ONLY public.active_storage_variant_records
ADD CONSTRAINT active_storage_variant_records_pkey PRIMARY KEY (id);


--
-- Name: altcha_solutions altcha_solutions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--

ALTER TABLE ONLY public.altcha_solutions
ADD CONSTRAINT altcha_solutions_pkey PRIMARY KEY (id);


--
-- Name: apps apps_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
Expand Down Expand Up @@ -1946,6 +1989,13 @@ CREATE UNIQUE INDEX index_active_storage_blobs_on_key ON public.active_storage_b
CREATE UNIQUE INDEX index_active_storage_variant_records_uniqueness ON public.active_storage_variant_records USING btree (blob_id, variation_digest);


--
-- Name: index_altcha_solutions; Type: INDEX; Schema: public; Owner: -
--

CREATE UNIQUE INDEX index_altcha_solutions ON public.altcha_solutions USING btree (algorithm, challenge, salt, signature, number);


--
-- Name: index_categories_categorizations; Type: INDEX; Schema: public; Owner: -
--
Expand Down Expand Up @@ -2486,6 +2536,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20221022143119'),
('20230325092744'),
('20230325095737'),
('20230325151049');
('20230325151049'),
('20240427082705');


0 comments on commit 29aa3de

Please sign in to comment.