diff --git a/Gemfile b/Gemfile index 661419ff..10d143b9 100644 --- a/Gemfile +++ b/Gemfile @@ -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' diff --git a/Gemfile.lock b/Gemfile.lock index dbcd617e..a6aae8cd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) @@ -460,6 +461,7 @@ PLATFORMS ruby DEPENDENCIES + altcha-rails aws-sdk-cloudwatch aws-sdk-rails aws-sdk-s3 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index a870b1ef..64e522b3 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,7 +15,6 @@ //= require activestorage //= require turbolinks //= require cookieconsent.min -//= require newsletter_sign_up //= require jscookie $(document).on("turbolinks:load", function () { diff --git a/app/assets/javascripts/newsletter_sign_up.js b/app/assets/javascripts/newsletter_sign_up.js deleted file mode 100644 index dda5cfb3..00000000 --- a/app/assets/javascripts/newsletter_sign_up.js +++ /dev/null @@ -1,42 +0,0 @@ -$(document).on('turbolinks:load', function () { - $('#newsletter-form').submit(function (evt) { - evt.preventDefault(); - - var form = $(evt.target); - var data = form.serialize(); - var button = form.find('button')[0]; - button.innerHTML = 'Prihlasujem...'; - - $.ajax({ - type: 'POST', - url: form.data('action'), - data: data, - dataType: 'json', - success: function (data) { - if (data.result !== undefined) { - if (data.result.result === 'success') { - form.remove(); - $('#newsletter-success').show(); - if ($('#newsletter-warning').is(':visible')) { - $('#newsletter-warning').hide(); - } - } else { - var warningText = null; - if (data.result.result === 'emailExist') { - warningText = data.result.exist_err_msg; - } else if (data.result.result === 'invalidEmail') { - warningText = data.result.invalid_err_msg; - } else { - warningText = 'Prihlásenie do newslettera sa nepodarilo. Prosím skúste znova.' - } - $('#newsletter-warning').show(); - $('#newsletter-warning strong').text(warningText); - } - } - }, - complete: function () { - button.innerHTML = 'Prihlásiť' - } - }); - }); -}); diff --git a/app/controllers/altcha_controller.rb b/app/controllers/altcha_controller.rb new file mode 100644 index 00000000..cd31d3a2 --- /dev/null +++ b/app/controllers/altcha_controller.rb @@ -0,0 +1,5 @@ +class AltchaController < ApplicationController + def new + render json: Altcha::Challenge.create.to_json + end +end diff --git a/app/controllers/newsletter_subscriptions_controller.rb b/app/controllers/newsletter_subscriptions_controller.rb new file mode 100644 index 00000000..3f06c788 --- /dev/null +++ b/app/controllers/newsletter_subscriptions_controller.rb @@ -0,0 +1,22 @@ +class NewsletterSubscriptionsController < ApplicationController + + def subscribe + email, altcha = subscription_params + if AltchaSolution.verify_and_save(altcha) + SubscribeToNewsletterJob.perform_later(email, 'NewsletterSubscription') + respond_to do |format| + format.js { render :success} + end + else + respond_to do |format| + format.js { render :altcha_failure, status: :unprocessable_entity} + end + end + end + + private + + def subscription_params + params.require([:email, :altcha]) + end +end diff --git a/app/controllers/notification_subscriptions_controller.rb b/app/controllers/notification_subscriptions_controller.rb index 42da9231..f8d845da 100644 --- a/app/controllers/notification_subscriptions_controller.rb +++ b/app/controllers/notification_subscriptions_controller.rb @@ -7,17 +7,22 @@ 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.js + else + format.js { render :new } + end + end + else + respond_to do |format| + format.js { render :failure, status: :unprocessable_entity} end end + end def confirm diff --git a/app/models/altcha_solution.rb b/app/models/altcha_solution.rb new file mode 100644 index 00000000..5f6143c1 --- /dev/null +++ b/app/models/altcha_solution.rb @@ -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 diff --git a/app/views/components/_footer.html.erb b/app/views/components/_footer.html.erb index fa0f92c5..ea5ed53b 100644 --- a/app/views/components/_footer.html.erb +++ b/app/views/components/_footer.html.erb @@ -5,7 +5,7 @@
- + <%= render 'newsletter_subscriptions/form' %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 703f7e93..c44cc2bb 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -21,6 +21,7 @@ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + diff --git a/app/views/newsletter_subscriptions/_form.html.erb b/app/views/newsletter_subscriptions/_form.html.erb new file mode 100644 index 00000000..0036e564 --- /dev/null +++ b/app/views/newsletter_subscriptions/_form.html.erb @@ -0,0 +1,6 @@ +<%= form_tag newsletter_subscribe_path, method: :post, remote: true, id: "newsletter-form" do |f| %> + <%= email_field_tag :email, '', class: 'govuk-input sdn-footer__newsletter-input', placeholder: "Zadajte emailovú adresu", autocomplete: :email %> + <%= submit_tag 'Prihlásiť', class: 'govuk-button sdn-footer__newsletter-button' %> +