diff --git a/.circleci/config.yml b/.circleci/config.yml index 1d4fc97..026bb87 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,17 +4,18 @@ version: 2.1 orbs: - ruby: circleci/ruby@1.1.1 + ruby: circleci/ruby@2.2.1 jobs: build: docker: - - image: circleci/ruby:2.7.3-node-browsers + - image: cimg/ruby:3.3.5-browsers environment: - RACK_ENV=test - RAILS_ENV=test - DATABASE_URL=postgres://testuser:testpass@127.0.0.1:5432/mahbucket_test + - SKIP_AUTH=true - - image: circleci/postgres:10-alpine-ram + - image: cimg/postgres:10.20 environment: - POSTGRES_USER=testuser - POSTGRES_PASSWORD=testpass @@ -37,3 +38,5 @@ jobs: - ruby/rubocop-check - ruby/rspec-test + - store_artifacts: + path: ./log/ diff --git a/.github/workflows/dependabot-auto-approve-and-merge.yml b/.github/workflows/dependabot-auto-approve-and-merge.yml new file mode 100644 index 0000000..7840e9a --- /dev/null +++ b/.github/workflows/dependabot-auto-approve-and-merge.yml @@ -0,0 +1,11 @@ +name: Dependabot auto-approve and auto-merge +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot-workflow: + uses: 38degrees/github-workflows/.github/workflows/dependabot-auto-approve-and-merge.yml@main + \ No newline at end of file diff --git a/.rubocop.yml b/.rubocop.yml index 76cebc3..a65ec45 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,3 +1,5 @@ +inherit_from: .rubocop_todo.yml + require: - rubocop-performance - rubocop-rails @@ -33,7 +35,7 @@ Metrics/BlockLength: # This set of tests use IP addresses for good reasons Style/IpAddresses: Exclude: - - spec/features/ip_restrictions_spec.rb + - spec/requests/ip_restrictions_spec.rb # This might be worth enabling at some point? Bundler/GemVersion: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml new file mode 100644 index 0000000..d0aba71 --- /dev/null +++ b/.rubocop_todo.yml @@ -0,0 +1,30 @@ +# This configuration was generated by +# `rubocop --auto-gen-config` +# on 2023-03-06 15:31:07 UTC using RuboCop version 1.47.0. +# The point is for the user to remove these configuration records +# one by one as the offenses are removed from the code base. +# Note that changes in the inspected code, or installation of new +# versions of RuboCop, may require this file to be generated again. + +# Offense count: 6 +Capybara/SpecificMatcher: + Exclude: + - 'spec/features/user_views_items_spec.rb' + - 'spec/requests/ip_restrictions_spec.rb' + +# Offense count: 1 +# Configuration parameters: EnforcedStyle. +# SupportedStyles: slashes, arguments +Rails/FilePath: + Exclude: + - 'spec/rails_helper.rb' + +# Offense count: 3 +Rails/I18nLocaleTexts: + Exclude: + - 'app/controllers/items_controller.rb' + +# Offense count: 1 +Style/TopLevelMethodDefinition: + Exclude: + - 'spec/support/capybara.rb' diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 2c9b4ef..0000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.7.3 diff --git a/Gemfile b/Gemfile index f7c06d8..34be5c3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,38 +1,44 @@ +ruby "3.3.5" + source 'https://rubygems.org' do - gem 'rails', '~> 6.0.3' + gem 'rails', '~> 6.1.7' # Use postgres as the database for Active Record gem 'pg' # Use Puma as the app server gem 'puma', '~> 5.6' # Use SCSS for stylesheets - gem 'sass-rails', '~> 6.0' + gem 'sass-rails' # Use Uglifier as compressor for JavaScript assets - gem 'uglifier', '>= 1.3.0' + gem 'uglifier' # Use CoffeeScript for .coffee assets and views - gem 'coffee-rails', '~> 5.0' + gem 'coffee-rails' # Use jquery as the JavaScript library gem 'jquery-rails' # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks - gem 'turbolinks', '~> 5' + gem 'turbolinks' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder - gem 'jbuilder', '~> 2.10' + gem 'jbuilder' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 3.0' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' # Authenticate via Google OAuth - gem 'omniauth', '~> 1.9.1' + gem 'omniauth', '~> 2.0' gem 'omniauth-google-oauth2' + gem 'omniauth-rails_csrf_protection' gem 'repost' # Store files on Amazon S3 gem 'aws-sdk-s3' - gem 'paperclip', '~> 6.1.0' - gem 'paperclip-meta' - + # Paperclip is no longer maintained and isn't compatible with Ruby 3. + # We switched to kt-paperclip. Sadly we use paperclip-meta for width and height + # which is tied to the original paperclip. The author refused the PR that fixes this + # so we're now using a version that someone else has fixed! + gem "kt-paperclip", '>= 7.0.1' + gem 'paperclip-meta', git: 'https://github.com/GoodMeasuresLLC/paperclip-meta' # Add tag features gem 'acts-as-taggable-on' @@ -42,6 +48,10 @@ source 'https://rubygems.org' do # New Relic monitoring gem 'newrelic_rpm' + # Error reporting + gem 'sentry-rails' + gem 'sentry-ruby' + # https://github.com/advisories/GHSA-vr8q-g5c7-m54m gem "nokogiri", ">= 1.11.0.rc4" @@ -51,11 +61,11 @@ source 'https://rubygems.org' do gem 'bundler-audit', require: false gem 'capybara' gem 'pry-rails' - gem 'rspec-rails', '~> 4.0' + gem 'rspec-rails' end group :development do - gem 'listen', '~> 3.2.1' + gem 'listen' gem 'rails-erd' gem 'rubocop', require: false gem 'rubocop-performance', require: false @@ -70,6 +80,5 @@ source 'https://rubygems.org' do gem 'rspec' gem 'rspec_junit_formatter' gem 'selenium-webdriver' - gem 'webdrivers', '~> 4.4' end end diff --git a/Gemfile.lock b/Gemfile.lock index 36f31dc..41a8d0a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,102 +1,114 @@ +GIT + remote: https://github.com/GoodMeasuresLLC/paperclip-meta + revision: 71ca62d8479a9a4787d902413906a38e42c9230d + specs: + paperclip-meta (3.1.0) + kt-paperclip (>= 7.0) + GEM specs: GEM remote: https://rubygems.org/ specs: - actioncable (6.0.3.7) - actionpack (= 6.0.3.7) + actioncable (6.1.7.4) + actionpack (= 6.1.7.4) + activesupport (= 6.1.7.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.3.7) - actionpack (= 6.0.3.7) - activejob (= 6.0.3.7) - activerecord (= 6.0.3.7) - activestorage (= 6.0.3.7) - activesupport (= 6.0.3.7) + actionmailbox (6.1.7.4) + actionpack (= 6.1.7.4) + activejob (= 6.1.7.4) + activerecord (= 6.1.7.4) + activestorage (= 6.1.7.4) + activesupport (= 6.1.7.4) mail (>= 2.7.1) - actionmailer (6.0.3.7) - actionpack (= 6.0.3.7) - actionview (= 6.0.3.7) - activejob (= 6.0.3.7) + actionmailer (6.1.7.4) + actionpack (= 6.1.7.4) + actionview (= 6.1.7.4) + activejob (= 6.1.7.4) + activesupport (= 6.1.7.4) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.0.3.7) - actionview (= 6.0.3.7) - activesupport (= 6.0.3.7) - rack (~> 2.0, >= 2.0.8) + actionpack (6.1.7.4) + actionview (= 6.1.7.4) + activesupport (= 6.1.7.4) + rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.3.7) - actionpack (= 6.0.3.7) - activerecord (= 6.0.3.7) - activestorage (= 6.0.3.7) - activesupport (= 6.0.3.7) + actiontext (6.1.7.4) + actionpack (= 6.1.7.4) + activerecord (= 6.1.7.4) + activestorage (= 6.1.7.4) + activesupport (= 6.1.7.4) nokogiri (>= 1.8.5) - actionview (6.0.3.7) - activesupport (= 6.0.3.7) + actionview (6.1.7.4) + activesupport (= 6.1.7.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.0.3.7) - activesupport (= 6.0.3.7) + activejob (6.1.7.4) + activesupport (= 6.1.7.4) globalid (>= 0.3.6) - activemodel (6.0.3.7) - activesupport (= 6.0.3.7) - activerecord (6.0.3.7) - activemodel (= 6.0.3.7) - activesupport (= 6.0.3.7) - activestorage (6.0.3.7) - actionpack (= 6.0.3.7) - activejob (= 6.0.3.7) - activerecord (= 6.0.3.7) - marcel (~> 1.0.0) - activesupport (6.0.3.7) + activemodel (6.1.7.4) + activesupport (= 6.1.7.4) + activerecord (6.1.7.4) + activemodel (= 6.1.7.4) + activesupport (= 6.1.7.4) + activestorage (6.1.7.4) + actionpack (= 6.1.7.4) + activejob (= 6.1.7.4) + activerecord (= 6.1.7.4) + activesupport (= 6.1.7.4) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (6.1.7.4) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.2, >= 2.2.2) - acts-as-taggable-on (7.0.0) - activerecord (>= 5.0, < 6.2) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + acts-as-taggable-on (9.0.1) + activerecord (>= 6.0, < 7.1) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) ast (2.4.2) - aws-eventstream (1.1.1) - aws-partitions (1.452.0) - aws-sdk-core (3.114.0) + aws-eventstream (1.2.0) + aws-partitions (1.720.0) + aws-sdk-core (3.170.0) aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.43.0) - aws-sdk-core (~> 3, >= 3.112.0) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.63.0) + aws-sdk-core (~> 3, >= 3.165.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.94.1) - aws-sdk-core (~> 3, >= 3.112.0) + aws-sdk-s3 (1.119.1) + aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.2.3) + aws-sigv4 (~> 1.4) + aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) - brakeman (5.0.1) + base64 (0.2.0) + brakeman (5.4.1) builder (3.2.4) - bundler-audit (0.8.0) + bundler-audit (0.9.1) bundler (>= 1.2.0, < 3) thor (~> 1.0) - capybara (3.35.3) + capybara (3.40.0) addressable + matrix mini_mime (>= 0.1.3) - nokogiri (~> 1.8) + nokogiri (~> 1.11) rack (>= 1.6.0) rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - childprocess (3.0.0) choice (0.2.0) climate_control (0.2.0) - codecov (0.5.2) + codecov (0.6.0) simplecov (>= 0.15, < 0.22) coderay (1.1.3) coffee-rails (5.0.0) @@ -106,7 +118,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.8) + concurrent-ruby (1.2.2) crass (1.0.6) database_cleaner (2.0.1) database_cleaner-active_record (~> 2.0.0) @@ -114,203 +126,220 @@ GEM activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) - diff-lcs (1.4.4) - docile (1.3.5) - erubi (1.10.0) - execjs (2.7.0) - faraday (1.4.1) - faraday-excon (~> 1.1) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.1) - multipart-post (>= 1.2, < 3) + date (3.3.3) + diff-lcs (1.5.0) + docile (1.4.0) + erubi (1.12.0) + execjs (2.8.1) + faraday (2.7.4) + faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) - faraday-excon (1.1.0) - faraday-net_http (1.0.1) - faraday-net_http_persistent (1.1.0) - ffi (1.15.0) - globalid (0.4.2) - activesupport (>= 4.2.0) - hashie (4.1.0) - i18n (1.8.10) + faraday-net_http (3.0.2) + ffi (1.15.5) + globalid (1.1.0) + activesupport (>= 5.0) + hashie (5.0.0) + i18n (1.14.1) concurrent-ruby (~> 1.0) - jbuilder (2.11.2) + jbuilder (2.11.5) + actionview (>= 5.0.0) activesupport (>= 5.0.0) - jmespath (1.4.0) - jquery-rails (4.4.0) + jmespath (1.6.2) + jquery-rails (4.5.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) - jwt (2.2.3) - kaminari (1.2.1) + json (2.6.3) + jwt (2.7.0) + kaminari (1.2.2) activesupport (>= 4.1.0) - kaminari-actionview (= 1.2.1) - kaminari-activerecord (= 1.2.1) - kaminari-core (= 1.2.1) - kaminari-actionview (1.2.1) + kaminari-actionview (= 1.2.2) + kaminari-activerecord (= 1.2.2) + kaminari-core (= 1.2.2) + kaminari-actionview (1.2.2) actionview - kaminari-core (= 1.2.1) - kaminari-activerecord (1.2.1) + kaminari-core (= 1.2.2) + kaminari-activerecord (1.2.2) activerecord - kaminari-core (= 1.2.1) - kaminari-core (1.2.1) - listen (3.2.1) + kaminari-core (= 1.2.2) + kaminari-core (1.2.2) + kt-paperclip (7.1.1) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + marcel (~> 1.0.1) + mime-types + terrapin (~> 0.6.0) + listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.9.1) + logger (1.6.1) + loofah (2.21.3) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) - marcel (1.0.1) + net-imap + net-pop + net-smtp + marcel (1.0.2) + matrix (0.4.2) method_source (1.0.0) - mime-types (3.3.1) + mime-types (3.4.1) mime-types-data (~> 3.2015) - mime-types-data (3.2021.0225) - mimemagic (0.3.10) - nokogiri (~> 1) - rake - mini_mime (1.1.0) - mini_portile2 (2.8.0) - minitest (5.14.4) - multi_json (1.15.0) + mime-types-data (3.2023.0218.1) + mini_mime (1.1.5) + mini_portile2 (2.8.8) + minitest (5.18.1) multi_xml (0.6.0) - multipart-post (2.1.1) - newrelic_rpm (7.0.0) - nio4r (2.5.8) - nokogiri (1.13.4) - mini_portile2 (~> 2.8.0) + net-imap (0.3.6) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.1) + timeout + net-smtp (0.3.3) + net-protocol + newrelic_rpm (9.0.0) + nio4r (2.7.3) + nokogiri (1.16.7) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.13.4-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) - oauth2 (1.4.7) - faraday (>= 0.8, < 2.0) + oauth2 (2.0.9) + faraday (>= 0.17.3, < 3.0) jwt (>= 1.0, < 3.0) - multi_json (~> 1.3) multi_xml (~> 0.5) - rack (>= 1.2, < 3) - omniauth (1.9.1) + rack (>= 1.2, < 4) + snaky_hash (~> 2.0) + version_gem (~> 1.1) + omniauth (2.1.1) hashie (>= 3.4.6) - rack (>= 1.6.2, < 3) - omniauth-google-oauth2 (0.8.2) + rack (>= 2.2.3) + rack-protection + omniauth-google-oauth2 (1.1.1) jwt (>= 2.0) - oauth2 (~> 1.1) - omniauth (~> 1.1) - omniauth-oauth2 (>= 1.6) - omniauth-oauth2 (1.7.1) - oauth2 (~> 1.4) - omniauth (>= 1.9, < 3) - paperclip (6.1.0) - activemodel (>= 4.2.0) - activesupport (>= 4.2.0) - mime-types - mimemagic (~> 0.3.0) - terrapin (~> 0.6.0) - paperclip-meta (3.1.0) - paperclip (>= 5.0) - parallel (1.20.1) - parser (3.0.1.1) + oauth2 (~> 2.0.6) + omniauth (~> 2.0) + omniauth-oauth2 (~> 1.8.0) + omniauth-oauth2 (1.8.0) + oauth2 (>= 1.4, < 3) + omniauth (~> 2.0) + omniauth-rails_csrf_protection (1.0.1) + actionpack (>= 4.2) + omniauth (~> 2.0) + parallel (1.22.1) + parser (3.2.1.0) ast (~> 2.4.1) - pg (1.2.3) - pry (0.14.1) + pg (1.4.6) + pry (0.14.2) coderay (~> 1.1) method_source (~> 1.0) pry-rails (0.3.9) pry (>= 0.10.4) - public_suffix (4.0.6) - puma (5.6.4) + public_suffix (6.0.1) + puma (5.6.9) nio4r (~> 2.0) - racc (1.6.0) - rack (2.2.3) - rack-test (1.1.0) - rack (>= 1.0, < 3) - rails (6.0.3.7) - actioncable (= 6.0.3.7) - actionmailbox (= 6.0.3.7) - actionmailer (= 6.0.3.7) - actionpack (= 6.0.3.7) - actiontext (= 6.0.3.7) - actionview (= 6.0.3.7) - activejob (= 6.0.3.7) - activemodel (= 6.0.3.7) - activerecord (= 6.0.3.7) - activestorage (= 6.0.3.7) - activesupport (= 6.0.3.7) - bundler (>= 1.3.0) - railties (= 6.0.3.7) + racc (1.8.1) + rack (2.2.10) + rack-protection (3.0.5) + rack + rack-test (2.1.0) + rack (>= 1.3) + rails (6.1.7.4) + actioncable (= 6.1.7.4) + actionmailbox (= 6.1.7.4) + actionmailer (= 6.1.7.4) + actionpack (= 6.1.7.4) + actiontext (= 6.1.7.4) + actionview (= 6.1.7.4) + activejob (= 6.1.7.4) + activemodel (= 6.1.7.4) + activerecord (= 6.1.7.4) + activestorage (= 6.1.7.4) + activesupport (= 6.1.7.4) + bundler (>= 1.15.0) + railties (= 6.1.7.4) sprockets-rails (>= 2.0.0) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + rails-dom-testing (2.1.1) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-erd (1.6.1) + rails-erd (1.7.2) activerecord (>= 4.2) activesupport (>= 4.2) choice (~> 0.2.0) ruby-graphviz (~> 1.2) - rails-html-sanitizer (1.3.0) - loofah (~> 2.3) - railties (6.0.3.7) - actionpack (= 6.0.3.7) - activesupport (= 6.0.3.7) + rails-html-sanitizer (1.6.0) + loofah (~> 2.21) + nokogiri (~> 1.14) + railties (6.1.7.4) + actionpack (= 6.1.7.4) + activesupport (= 6.1.7.4) method_source - rake (>= 0.8.7) - thor (>= 0.20.3, < 2.0) - rainbow (3.0.0) - rake (13.0.3) - rb-fsevent (0.11.0) + rake (>= 12.2) + thor (~> 1.0) + rainbow (3.1.1) + rake (13.0.6) + rb-fsevent (0.11.2) rb-inotify (0.10.1) ffi (~> 1.0) - regexp_parser (2.1.1) - repost (0.3.6) - rexml (3.2.5) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + regexp_parser (2.9.3) + repost (0.4.1) + rexml (3.3.9) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.1) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-mocks (3.10.2) + rspec-support (~> 3.12.0) + rspec-mocks (3.12.3) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-rails (4.1.2) - actionpack (>= 4.2) - activesupport (>= 4.2) - railties (>= 4.2) - rspec-core (~> 3.10) - rspec-expectations (~> 3.10) - rspec-mocks (~> 3.10) - rspec-support (~> 3.10) - rspec-support (3.10.2) - rspec_junit_formatter (0.4.1) + rspec-support (~> 3.12.0) + rspec-rails (6.0.1) + actionpack (>= 6.1) + activesupport (>= 6.1) + railties (>= 6.1) + rspec-core (~> 3.11) + rspec-expectations (~> 3.11) + rspec-mocks (~> 3.11) + rspec-support (~> 3.11) + rspec-support (3.12.0) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) - rubocop (1.14.0) + rubocop (1.47.0) + json (~> 2.3) parallel (~> 1.10) - parser (>= 3.0.0.0) + parser (>= 3.2.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.5.0, < 2.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.26.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.5.0) - parser (>= 3.0.1.1) - rubocop-performance (1.11.3) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.27.0) + parser (>= 3.2.1.0) + rubocop-capybara (2.17.1) + rubocop (~> 1.41) + rubocop-performance (1.16.0) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) - rubocop-rails (2.10.1) + rubocop-rails (2.18.0) activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 1.7.0, < 2.0) - rubocop-rspec (2.3.0) - rubocop (~> 1.0) - rubocop-ast (>= 1.1.0) + rubocop (>= 1.33.0, < 2.0) + rubocop-rspec (2.18.1) + rubocop (~> 1.33) + rubocop-capybara (~> 2.17) ruby-graphviz (1.2.5) rexml - ruby-progressbar (1.11.0) - ruby2_keywords (0.0.4) - rubyzip (2.3.0) + ruby-progressbar (1.12.0) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) sass-rails (6.0.0) sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) @@ -321,45 +350,54 @@ GEM sprockets (> 3.0) sprockets-rails tilt - selenium-webdriver (3.142.7) - childprocess (>= 0.5, < 4.0) - rubyzip (>= 1.2.2) + selenium-webdriver (4.27.0) + base64 (~> 0.2) + logger (~> 1.4) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + sentry-rails (5.8.0) + railties (>= 5.0) + sentry-ruby (~> 5.8.0) + sentry-ruby (5.8.0) + concurrent-ruby (~> 1.0, >= 1.0.2) simplecov (0.21.2) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) - simplecov_json_formatter (0.1.3) - sprockets (4.0.2) + simplecov_json_formatter (0.1.4) + snaky_hash (2.0.1) + hashie + version_gem (~> 1.1, >= 1.1.1) + sprockets (4.2.0) concurrent-ruby (~> 1.0) - rack (> 1, < 3) - sprockets-rails (3.2.2) - actionpack (>= 4.0) - activesupport (>= 4.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) sprockets (>= 3.0.0) terrapin (0.6.0) climate_control (>= 0.0.3, < 1.0) - thor (1.1.0) - thread_safe (0.3.6) - tilt (2.0.10) + thor (1.2.2) + tilt (2.1.0) + timeout (0.4.0) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - tzinfo (1.2.9) - thread_safe (~> 0.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) uglifier (4.2.0) execjs (>= 0.3.0, < 3) - unicode-display_width (2.0.0) - webdrivers (4.6.0) - nokogiri (~> 1.6) - rubyzip (>= 1.3.0) - selenium-webdriver (>= 3.0, < 4.0) - websocket-driver (0.7.3) + unicode-display_width (2.4.2) + version_gem (1.1.1) + websocket (1.2.11) + websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.4.2) + zeitwerk (2.6.8) PLATFORMS ruby @@ -372,36 +410,41 @@ DEPENDENCIES bundler-audit! capybara! codecov! - coffee-rails (~> 5.0)! + coffee-rails! database_cleaner! - jbuilder (~> 2.10)! + jbuilder! jquery-rails! kaminari! - listen (~> 3.2.1)! + kt-paperclip (>= 7.0.1)! + listen! newrelic_rpm! nokogiri (>= 1.11.0.rc4)! - omniauth (~> 1.9.1)! + omniauth (~> 2.0)! omniauth-google-oauth2! - paperclip (~> 6.1.0)! + omniauth-rails_csrf_protection! paperclip-meta! pg! pry-rails! puma (~> 5.6)! - rails (~> 6.0.3)! + rails (~> 6.1.7)! rails-erd! repost! rspec! - rspec-rails (~> 4.0)! + rspec-rails! rspec_junit_formatter! rubocop! rubocop-performance! rubocop-rails! rubocop-rspec! - sass-rails (~> 6.0)! + sass-rails! selenium-webdriver! - turbolinks (~> 5)! - uglifier (>= 1.3.0)! - webdrivers (~> 4.4)! + sentry-rails! + sentry-ruby! + turbolinks! + uglifier! + +RUBY VERSION + ruby 3.3.5p100 BUNDLED WITH - 2.2.17 + 2.5.23 diff --git a/README.md b/README.md index 9007e59..88be9c6 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,20 @@ should hopefully be useful (or at least usable) for other file types too. ## Ruby and Rails versions Mah Bucket was initially developed using Ruby 2.3.1 and Rails 5.0.1, and is -currently using Ruby 2.7.1 and Rails 6.0.3 - so it should have a fairly wide +currently using Ruby 3.3.5 and Rails ~>6.1.7 - so it should have a fairly wide compatibility range. If you find a version of either that it won't run with, please let me know. +## Running locally +If you are docker enabled then there is a very simple docker compose +setup that when run will run rubocop, rspecs and if successful launch +the app. + +Run with +``` +docker compose up +``` + ## System dependencies diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f642256..489f0c3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,7 +6,11 @@ class ApplicationController < ActionController::Base before_action :authenticate def self.permitted_ips_from_env - ENV['PERMITTED_IPS'] + ENV.fetch('PERMITTED_IPS', nil) + end + + def self.client_ip_header_from_env + ENV.fetch('CLIENT_IP_HEADER', nil) end private @@ -15,8 +19,12 @@ def check_permitted_ips return if Rails.env.development? return if self.class.permitted_ips_from_env.blank? + ip_to_verify = self.class.client_ip_header_from_env ? request.headers[self.class.client_ip_header_from_env] : request.ip + ip_addresses = list_of_permitted_ips( self.class.permitted_ips_from_env ) - return if ip_addresses.include? request.ip + + Rails.logger.debug { "check_permitted_ips: checking ip: #{ip_to_verify} against #{ip_addresses}" } + return if ip_addresses.include? ip_to_verify render plain: 'Access Denied', status: :unauthorized end @@ -45,9 +53,13 @@ def handle_ip_list_with_comments( permitted_ips ) end def authenticate + if Rails.env.test? && ENV['SKIP_AUTH'] + session[:email] = 'admin@example.com' + return + end return if session[:email] - return if /google_oauth2/.match?(request.path) + return if request.path.include?('google_oauth2') - redirect_post '/auth/google_oauth2' + redirect_post('/auth/google_oauth2', options: {authenticity_token: :auto}) end end diff --git a/app/controllers/items_controller.rb b/app/controllers/items_controller.rb index 130c0d3..dad7db9 100644 --- a/app/controllers/items_controller.rb +++ b/app/controllers/items_controller.rb @@ -17,6 +17,9 @@ def new @item = Item.new end + # GET /items/1/edit + def edit; end + # POST /items # POST /items.json def create @@ -33,9 +36,6 @@ def create end end - # GET /items/1/edit - def edit; end - # PATCH/PUT /items/1 # PATCH/PUT /items/1.json def update @@ -67,7 +67,7 @@ def set_item @item = Item.find(params[:id]) end - # Never trust parameters from the Internet, only allow the whitelist through + # Never trust parameters from the Internet, only allow the safelist through def item_params params.require( :item ).permit( :file, :tag_list ) end diff --git a/config/environments/production.rb b/config/environments/production.rb index 01dfc64..ac63754 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -74,7 +74,7 @@ config.active_support.deprecation = :notify # Use default logging formatter so that PID and timestamp are not suppressed. - config.log_formatter = ::Logger::Formatter.new + config.log_formatter = Logger::Formatter.new # Use a different logger for distributed setups. # require 'syslog/logger' diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 69ccd81..707701b 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -1,3 +1,3 @@ Rails.application.config.middleware.use OmniAuth::Builder do - provider :google_oauth2, ENV['GOOGLE_OAUTH_CLIENT_ID'], ENV['GOOGLE_OAUTH_CLIENT_SECRET'] + provider :google_oauth2, ENV.fetch('GOOGLE_OAUTH_CLIENT_ID', nil), ENV.fetch('GOOGLE_OAUTH_CLIENT_SECRET', nil) end diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb new file mode 100644 index 0000000..5510990 --- /dev/null +++ b/config/initializers/sentry.rb @@ -0,0 +1,6 @@ +return unless ENV['SENTRY_DSN'] + +Sentry.init do |config| + config.dsn = ENV.fetch('SENTRY_DSN', nil) + config.breadcrumbs_logger = [:active_support_logger] +end diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 0000000..d52c199 --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,21 @@ +default: &default + adapter: postgresql + pool: 5 + timeout: 5000 + encoding: utf8 + +development: + <<: *default + database: mahbucket_dev + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: mahbucket_test + +# This should be overridden by DATABASE_URL in your ENV +production: + <<: *default + database: mahbucket diff --git a/db/migrate/20170112140910_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb b/db/migrate/20170112140910_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb index 6bbd559..ad52220 100644 --- a/db/migrate/20170112140910_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb +++ b/db/migrate/20170112140910_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb @@ -1,5 +1,5 @@ # This migration comes from acts_as_taggable_on_engine (originally 1) -class ActsAsTaggableOnMigration < ActiveRecord::Migration +class ActsAsTaggableOnMigration < ActiveRecord::Migration[5.0] def self.up create_table :tags do |t| t.string :name diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..a71d3dc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,58 @@ +version: '3' + +x-service-fragments: + ##### Abstract services for common setup ##### + app: &app + image: cimg/ruby:3.3.5-browsers + networks: + - default + volumes: + # Use cached bind mount for mac performance + - '.:/app:cached' + # use separate volume for gems and node packages to make development nicer + - bundle_cache:/bundle + - node_cache:/app/node_modules + - webdriver_cache:/root/.webdrivers + # use separate volume for bootsnap cache for performance... + - tmp_cache:/app/tmp/cache + +services: + ##### Concrete services ##### + postgres: + image: 'cimg/postgres:10.20' + environment: + # The Postgres image uses these to create a db user - note the user/pass here should match that in your env file! + - POSTGRES_USER=user + - POSTGRES_PASSWORD=password + - POSTGRES_DB=mahbucket_test + volumes: + - 'postgres:/var/lib/postgresql/data' + + web: + <<: *app + volumes: + - '.:/usr/src/app:cached' + working_dir: /usr/src/app + ports: + # Use a default port of 3005, but allow it to be overriden by an env variable + # E.g. `PORT=3001 docker-compose up` + - '${PORT:-3005}:3000' + stdin_open: true + tty: true + depends_on: + - postgres + environment: + - DATABASE_URL=postgres://user:password@postgres/mahbucket + - RACK_ENV=test + - RAILS_ENV=test + - DATABASE_CLEANER_ALLOW_REMOTE_DATABASE_URL=true + - SKIP_AUTH=true + command: sh -c 'bundle check || bundle install && rm -f /tmp/.X99-lock && bundle exec rails db:create db:environment:set db:schema:load && bundle exec rubocop && bundle exec rspec && bundle exec puma -C ./config/puma.rb' + +volumes: + postgres: + bundle_cache: + node_cache: + tmp_cache: + webdriver_cache: + diff --git a/public/403.html b/public/403.html new file mode 100644 index 0000000..8d8f069 --- /dev/null +++ b/public/403.html @@ -0,0 +1,67 @@ + + + + Forbidden (403) + + + + + + +
+
+

Authentication Error

+

oauth may not be correctly configured

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/spec/features/google_oauth_spec.rb b/spec/features/google_oauth_spec.rb index 86de70b..cf0052f 100644 --- a/spec/features/google_oauth_spec.rb +++ b/spec/features/google_oauth_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe "User authentication", type: :feature, js: false do +RSpec.describe "User authentication", js: false do it 'succeeds' do visit '/' expect( page ).to have_http_status :ok diff --git a/spec/features/ip_restrictions_spec.rb b/spec/features/ip_restrictions_spec.rb deleted file mode 100644 index ac55680..0000000 --- a/spec/features/ip_restrictions_spec.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'rails_helper' - -RSpec.describe "User attempts to view items", type: :feature, js: true do - it 'allows anyone if there are no permitted-IPs list' do - allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( nil ) - - visit '/' - - expect( page ).to have_css 'a[href="/search"]' - end - - it 'allows a matching ip with basic permitted-IPs list' do - allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '127.0.0.1' ) - - visit '/' - - expect( page ).to have_css 'a[href="/search"]' - end - - it 'allows a matching ip with bracketed notes' do - allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '127.0.0.1 (localhost)' ) - - visit '/' - - expect( page ).to have_css 'a[href="/search"]' - end - - it 'allows a matching ip with multi-line permitted-IPs list in place' do - allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return(<<~PERMITTED_IPS) - 127.0.0.1 # localhost - 19.168.0.1 # local netblock - PERMITTED_IPS - - visit '/' - - expect( page ).to have_css 'a[href="/search"]' - end - - it 'blocks non-matching permitted-IPs' do - allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '1.3.3.7' ) - - visit '/' - - expect( page ).to have_text 'Access Denied' - end -end diff --git a/spec/features/user_views_items_spec.rb b/spec/features/user_views_items_spec.rb index 0471b96..ecfaebc 100644 --- a/spec/features/user_views_items_spec.rb +++ b/spec/features/user_views_items_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe "User views items", type: :feature, js: true do +RSpec.describe "User views items", js: true do it 'successfully' do visit '/' diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index db99d8b..f55ad8e 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -4,8 +4,8 @@ # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? -require 'spec_helper' require 'rspec/rails' +require 'spec_helper' # Add additional requires below this line. Rails is not loaded until this point! @@ -24,7 +24,7 @@ # directory. Alternatively, in the individual `*_spec.rb` files, manually # require only the support files necessary. # -Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f } +Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } # Checks for pending migration and applies them before tests are run. # If you are not using ActiveRecord, you can remove this line. @@ -32,7 +32,7 @@ RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures - config.fixture_path = "#{::Rails.root}/spec/fixtures" + config.fixture_path = "#{Rails.root}/spec/fixtures" # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false diff --git a/spec/requests/ip_restrictions_spec.rb b/spec/requests/ip_restrictions_spec.rb new file mode 100644 index 0000000..2aad5a0 --- /dev/null +++ b/spec/requests/ip_restrictions_spec.rb @@ -0,0 +1,62 @@ +require 'rails_helper' + +RSpec.describe "User attempts to view items" do + it 'allows anyone if there are no permitted-IPs list' do + allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( nil ) + + get '/' + expect( response.body ).to have_css 'a[href="/search"]' + end + + it 'allows a matching ip with basic permitted-IPs list' do + allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '127.0.0.1' ) + + get '/' + + expect( response.body ).to have_css 'a[href="/search"]' + end + + it 'allows a matching ip with bracketed notes' do + allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '127.0.0.1 (localhost)' ) + + get '/' + + expect( response.body ).to have_css 'a[href="/search"]' + end + + it 'allows a matching ip with multi-line permitted-IPs list in place' do + allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return(<<~PERMITTED_IPS) + 127.0.0.1 # localhost + 19.168.0.1 # local netblock + PERMITTED_IPS + + get '/' + + expect( response.body ).to have_css 'a[href="/search"]' + end + + it 'blocks non-matching permitted-IPs' do + allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '1.3.3.7' ) + + get '/' + + expect( response.body ).to have_text 'Access Denied' + end + + it 'Disallows a non matching ip in a custom header with basic permitted-IPs list' do + allow( ApplicationController ).to receive( :client_ip_header_from_env ).and_return('CF-Connecting-Ip') + allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '192.168.0.1' ) + get '/', headers: {'CF-Connecting-Ip' => '127.0.0.1' } + + expect( response.body ).to have_text 'Access Denied' + end + + it 'Allows a matching ip in a custom header with basic permitted-IPs list' do + allow( ApplicationController ).to receive( :client_ip_header_from_env ).and_return('CF-Connecting-Ip') + allow( ApplicationController ).to receive( :permitted_ips_from_env ).and_return( '127.0.0.1' ) + + get '/', headers: {'CF-Connecting-Ip' => '127.0.0.1' } + + expect( response.body ).to have_css 'a[href="/search"]' + end +end diff --git a/spec/requests/items_spec.rb b/spec/requests/items_spec.rb index 43fd4c5..4399e7d 100644 --- a/spec/requests/items_spec.rb +++ b/spec/requests/items_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.describe 'Create/Update/Delete items', type: :request do +RSpec.describe 'Create/Update/Delete items' do before do allow_any_instance_of( ItemsController ).to receive( :authenticate ).and_return(nil) end diff --git a/spec/support/omniauth.rb b/spec/support/omniauth.rb index bce71bc..22f220f 100644 --- a/spec/support/omniauth.rb +++ b/spec/support/omniauth.rb @@ -5,7 +5,7 @@ uid: '12345', info: { name: 'Alice Tester', - email: "alice@#{ENV['GOOGLE_OAUTH_DOMAIN']}" + email: "alice@#{ENV.fetch('GOOGLE_OAUTH_DOMAIN', nil)}" } } )