Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jobs:
PGHOST: 127.0.0.1
PGUSER: root

- image: cimg/redis:7.2.7
environment:
REDIS_URL: redis://redis:6379/1

# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
Expand Down
9 changes: 6 additions & 3 deletions .circleci/cron.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ echo "Logging into cloud.gov"
cf login -a $CF_API_ENDPOINT -u $CF_USERNAME -p $CF_PASSWORD -o $CF_ORG -s $CF_SPACE

#
# === Staging environment =========================================================
# === STAGING environment ======================================================
#

echo "Running tasks in Staging..."
Expand All @@ -24,11 +24,12 @@ cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:send_week
cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms"
cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:archive_forms"
cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms"
# cf run-task touchpoints-staging-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash"

echo "Staging tasks have completed."

#
# === Demo environment =========================================================
# === DEMO environment =========================================================
#

echo "Running tasks in Demo..."
Expand All @@ -44,13 +45,14 @@ cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:send_weekly_
cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms"
cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:archive_forms"
cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms"
# cf run-task touchpoints-demo-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash"

echo "Demo tasks have completed."

cf logout

#
# === Production environment =========================================================
# === PRODUCTION environment ===================================================
#

echo "Logging into cloud.gov"
Expand All @@ -69,6 +71,7 @@ cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:deacti
# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:check_expiring_forms"
# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:archive_forms"
cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:notify_form_managers_of_inactive_forms"
# cf run-task touchpoints-production-sidekiq-worker -c "rake scheduled_jobs:delete_submissions_trash"

echo "Production tasks have completed."

Expand Down
5 changes: 4 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,15 @@ group :development, :test do
gem 'pry'
end

group :development, :staging do
gem 'faker'
end

group :development do
gem 'aasm-diagram'
gem "brakeman"
gem 'bullet'
gem "bundler-audit"
gem 'faker', git: 'https://github.com/faker-ruby/faker.git', branch: 'main'
gem 'listen'
gem 'rails-erd'
gem "rubocop-rails"
Expand Down
12 changes: 3 additions & 9 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,6 @@ GIT
multi_json (~> 1.14)
omniauth (~> 2.0)

GIT
remote: https://github.com/faker-ruby/faker.git
revision: 3a65e1e2e567cb3be3f6b9582484ba4d5ee5d8c6
branch: main
specs:
faker (3.5.1)
i18n (>= 1.8.11, < 2)

GEM
remote: https://rubygems.org/
specs:
Expand Down Expand Up @@ -242,6 +234,8 @@ GEM
factory_bot_rails (6.4.4)
factory_bot (~> 6.5)
railties (>= 5.0.0)
faker (3.5.1)
i18n (>= 1.8.11, < 2)
faraday (2.12.2)
faraday-net_http (>= 2.0, < 3.5)
json
Expand Down Expand Up @@ -674,7 +668,7 @@ DEPENDENCIES
devise (>= 4.8.1)
dotenv
factory_bot_rails
faker!
faker
fog-aws (>= 3.15.0)
image_processing (~> 1.12)
importmap-rails (>= 2.0.0)
Expand Down
2 changes: 2 additions & 0 deletions app/models/event.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class Generic
organization_created: 'organization_created',
organization_updated: 'organization_updated',
organization_deleted: 'organization_deleted',

maintenance_submissions_deleted: 'maintenance_submissions_deleted',
}

def self.log_event(ename, otype, oid, desc, uid = nil)
Expand Down
3 changes: 2 additions & 1 deletion app/views/admin/forms/example.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@
<br>
<% else %>
<div class="grid-container demo-grid">
<header style="text-align: center; padding: 1rem;">
<header style="text-align: center;">
<div class="grid-row grid-gap-md">
<div class="grid-col-12 demo-content">
<br>
<span class="font-normal text-sans-sm">
Example page
</span>
Expand Down
9 changes: 5 additions & 4 deletions app/views/admin/forms/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@
<%- if @templates.present? %>
<ul class="usa-card-group">
<% @templates.each do |form| %>
<li class="usa-card desktop:grid-col-12">
<li
class="usa-card desktop:grid-col-12 margin-bottom-2">
<div class="usa-card__container">
<div class="usa-card__header">
<h2 class="usa-card__heading">
<h2 class="usa-card__heading font-sans-md">
<%= form.name %>
</h2>
</div>
<div class="usa-card__body">
<div class="usa-card__body font-sans-xs">
<%= to_markdown(form.notes) %>
</div>
<div class="usa-card__footer">
<div class="usa-card__footer padding-bottom-2 text-right">
<%= link_to example_admin_form_path(form), class: "usa-button usa-button--unstyled", target: "_blank", rel: "noopener" do %>
<i class="fa fa-eye" aria-hidden="true"></i>
Preview
Expand Down
2 changes: 1 addition & 1 deletion app/views/admin/submissions/flag.js.erb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
$(".response[data-id='<%= @submission.uuid %>'] .flagged").html("<%= j (render partial: 'flag', locals: { submission: @submission }) %>")
$(".response[data-id='<%= @submission.uuid %>'] .flagged").html("<%= j(render 'flag', submission: @submission) %>")
2 changes: 1 addition & 1 deletion app/views/admin/submissions/mark.js.erb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
$(".response[data-id='<%= @submission.uuid %>'] .marked").html("<%= j (render partial: 'flag', locals: { submission: @submission }) %>")
$(".response[data-id='<%= @submission.uuid %>'] .marked").html("<%= j(render 'mark', submission: @submission) %>")
2 changes: 1 addition & 1 deletion app/views/admin/submissions/unarchive.js.erb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
$(".response[data-id='<%= @submission.uuid %>'] .archived").html("<%= j (render 'unarchive', { submission: @submission }) %>")
$(".response[data-id='<%= @submission.uuid %>'] .archived").html("<%= j(render 'unarchive', submission: @submission) %>")
2 changes: 1 addition & 1 deletion app/views/admin/submissions/undelete.js.erb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
$(".response[data-id='<%= @submission.uuid %>'] .deleted").html("<%= j (render 'undeleted', { submission: @submission }) %>")
$(".response[data-id='<%= @submission.uuid %>'] .deleted").html("<%= j(render 'undeleted', submission: @submission) %>")
2 changes: 1 addition & 1 deletion app/views/admin/submissions/unflag.js.erb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
$(".response[data-id='<%= @submission.uuid %>'] .flagged").html("<%= j (render partial: 'unflag', :locals => { submission: @submission }) %>")
$(".response[data-id='<%= @submission.uuid %>'] .flagged").html("<%= j(render 'unflag', submission: @submission) %>")
2 changes: 1 addition & 1 deletion app/views/admin/submissions/unmark.js.erb
Original file line number Diff line number Diff line change
@@ -1 +1 @@
$(".response[data-id='<%= @submission.uuid %>'] .marked").html("<%= j (render partial: 'unmark', locals: { submission: @submission }) %>")
$(".response[data-id='<%= @submission.uuid %>'] .marked").html("<%= j(render 'unmark', submission: @submission) %>")
10 changes: 1 addition & 9 deletions config/initializers/rack_attack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,7 @@ class Rack::Attack

# Is the request to the form submission route?
def self.submission_route?(req)
begin
recognized_route = Rails.application.routes.recognize_path(req.path, method: req.request_method)
recognized_route[:controller] == 'submissions' &&
recognized_route[:action] == 'create' &&
recognized_route[:touchpoint_id].present? &&
recognized_route[:format] == 'json'
rescue ActionController::RoutingError
false
end
!!(req.path =~ %r{^/submissions/[\h]{1,8}\.json$}i)
end

# Response for throttled requests
Expand Down
10 changes: 10 additions & 0 deletions lib/tasks/scheduled_jobs.rake
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,14 @@ namespace :scheduled_jobs do
Form.archive_expired!
puts "Archiving forms based on expiration date"
end

task delete_submissions_trash: :environment do
puts "Deleting Trashed Submissions..."
@submissions = Submission
.deleted
.where("deleted_at < ?", Time.now - 30.days)
Event.log_event(Event.names[:maintenance_submissions_deleted], "Scheduled Job", "0", "Scheduled Job: Deleted #{@submissions.size} Trashed Submissions", 0)
@submissions.delete_all
puts "Deleted #{@submissions.size} Trashed Submissions"
end
end
7 changes: 5 additions & 2 deletions spec/helpers/application_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
require 'rails_helper'
include ActiveSupport::Testing::TimeHelpers

RSpec.describe ApplicationHelper, type: :helper do
describe "#format_submission_time" do
let(:time_zone) { "Eastern Time (US & Canada)" }

it "returns time for today's submissions" do
datetime = Time.zone.now
expect(helper.format_submission_time(datetime, time_zone)).to eq(datetime.in_time_zone(time_zone).strftime("%-I:%M %p"))
travel_to Time.zone.local(2025, 2, 17, 14, 30) do
datetime = Time.zone.now
expect(helper.format_submission_time(datetime, time_zone)).to eq(datetime.in_time_zone(time_zone).strftime("%-I:%M %p"))
end
end

it "returns month and day for submissions from this year (but not today)" do
Expand Down
55 changes: 55 additions & 0 deletions spec/requests/rack_attack_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
require 'rails_helper'

RSpec.describe 'Rack::Attack throttling', type: :request do
before do
Rack::Attack.reset! # Reset throttle counters before each test
end

let(:ip) { '1.2.3.4' }
let(:headers) { { 'REMOTE_ADDR' => ip } }
let(:valid_submission_path) { "/submissions/1234abcd.json" }

it 'allows up to 10 requests per minute' do
10.times do
post valid_submission_path, headers: headers
expect(response).not_to have_http_status(:too_many_requests)
end
end

it 'blocks the 11th request within a minute' do
10.times { post valid_submission_path, headers: headers }

post valid_submission_path, headers: headers
expect(response).to have_http_status(:too_many_requests)
end

it 'does not throttle requests from different IPs' do
10.times do |i|
post valid_submission_path, headers: { 'REMOTE_ADDR' => "192.168.1.#{i}" }
expect(response).not_to have_http_status(:too_many_requests)
end
end

it 'does not throttle non-matching routes' do
20.times do
post "/other_path", headers: headers
expect(response).not_to have_http_status(:too_many_requests)
end
end

it 'recognizes both numeric and short UUID paths' do
valid_paths = ["/submissions/123.json", "/submissions/abc123de.json"]
valid_paths.each do |path|
post path, headers: headers
expect(response).not_to have_http_status(:too_many_requests)
end
end

it 'does not throttle invalid submission paths' do
invalid_paths = ["/submissions/too_long_uuid_1234.json", "/submissions/.json"]
invalid_paths.each do |path|
post path, headers: headers
expect(response).not_to have_http_status(:too_many_requests)
end
end
end