diff --git a/Gemfile.lock b/Gemfile.lock index 6dbc59a9c..8aeda5b11 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -250,7 +250,7 @@ GEM thor (>= 0.14, < 2.0) jquery-ui-rails (6.0.1) railties (>= 3.2.16) - json (2.3.1) + json (2.6.1) kaminari (1.2.1) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.1) @@ -392,7 +392,7 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.10.1) ffi (~> 1.0) - recaptcha (4.14.0) + recaptcha (5.8.1) json recipient_interceptor (0.2.0) mail diff --git a/app/assets/images/captcha_loading.gif b/app/assets/images/captcha_loading.gif new file mode 100644 index 000000000..d7db28fab Binary files /dev/null and b/app/assets/images/captcha_loading.gif differ diff --git a/app/assets/javascripts/lumen/captcha_gateway.js.coffee b/app/assets/javascripts/lumen/captcha_gateway.js.coffee new file mode 100644 index 000000000..c2995e0c9 --- /dev/null +++ b/app/assets/javascripts/lumen/captcha_gateway.js.coffee @@ -0,0 +1,7 @@ +$ -> + if $('.captcha-gateway') + checkFormCaptchaReady = setInterval -> + if $('#g-recaptcha-response-data-gateway').val() + $('#captcha-gateway-form').submit() + clearInterval(checkFormCaptchaReady) + , 100 diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss index c26104bbb..580a5d648 100644 --- a/app/assets/stylesheets/application.css.scss +++ b/app/assets/stylesheets/application.css.scss @@ -49,6 +49,7 @@ @import 'token_urls/index'; @import 'media_mentions/index'; @import 'api_submitter_requests/index'; +@import 'captcha_gateway/index'; @import 'pages/pages'; diff --git a/app/assets/stylesheets/bitters/_forms.scss b/app/assets/stylesheets/bitters/_forms.scss index e3e554993..b35f788a9 100644 --- a/app/assets/stylesheets/bitters/_forms.scss +++ b/app/assets/stylesheets/bitters/_forms.scss @@ -112,6 +112,10 @@ input[type="submit"]:not(.reset) { } } +button[disabled] { + cursor: not-allowed; +} + input { @include placeholder { color: #AEB6C2; diff --git a/app/assets/stylesheets/captcha_gateway/index.scss b/app/assets/stylesheets/captcha_gateway/index.scss new file mode 100644 index 000000000..13ee1096f --- /dev/null +++ b/app/assets/stylesheets/captcha_gateway/index.scss @@ -0,0 +1,9 @@ +.captcha-gateway { + .main { + padding: 35px; + + img { + padding: 15px; + } + } +} diff --git a/app/controllers/api_submitter_requests_controller.rb b/app/controllers/api_submitter_requests_controller.rb index f991f2408..ee7ca1151 100644 --- a/app/controllers/api_submitter_requests_controller.rb +++ b/app/controllers/api_submitter_requests_controller.rb @@ -1,6 +1,4 @@ class ApiSubmitterRequestsController < ApplicationController - include Recaptcha::ClientHelper - def new @api_submitter_request = ApiSubmitterRequest.new end @@ -56,13 +54,14 @@ def api_submitter_request_params end def validate - unless verify_recaptcha(model: @api_submitter_request) + unless verify_recaptcha(action: 'new_submitter_request', minimum_score: 0.5) + flash.delete(:recaptcha_error) + return { status: false, why: 'Captcha verification failed, please try again.' } end - { status: true } diff --git a/app/controllers/captcha_gateway_controller.rb b/app/controllers/captcha_gateway_controller.rb new file mode 100644 index 000000000..1b87689ce --- /dev/null +++ b/app/controllers/captcha_gateway_controller.rb @@ -0,0 +1,21 @@ +class CaptchaGatewayController < ApplicationController + def index + redirect_to root_path and return if params[:destination].nil? + + if params.dig('g-recaptcha-response-data', 'gateway') + success_captcha = verify_recaptcha(action: 'gateway', minimum_score: 0.5) + + captcha_gateway_logger = Logger.new("#{Rails.root}/log/captcha_gateway_logger.log") + captcha_gateway_logger.info(recaptcha_reply.inspect) + + if success_captcha + session[:captcha_permission] = Time.now + ENV['CAPTCHA_GATEWAY_PERMISSION_TIME'].to_i.seconds + redirect_to CGI.unescape(params[:destination]) and return + else + flash.delete(:recaptcha_error) + flash.alert = 'Sorry, we don\'t this that you are a human, if you think this is an error please contact our team at team@lumendatabase.org.' + redirect_to root_path and return + end + end + end +end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 0aad64961..8d2f1c257 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -18,6 +18,19 @@ class SearchController < ApplicationController URL_ROOT = nil def index + if request.format.html? && current_user.nil? && !Rails.env.test? + permitted = false + + if session[:captcha_permission] + time_permission = session[:captcha_permission] + permitted = true if time_permission > Time.now + end + + unless permitted + redirect_to(captcha_gateway_index_path(destination: CGI.escape(request.original_url))) and return + end + end + @searcher = item_searcher @searchdata = @searcher.search @wrapped_instances = wrap_instances diff --git a/app/controllers/submitter_widget_notices_controller.rb b/app/controllers/submitter_widget_notices_controller.rb index a104cac0f..f0d4f721e 100644 --- a/app/controllers/submitter_widget_notices_controller.rb +++ b/app/controllers/submitter_widget_notices_controller.rb @@ -1,6 +1,4 @@ class SubmitterWidgetNoticesController < NoticesController - include Recaptcha::ClientHelper - layout 'submitter_widget' before_action :before_actions @@ -36,7 +34,8 @@ def create get_notice_type(params), notice_params, submitter_widget_user ).build - unless verify_recaptcha(model: @notice) + unless verify_recaptcha(action: 'submitter_widget_new_notice', minimum_score: 0.5) + flash.delete(:recaptcha_error) flash.alert = 'Captcha verification failed, please try again.' strip_fixed_roles and render 'notices/submitter_widget/new' and return end diff --git a/app/controllers/token_urls_controller.rb b/app/controllers/token_urls_controller.rb index 15af1bc58..a7b0b9fd1 100644 --- a/app/controllers/token_urls_controller.rb +++ b/app/controllers/token_urls_controller.rb @@ -3,8 +3,6 @@ require 'hasher' class TokenUrlsController < ApplicationController - include Recaptcha::ClientHelper - IP_BETWEEN_REQUESTS_WAITING_TIME = 2.hours def new @@ -133,7 +131,9 @@ def validate } end - unless verify_recaptcha(model: @token_url) + unless verify_recaptcha(action: 'new_token_url', minimum_score: 0.5) + flash.delete(:recaptcha_error) + return { status: false, why: 'Captcha verification failed, please try again.' diff --git a/app/models/elasticsearch/elasticsearch_query.rb b/app/models/elasticsearch/elasticsearch_query.rb index ef1d490b5..73058ee08 100644 --- a/app/models/elasticsearch/elasticsearch_query.rb +++ b/app/models/elasticsearch/elasticsearch_query.rb @@ -71,7 +71,7 @@ def search # Adding a datestamp guarantees that the cache_key eventually expires. def cache_key is_super_admin = Current.user&.role?(Role.super_admin) - @cache_key ||= "search-result-#{Digest::MD5.hexdigest(params.values.to_s)}-#{Date.today}-#{is_super_admin}" + @cache_key ||= "search-result-#{Digest::MD5.hexdigest(params.except('g-recaptcha-response-data', 'g-recaptcha-response').values.to_s)}-#{Date.today}-#{is_super_admin}" end private diff --git a/app/views/api_submitter_requests/new.html.erb b/app/views/api_submitter_requests/new.html.erb index d83f08485..1c69809a3 100644 --- a/app/views/api_submitter_requests/new.html.erb +++ b/app/views/api_submitter_requests/new.html.erb @@ -35,7 +35,7 @@ <%= f.input :entity_url, label: 'URL' %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'new_submitter_request') %>
diff --git a/app/views/captcha_gateway/index.html.erb b/app/views/captcha_gateway/index.html.erb new file mode 100644 index 000000000..5b4994987 --- /dev/null +++ b/app/views/captcha_gateway/index.html.erb @@ -0,0 +1,18 @@ +<% title 'Are you a human?' %> + +
+
+
+

We need to check if you are a human, wait a few seconds please.

+

You will be automatically redirected to your destination page.

+
+ <%= image_tag '/assets/captcha_loading.gif' %> +
+ +
+ <%= recaptcha_v3(action: 'gateway') %> + +
+
+
+
diff --git a/app/views/notices/submitter_widget/_counterfeit_form.html.erb b/app/views/notices/submitter_widget/_counterfeit_form.html.erb index 6f322df70..64ae44c8a 100644 --- a/app/views/notices/submitter_widget/_counterfeit_form.html.erb +++ b/app/views/notices/submitter_widget/_counterfeit_form.html.erb @@ -30,7 +30,7 @@ label: 'Counterfeit notice', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_counternotice_form.html.erb b/app/views/notices/submitter_widget/_counternotice_form.html.erb index a017dcde3..9d1799871 100644 --- a/app/views/notices/submitter_widget/_counternotice_form.html.erb +++ b/app/views/notices/submitter_widget/_counternotice_form.html.erb @@ -42,7 +42,7 @@ <%= render 'notices/submitter_widget/form_components/roles', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_court_order_form.html.erb b/app/views/notices/submitter_widget/_court_order_form.html.erb index b2bba873f..10ba10708 100644 --- a/app/views/notices/submitter_widget/_court_order_form.html.erb +++ b/app/views/notices/submitter_widget/_court_order_form.html.erb @@ -28,7 +28,7 @@ <%= render 'notices/submitter_widget/form_components/roles', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_defamation_form.html.erb b/app/views/notices/submitter_widget/_defamation_form.html.erb index 0d1a359d0..be8318509 100644 --- a/app/views/notices/submitter_widget/_defamation_form.html.erb +++ b/app/views/notices/submitter_widget/_defamation_form.html.erb @@ -28,7 +28,7 @@ label: 'Defamation takedown notice', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_dmca_form.html.erb b/app/views/notices/submitter_widget/_dmca_form.html.erb index e06539db7..b291bd73a 100644 --- a/app/views/notices/submitter_widget/_dmca_form.html.erb +++ b/app/views/notices/submitter_widget/_dmca_form.html.erb @@ -32,7 +32,7 @@ label: 'DMCA takedown notice', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_government_request_form.html.erb b/app/views/notices/submitter_widget/_government_request_form.html.erb index beafe69c8..8188ef228 100644 --- a/app/views/notices/submitter_widget/_government_request_form.html.erb +++ b/app/views/notices/submitter_widget/_government_request_form.html.erb @@ -27,7 +27,7 @@ <%= render 'notices/submitter_widget/form_components/roles', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_law_enforcement_request_form.html.erb b/app/views/notices/submitter_widget/_law_enforcement_request_form.html.erb index 426c19524..97c4c2562 100644 --- a/app/views/notices/submitter_widget/_law_enforcement_request_form.html.erb +++ b/app/views/notices/submitter_widget/_law_enforcement_request_form.html.erb @@ -29,7 +29,7 @@ <%= render 'notices/submitter_widget/form_components/roles', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_other_form.html.erb b/app/views/notices/submitter_widget/_other_form.html.erb index 80b2c7cb0..1b4ac3e2c 100644 --- a/app/views/notices/submitter_widget/_other_form.html.erb +++ b/app/views/notices/submitter_widget/_other_form.html.erb @@ -30,7 +30,7 @@ label: 'Notice', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_private_information_form.html.erb b/app/views/notices/submitter_widget/_private_information_form.html.erb index 3579d170b..91506fd07 100644 --- a/app/views/notices/submitter_widget/_private_information_form.html.erb +++ b/app/views/notices/submitter_widget/_private_information_form.html.erb @@ -28,7 +28,7 @@ label: 'Private Information notice', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/notices/submitter_widget/_trademark_form.html.erb b/app/views/notices/submitter_widget/_trademark_form.html.erb index c51b73ddd..90ec28420 100644 --- a/app/views/notices/submitter_widget/_trademark_form.html.erb +++ b/app/views/notices/submitter_widget/_trademark_form.html.erb @@ -34,7 +34,7 @@ label: 'Trademark takedown notice', form: form %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'submitter_widget_new_notice') %> <%= render 'notices/form_components/submit', form: form %> <% end %> diff --git a/app/views/search/index.html.erb b/app/views/search/index.html.erb index 32166b6e9..5999c2e3d 100644 --- a/app/views/search/index.html.erb +++ b/app/views/search/index.html.erb @@ -1,4 +1,5 @@ <%= title 'Search' %> + <%= cache(@searcher.cache_key) do %>
diff --git a/app/views/shared/_header_search.html.erb b/app/views/shared/_header_search.html.erb index 09408d0aa..5c09e922d 100644 --- a/app/views/shared/_header_search.html.erb +++ b/app/views/shared/_header_search.html.erb @@ -12,7 +12,7 @@ <% end %> Add more
- +
diff --git a/app/views/shared/_search.html.erb b/app/views/shared/_search.html.erb index e1369fa7c..f3761b8ff 100644 --- a/app/views/shared/_search.html.erb +++ b/app/views/shared/_search.html.erb @@ -4,7 +4,7 @@ <%= text_field_tag :term, params[:term], id: "search", type: 'search', placeholder: @search_all_placeholder %>
- + diff --git a/app/views/token_urls/new.html.erb b/app/views/token_urls/new.html.erb index 1e495278f..c49ad45cc 100644 --- a/app/views/token_urls/new.html.erb +++ b/app/views/token_urls/new.html.erb @@ -12,7 +12,7 @@ <%= f.input :email, label: 'Email address', required: true %> <%= f.input :documents_notification, as: :boolean, inline_label: 'Select to get a notification when new notice documents are added (or when existing notice documents are updated).', label: false %> - <%= recaptcha_tags %> + <%= recaptcha_v3(action: 'new_token_url') %>
diff --git a/config/initializers/new_framework_defaults_5_2.rb b/config/initializers/new_framework_defaults_5_2.rb deleted file mode 100644 index c383d072b..000000000 --- a/config/initializers/new_framework_defaults_5_2.rb +++ /dev/null @@ -1,38 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.2 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Make Active Record use stable #cache_key alongside new #cache_version method. -# This is needed for recyclable cache keys. -# Rails.application.config.active_record.cache_versioning = true - -# Use AES-256-GCM authenticated encryption for encrypted cookies. -# Also, embed cookie expiry in signed or encrypted cookies for increased security. -# -# This option is not backwards compatible with earlier Rails versions. -# It's best enabled when your entire app is migrated and stable on 5.2. -# -# Existing cookies will be converted on read then written with the new scheme. -# Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true - -# Use AES-256-GCM authenticated encryption as default cipher for encrypting messages -# instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. -# Rails.application.config.active_support.use_authenticated_message_encryption = true - -# Add default protection from forgery to ActionController::Base instead of in -# ApplicationController. -# Rails.application.config.action_controller.default_protect_from_forgery = true - -# Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and -# 'f' after migrating old data. -# Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true - -# Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. -# Rails.application.config.active_support.use_sha1_digests = true - -# Make `form_with` generate id attributes for any generated HTML tags. -# Rails.application.config.action_view.form_with_generates_ids = true diff --git a/config/initializers/new_framework_defaults_6_0.rb b/config/initializers/new_framework_defaults_6_0.rb deleted file mode 100644 index 92240ef5f..000000000 --- a/config/initializers/new_framework_defaults_6_0.rb +++ /dev/null @@ -1,45 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 6.0 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Don't force requests from old versions of IE to be UTF-8 encoded. -# Rails.application.config.action_view.default_enforce_utf8 = false - -# Embed purpose and expiry metadata inside signed and encrypted -# cookies for increased security. -# -# This option is not backwards compatible with earlier Rails versions. -# It's best enabled when your entire app is migrated and stable on 6.0. -# Rails.application.config.action_dispatch.use_cookies_with_metadata = true - -# Change the return value of `ActionDispatch::Response#content_type` to Content-Type header without modification. -# Rails.application.config.action_dispatch.return_only_media_type_on_content_type = false - -# Return false instead of self when enqueuing is aborted from a callback. -# Rails.application.config.active_job.return_false_on_aborted_enqueue = true - -# Send Active Storage analysis and purge jobs to dedicated queues. -# Rails.application.config.active_storage.queues.analysis = :active_storage_analysis -# Rails.application.config.active_storage.queues.purge = :active_storage_purge - -# When assigning to a collection of attachments declared via `has_many_attached`, replace existing -# attachments instead of appending. Use #attach to add new attachments without replacing existing ones. -# Rails.application.config.active_storage.replace_on_assign_to_many = true - -# Use ActionMailer::MailDeliveryJob for sending parameterized and normal mail. -# -# The default delivery jobs (ActionMailer::Parameterized::DeliveryJob, ActionMailer::DeliveryJob), -# will be removed in Rails 6.1. This setting is not backwards compatible with earlier Rails versions. -# If you send mail in the background, job workers need to have a copy of -# MailDeliveryJob to ensure all delivery jobs are processed properly. -# Make sure your entire app is migrated and stable on 6.0 before using this setting. -# Rails.application.config.action_mailer.delivery_job = "ActionMailer::MailDeliveryJob" - -# Enable the same cache key to be reused when the object being cached of type -# `ActiveRecord::Relation` changes by moving the volatile information (max updated at and count) -# of the relation's cache key into the cache version to support recycling cache key. -# Rails.application.config.active_record.collection_cache_versioning = true diff --git a/config/initializers/new_framework_defaults_6_1.rb b/config/initializers/new_framework_defaults_6_1.rb deleted file mode 100644 index 9526b835a..000000000 --- a/config/initializers/new_framework_defaults_6_1.rb +++ /dev/null @@ -1,67 +0,0 @@ -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 6.1 upgrade. -# -# Once upgraded flip defaults one by one to migrate to the new default. -# -# Read the Guide for Upgrading Ruby on Rails for more info on each option. - -# Support for inversing belongs_to -> has_many Active Record associations. -# Rails.application.config.active_record.has_many_inversing = true - -# Track Active Storage variants in the database. -# Rails.application.config.active_storage.track_variants = true - -# Apply random variation to the delay when retrying failed jobs. -# Rails.application.config.active_job.retry_jitter = 0.15 - -# Stop executing `after_enqueue`/`after_perform` callbacks if -# `before_enqueue`/`before_perform` respectively halts with `throw :abort`. -# Rails.application.config.active_job.skip_after_callbacks_if_terminated = true - -# Specify cookies SameSite protection level: either :none, :lax, or :strict. -# -# This change is not backwards compatible with earlier Rails versions. -# It's best enabled when your entire app is migrated and stable on 6.1. -# Rails.application.config.action_dispatch.cookies_same_site_protection = :lax - -# Generate CSRF tokens that are encoded in URL-safe Base64. -# -# This change is not backwards compatible with earlier Rails versions. -# It's best enabled when your entire app is migrated and stable on 6.1. -# Rails.application.config.action_controller.urlsafe_csrf_tokens = true - -# Specify whether `ActiveSupport::TimeZone.utc_to_local` returns a time with an -# UTC offset or a UTC time. -# ActiveSupport.utc_to_local_returns_utc_offset_times = true - -# Change the default HTTP status code to `308` when redirecting non-GET/HEAD -# requests to HTTPS in `ActionDispatch::SSL` middleware. -# Rails.application.config.action_dispatch.ssl_default_redirect_status = 308 - -# Use new connection handling API. For most applications this won't have any -# effect. For applications using multiple databases, this new API provides -# support for granular connection swapping. -# Rails.application.config.active_record.legacy_connection_handling = false - -# Make `form_with` generate non-remote forms by default. -# Rails.application.config.action_view.form_with_generates_remote_forms = false - -# Set the default queue name for the analysis job to the queue adapter default. -# Rails.application.config.active_storage.queues.analysis = nil - -# Set the default queue name for the purge job to the queue adapter default. -# Rails.application.config.active_storage.queues.purge = nil - -# Set the default queue name for the incineration job to the queue adapter default. -# Rails.application.config.action_mailbox.queues.incineration = nil - -# Set the default queue name for the routing job to the queue adapter default. -# Rails.application.config.action_mailbox.queues.routing = nil - -# Set the default queue name for the mail deliver job to the queue adapter default. -# Rails.application.config.action_mailer.deliver_later_queue_name = nil - -# Generate a `Link` header that gives a hint to modern browsers about -# preloading assets when using `javascript_include_tag` and `stylesheet_link_tag`. -# Rails.application.config.action_view.preload_links_header = true diff --git a/config/initializers/rack-attack.rb b/config/initializers/rack-attack.rb index b66d9762d..1d86c23cd 100644 --- a/config/initializers/rack-attack.rb +++ b/config/initializers/rack-attack.rb @@ -71,6 +71,17 @@ class Rack::Attack req.ip if throttled_path?(req) end + # Captcha gateway gives access for 10 minutes, we don't need to let for more + # than 6 per hour then. + throttle('unauthed captcha gateway request limit per hour', + limit: 6, + period: 1.hour) do |req| + next if req.authenticated? + + Rails.logger.debug "[rack-attack] request limit ip: #{req.ip}, content_type: #{req.content_type}" + req.ip if req.path.include?('captcha_gateway') + end + self.throttled_response = lambda do |_env| Rails.logger.warn "[rack-attack] 429 issued for #{_env['rack.attack.match_discriminator']}" [ diff --git a/config/initializers/rails_admin.rb b/config/initializers/rails_admin.rb index 5f9418518..fd14f541a 100644 --- a/config/initializers/rails_admin.rb +++ b/config/initializers/rails_admin.rb @@ -1,531 +1,529 @@ -Rails.application.config.to_prepare do - require 'rails_admin/config/actions/redact_queue' - require 'rails_admin/config/actions/redact_notice' - require 'rails_admin/config/actions/pdf_requests' - require 'rails_admin/config/actions/statistics' - require 'rails_admin/config/actions/approve_api_submitter_request' - require 'rails_admin/config/actions/reject_api_submitter_request' - require 'rails_admin/config/actions/top_notices_token_urls' - require 'rails_admin/config/fields/types/datetime_timezoned' - - RailsAdmin.config do |config| - config.parent_controller = '::ApplicationController' - - config.main_app_name = ['Lumen Database', 'Admin'] - - config.current_user_method { current_user } +require 'rails_admin/config/actions/redact_queue' +require 'rails_admin/config/actions/redact_notice' +require 'rails_admin/config/actions/pdf_requests' +require 'rails_admin/config/actions/statistics' +require 'rails_admin/config/actions/approve_api_submitter_request' +require 'rails_admin/config/actions/reject_api_submitter_request' +require 'rails_admin/config/actions/top_notices_token_urls' +require 'rails_admin/config/fields/types/datetime_timezoned' + +RailsAdmin.config do |config| + config.parent_controller = '::ApplicationController' + + config.main_app_name = ['Lumen Database', 'Admin'] + + config.current_user_method { current_user } + + config.authorize_with :cancancan + + config.audit_with :history, 'User' + config.audit_with :history, 'Role' + config.audit_with :history, 'Notice' + + boolean_true_icon = ''.html_safe + boolean_false_icon = ''.html_safe + + config.actions do + dashboard do + statistics false + end + + # collection-wide actions + index + new + export + history_index + bulk_delete + + # member actions + show + edit + delete + history_show + show_in_app + + init_actions! + + redact_queue + redact_notice + pdf_requests + statistics + approve_api_submitter_request + reject_api_submitter_request + top_notices_token_urls + end - config.authorize_with :cancancan + ['Notice', Notice::TYPES].flatten.each do |notice_type| + config.audit_with :history, notice_type - config.audit_with :history, 'User' - config.audit_with :history, 'Role' - config.audit_with :history, 'Notice' + config.model notice_type do + label { abstract_model.model.label } - boolean_true_icon = ''.html_safe - boolean_false_icon = ''.html_safe + list do + # SELECT COUNT is slow when the number of instances is large; let's + # avoid calling it for Notice and its subclasses. + limited_pagination true - config.actions do - dashboard do - statistics false + field :id + field :title + field(:date_sent) { label 'Sent' } + field(:date_received) { label 'Received' } + field(:created_at) { label 'Submitted' } + field(:original_notice_id) { label 'Legacy NoticeID' } + field :source + field :review_required + field :published + field :time_to_publish + field :body + field :entities + field :topics + field :works + field :url_count + field :action_taken + field :reviewer_id + field :language + field :rescinded + field :type + field :spam + field :hidden + field :request_type + field :webform + field :views_overall + field :views_by_notice_viewer + field :token_urls_count end - # collection-wide actions - index - new - export - history_index - bulk_delete - - # member actions - show - edit - delete - history_show - show_in_app - - init_actions! - - redact_queue - redact_notice - pdf_requests - statistics - approve_api_submitter_request - reject_api_submitter_request - top_notices_token_urls - end - - ['Notice', Notice::TYPES].flatten.each do |notice_type| - config.audit_with :history, notice_type - - config.model notice_type do - label { abstract_model.model.label } - - list do - # SELECT COUNT is slow when the number of instances is large; let's - # avoid calling it for Notice and its subclasses. - limited_pagination true - - field :id - field :title - field(:date_sent) { label 'Sent' } - field(:date_received) { label 'Received' } - field(:created_at) { label 'Submitted' } - field(:original_notice_id) { label 'Legacy NoticeID' } - field :source - field :review_required - field :published - field :time_to_publish - field :body - field :entities - field :topics - field :works - field :url_count - field :action_taken - field :reviewer_id - field :language - field :rescinded - field :type - field :spam - field :hidden - field :request_type - field :webform - field :views_overall - field :views_by_notice_viewer - field :token_urls_count - end - - show do - field :title - field :type - field :published - field :date_received - field :date_sent - field :source - field :subject - field :review_required - field :language - field :rescinded - field :spam - field :hidden - field :restricted_to_researchers do - formatted_value do - bindings[:object].restricted_to_researchers? ? boolean_true_icon : boolean_false_icon - end + show do + field :title + field :type + field :published + field :date_received + field :date_sent + field :source + field :subject + field :review_required + field :language + field :rescinded + field :spam + field :hidden + field :restricted_to_researchers do + formatted_value do + bindings[:object].restricted_to_researchers? ? boolean_true_icon : boolean_false_icon end - field :webform - field :views_overall - field :views_by_notice_viewer - field :temporary_token_urls do - formatted_value do - notice_token_urls_count_links(bindings) - end + end + field :webform + field :views_overall + field :views_by_notice_viewer + field :temporary_token_urls do + formatted_value do + notice_token_urls_count_links(bindings) end - field :permanent_token_urls do - formatted_value do - notice_token_urls_count_links(bindings, true) - end + end + field :permanent_token_urls do + formatted_value do + notice_token_urls_count_links(bindings, true) end - field :topics - field :entity_notice_roles - field :entities - field :works - field :file_uploads end + field :topics + field :entity_notice_roles + field :entities + field :works + field :file_uploads + end - edit do - # This dramatically speeds up the admin page. - configure :works do - nested_form false - end + edit do + # This dramatically speeds up the admin page. + configure :works do + nested_form false + end - configure :action_taken, :enum do - enum do - %w[Yes No Partial Unspecified] - end - default_value 'Unspecified' + configure :action_taken, :enum do + enum do + %w[Yes No Partial Unspecified] end + default_value 'Unspecified' + end - configure(:type) do - hide - end - configure :reset_type, :enum do - label 'Type' - required true - end + configure(:type) do + hide + end + configure :reset_type, :enum do + label 'Type' + required true + end - exclude_fields :topic_assignments, - :topic_relevant_questions, - :infringing_urls, - :copyrighted_urls, - :token_urls, - :entities - - configure :review_required do - visible do - ability = Ability.new(bindings[:view]._current_user) - ability.can? :publish, Notice - end + exclude_fields :topic_assignments, + :topic_relevant_questions, + :infringing_urls, + :copyrighted_urls, + :token_urls, + :entities + + configure :review_required do + visible do + ability = Ability.new(bindings[:view]._current_user) + ability.can? :publish, Notice end + end - configure :rescinded do - visible do - ability = Ability.new(bindings[:view]._current_user) - ability.can? :rescind, Notice - end + configure :rescinded do + visible do + ability = Ability.new(bindings[:view]._current_user) + ability.can? :rescind, Notice end end end end + end - config.model 'Topic' do - list do - field :id - field :name - field :parent do - formatted_value do - parent = bindings[:object].parent - parent && "#{parent.name} - ##{parent.id}" - end + config.model 'Topic' do + list do + field :id + field :name + field :parent do + formatted_value do + parent = bindings[:object].parent + parent && "#{parent.name} - ##{parent.id}" end end - edit do - # exclude_fields :notices might be a better performance option than hide, - # but it prevents topics with null ancestries from being saved. - configure(:notices) { hide } - configure(:topic_assignments) { hide } - - configure :parent_id, :enum do - enum_method do - :parent_enum - end + end + edit do + # exclude_fields :notices might be a better performance option than hide, + # but it prevents topics with null ancestries from being saved. + configure(:notices) { hide } + configure(:topic_assignments) { hide } + + configure :parent_id, :enum do + enum_method do + :parent_enum end end end + end - config.model 'EntityNoticeRole' do - edit do - configure(:notice) { hide } - configure :entity do - nested_form false - end + config.model 'EntityNoticeRole' do + edit do + configure(:notice) { hide } + configure :entity do + nested_form false end end + end - config.model 'Entity' do - list do - # See exclude_fields comment for Topic. - exclude_fields :notices - configure(:entity_notice_roles) { hide } - configure :parent do - formatted_value do - parent = bindings[:object].parent - parent && "#{parent.name} - ##{parent.id}" - end + config.model 'Entity' do + list do + # See exclude_fields comment for Topic. + exclude_fields :notices + configure(:entity_notice_roles) { hide } + configure :parent do + formatted_value do + parent = bindings[:object].parent + parent && "#{parent.name} - ##{parent.id}" end end - edit do - configure :kind, :enum do - enum do - %w[individual organization] - end - default_value 'organization' + end + edit do + configure :kind, :enum do + enum do + %w[individual organization] end - configure(:notices) { hide } - configure(:entity_notice_roles) { hide } - configure(:ancestry) { hide } - # Unfortunately, there are too many entities to make parents editable - # via default rails_admin functionality. - # configure :parent_id, :enum do - # enum_method do - # :parent_enum - # end - # end + default_value 'organization' end + configure(:notices) { hide } + configure(:entity_notice_roles) { hide } + configure(:ancestry) { hide } + # Unfortunately, there are too many entities to make parents editable + # via default rails_admin functionality. + # configure :parent_id, :enum do + # enum_method do + # :parent_enum + # end + # end end + end - config.model 'RelevantQuestion' do - object_label_method { :question } - end - - config.model 'Work' do - object_label_method { :custom_work_label } - - edit do - configure(:notices) { hide } - end + config.model 'RelevantQuestion' do + object_label_method { :question } + end - list do - limited_pagination true - configure(:copyrighted_urls) { hide } - configure(:infringing_urls) { hide } - end + config.model 'Work' do + object_label_method { :custom_work_label } - nested do - configure(:infringing_urls) { hide } - configure(:copyrighted_urls) { hide } - end + edit do + configure(:notices) { hide } end - config.model 'InfringingUrl' do - object_label_method { :url } - - list do - limited_pagination true - end + list do + limited_pagination true + configure(:copyrighted_urls) { hide } + configure(:infringing_urls) { hide } end - config.model 'FileUpload' do - edit do - configure :kind, :enum do - enum do - %w[original supporting] - end - end - end + nested do + configure(:infringing_urls) { hide } + configure(:copyrighted_urls) { hide } end + end - config.model 'ReindexRun' do - end + config.model 'InfringingUrl' do + object_label_method { :url } - def custom_work_label - %Q(#{self.id}: #{self.description && self.description[0,30]}...) + list do + limited_pagination true end + end - config.model 'User' do - object_label_method { :email } - edit do - configure :entity do - nested_form false + config.model 'FileUpload' do + edit do + configure :kind, :enum do + enum do + %w[original supporting] end - configure(:token_urls) { hide } - - field :email - field :password - field :password_confirmation - field :reset_password_sent_at - field :authentication_token - field :widget_public_key - field :publication_delay - field :can_generate_permanent_notice_token_urls - field :allow_generate_permanent_tokens_researchers_only_notices - field :full_notice_views_limit - field :full_notice_time_limit - field :viewed_notices - field :limit_notice_api_response - field :entity - field :roles - field :full_notice_only_researchers_entities - field :widget_submissions_forward_email end + end + end - list do - scopes [nil] + Role::NAMES.sort.map { |role| "#{role}s" } + config.model 'ReindexRun' do + end + + def custom_work_label + %Q(#{self.id}: #{self.description && self.description[0,30]}...) + end - field :email - field :entity - field :roles - field :created_at - field :full_notice_time_limit + config.model 'User' do + object_label_method { :email } + edit do + configure :entity do + nested_form false end + configure(:token_urls) { hide } + + field :email + field :password + field :password_confirmation + field :reset_password_sent_at + field :authentication_token + field :widget_public_key + field :publication_delay + field :can_generate_permanent_notice_token_urls + field :allow_generate_permanent_tokens_researchers_only_notices + field :full_notice_views_limit + field :full_notice_time_limit + field :viewed_notices + field :limit_notice_api_response + field :entity + field :roles + field :full_notice_only_researchers_entities + field :widget_submissions_forward_email + end + + list do + scopes [nil] + Role::NAMES.sort.map { |role| "#{role}s" } + + field :email + field :entity + field :roles + field :created_at + field :full_notice_time_limit end + end - config.model 'TokenUrl' do - token_url_config - end + config.model 'TokenUrl' do + token_url_config + end - config.model 'ArchivedTokenUrl' do - token_url_config - end + config.model 'ArchivedTokenUrl' do + token_url_config + end - config.model 'RiskTriggerCondition' do - edit do - configure :field, :enum do - enum do - RiskTriggerCondition::ALLOWED_FIELDS.sort - end + config.model 'RiskTriggerCondition' do + edit do + configure :field, :enum do + enum do + RiskTriggerCondition::ALLOWED_FIELDS.sort end - configure :matching_type, :enum do - enum do - RiskTriggerCondition::ALLOWED_MATCHING_TYPES - end + end + configure :matching_type, :enum do + enum do + RiskTriggerCondition::ALLOWED_MATCHING_TYPES end end end + end - config.model 'RiskTrigger' do - edit do - configure :matching_type, :enum do - enum do - RiskTrigger::ALLOWED_MATCHING_TYPES - end + config.model 'RiskTrigger' do + edit do + configure :matching_type, :enum do + enum do + RiskTrigger::ALLOWED_MATCHING_TYPES end end end + end - config.model 'LumenSetting' do - edit do - field :value - end + config.model 'LumenSetting' do + edit do + field :value end + end - config.model 'BlockedTokenUrlDomain' do - list do - field :name - field :comments - field :created_at - end + config.model 'BlockedTokenUrlDomain' do + list do + field :name + field :comments + field :created_at end + end - config.model 'BlockedTokenUrlIp' do - list do - field :address - field :comments - field :created_at - end + config.model 'BlockedTokenUrlIp' do + list do + field :address + field :comments + field :created_at end + end - config.model 'MediaMention' do - edit do - configure :scale_of_mention, :enum do - enum do - LumenSetting.get('media_mentions_scale_of_mentions').split(',') - end + config.model 'MediaMention' do + edit do + configure :scale_of_mention, :enum do + enum do + LumenSetting.get('media_mentions_scale_of_mentions').split(',') end - - field :title - field :author - field :description - field :source - field :link_to_source - field :scale_of_mention - field :date - field :document_type - field :comments - field :published - end - end - - config.model 'ApiSubmitterRequest' do - list do - field :id - field :email - field :entity_name - field :entity_url - field :user - field :approved end - edit do - field :email - field :submissions_forward_email - field :approved - field :entity_url - field :description - field :admin_notes - field :entity_name - field :entity_kind - field :entity_address_line_1 - field :entity_address_line_2 - field :entity_state - field :entity_country_code - field :entity_phone - field :entity_url - field :entity_email - field :entity_city - field :entity_zip - field :user - end + field :title + field :author + field :description + field :source + field :link_to_source + field :scale_of_mention + field :date + field :document_type + field :comments + field :published end + end - # Hide unused models from the admin - # == START ============================================================ - config.model 'ReindexRun' do - visible false - end - config.model 'NoticeImportError' do - visible false - end - config.model 'DocumentsUpdateNotificationNotice' do - visible false - end - config.model 'YoutubeImportError' do - visible false - end - config.model 'YtImport' do - visible false + config.model 'ApiSubmitterRequest' do + list do + field :id + field :email + field :entity_name + field :entity_url + field :user + field :approved + end + + edit do + field :email + field :submissions_forward_email + field :approved + field :entity_url + field :description + field :admin_notes + field :entity_name + field :entity_kind + field :entity_address_line_1 + field :entity_address_line_2 + field :entity_state + field :entity_country_code + field :entity_phone + field :entity_url + field :entity_email + field :entity_city + field :entity_zip + field :user end - config.model 'YoutubeImportFileLocation' do - visible false - end - config.model 'ActiveStorage::Blob' do - visible false - end - config.model 'ActiveStorage::Attachment' do - visible false - end - config.model 'ActiveStorage::VariantRecord' do - visible false - end - config.model 'Comfy::Cms::Categorization' do - visible false - end - config.model 'Comfy::Cms::Category' do - visible false - end - config.model 'Comfy::Cms::File' do - visible false - end - config.model 'Comfy::Cms::Fragment' do - visible false - end - config.model 'Comfy::Cms::Layout' do - visible false - end - config.model 'Comfy::Cms::Page' do - visible false - end - config.model 'Comfy::Cms::Revision' do - visible false - end - config.model 'Comfy::Cms::Site' do - visible false - end - config.model 'Comfy::Cms::Snippet' do - visible false - end - config.model 'Comfy::Cms::Translation' do - visible false - end - # == END ============================================================ + end - def notice_token_urls_count_links(bindings, perm = false) - bindings[:object].token_urls.where(valid_forever: perm).count - end + # Hide unused models from the admin + # == START ============================================================ + config.model 'ReindexRun' do + visible false + end + config.model 'NoticeImportError' do + visible false + end + config.model 'DocumentsUpdateNotificationNotice' do + visible false + end + config.model 'YoutubeImportError' do + visible false + end + config.model 'YtImport' do + visible false + end + config.model 'YoutubeImportFileLocation' do + visible false + end + config.model 'ActiveStorage::Blob' do + visible false + end + config.model 'ActiveStorage::Attachment' do + visible false + end + config.model 'ActiveStorage::VariantRecord' do + visible false + end + config.model 'Comfy::Cms::Categorization' do + visible false + end + config.model 'Comfy::Cms::Category' do + visible false + end + config.model 'Comfy::Cms::File' do + visible false + end + config.model 'Comfy::Cms::Fragment' do + visible false + end + config.model 'Comfy::Cms::Layout' do + visible false + end + config.model 'Comfy::Cms::Page' do + visible false + end + config.model 'Comfy::Cms::Revision' do + visible false + end + config.model 'Comfy::Cms::Site' do + visible false + end + config.model 'Comfy::Cms::Snippet' do + visible false + end + config.model 'Comfy::Cms::Translation' do + visible false + end + # == END ============================================================ - def token_url_config - list do - field :email - field :user - field :notice - field :expiration_date - field(:valid_forever) { label 'Permenent' } - field :views - field :created_at - field :ip - end + def notice_token_urls_count_links(bindings, perm = false) + bindings[:object].token_urls.where(valid_forever: perm).count + end - edit do - field :email do - required false - end - field :user - field :notice do - required true - end - field :expiration_date - field(:valid_forever) { label 'Permenent' } - field :documents_notification + def token_url_config + list do + field :email + field :user + field :notice + field :expiration_date + field(:valid_forever) { label 'Permenent' } + field :views + field :created_at + field :ip + end + + edit do + field :email do + required false + end + field :user + field :notice do + required true end + field :expiration_date + field(:valid_forever) { label 'Permenent' } + field :documents_notification end end end diff --git a/config/routes.rb b/config/routes.rb index 2959aa6f1..0ba443e0f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -74,6 +74,8 @@ resources :api_submitter_requests + resources :captcha_gateway, only: :index + root to: 'home#index' comfy_route :cms_admin, path: "/cms_admin" diff --git a/public/robots.txt b/public/robots.txt index a162fa872..bf5633029 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -8,15 +8,18 @@ Allow: /$ Allow: /pages Disallow: /notices Disallow: /faceted_search +Disallow: /captcha_gateway User-agent: ia_archiver Allow: / Disallow: /faceted_search +Disallow: /captcha_gateway User-agent: * Disallow: / Disallow: /notices Disallow: /faceted_search +Disallow: /captcha_gateway Allow: /pages Allow: /$