Skip to content

Deny access to methods other than GET for readonly access tokens #390

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby "3.2.2"

# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
gem "rails", "~> 7.0.8"
gem "rails", "~> 7.1.2"

gem "config"

Expand Down
164 changes: 93 additions & 71 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,74 +9,85 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.8)
actionpack (= 7.0.8)
activesupport (= 7.0.8)
actioncable (7.1.2)
actionpack (= 7.1.2)
activesupport (= 7.1.2)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.8)
actionpack (= 7.0.8)
activejob (= 7.0.8)
activerecord (= 7.0.8)
activestorage (= 7.0.8)
activesupport (= 7.0.8)
zeitwerk (~> 2.6)
actionmailbox (7.1.2)
actionpack (= 7.1.2)
activejob (= 7.1.2)
activerecord (= 7.1.2)
activestorage (= 7.1.2)
activesupport (= 7.1.2)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.8)
actionpack (= 7.0.8)
actionview (= 7.0.8)
activejob (= 7.0.8)
activesupport (= 7.0.8)
actionmailer (7.1.2)
actionpack (= 7.1.2)
actionview (= 7.1.2)
activejob (= 7.1.2)
activesupport (= 7.1.2)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.8)
actionview (= 7.0.8)
activesupport (= 7.0.8)
rack (~> 2.0, >= 2.2.4)
rails-dom-testing (~> 2.2)
actionpack (7.1.2)
actionview (= 7.1.2)
activesupport (= 7.1.2)
nokogiri (>= 1.8.5)
racc
rack (>= 2.2.4)
rack-session (>= 1.0.1)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.8)
actionpack (= 7.0.8)
activerecord (= 7.0.8)
activestorage (= 7.0.8)
activesupport (= 7.0.8)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
actiontext (7.1.2)
actionpack (= 7.1.2)
activerecord (= 7.1.2)
activestorage (= 7.1.2)
activesupport (= 7.1.2)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.8)
activesupport (= 7.0.8)
actionview (7.1.2)
activesupport (= 7.1.2)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (7.0.8)
activesupport (= 7.0.8)
erubi (~> 1.11)
rails-dom-testing (~> 2.2)
rails-html-sanitizer (~> 1.6)
activejob (7.1.2)
activesupport (= 7.1.2)
globalid (>= 0.3.6)
activemodel (7.0.8)
activesupport (= 7.0.8)
activerecord (7.0.8)
activemodel (= 7.0.8)
activesupport (= 7.0.8)
activestorage (7.0.8)
actionpack (= 7.0.8)
activejob (= 7.0.8)
activerecord (= 7.0.8)
activesupport (= 7.0.8)
activemodel (7.1.2)
activesupport (= 7.1.2)
activerecord (7.1.2)
activemodel (= 7.1.2)
activesupport (= 7.1.2)
timeout (>= 0.4.0)
activestorage (7.1.2)
actionpack (= 7.1.2)
activejob (= 7.1.2)
activerecord (= 7.1.2)
activesupport (= 7.1.2)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.8)
activesupport (7.1.2)
base64
bigdecimal
concurrent-ruby (~> 1.0, >= 1.0.2)
connection_pool (>= 2.2.5)
drb
i18n (>= 1.6, < 2)
minitest (>= 5.1)
mutex_m
tzinfo (~> 2.0)
acts_as_list (1.1.0)
activerecord (>= 4.2)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.5)
bootsnap (1.17.0)
msgpack (~> 1.2)
brakeman (6.1.0)
Expand All @@ -88,14 +99,17 @@ GEM
config (5.0.0)
deep_merge (~> 1.2, >= 1.2.1)
dry-validation (~> 1.0, >= 1.0.0)
connection_pool (2.4.1)
crass (1.0.6)
date (3.3.3)
date (3.3.4)
debug (1.9.0)
irb (~> 1.10)
reline (>= 0.3.8)
deep_merge (1.2.2)
diff-lcs (1.5.0)
docile (1.4.0)
drb (2.2.0)
ruby2_keywords
dry-configurable (1.1.0)
dry-core (~> 1.0, < 2)
zeitwerk (~> 2.6)
Expand Down Expand Up @@ -160,18 +174,18 @@ GEM
net-pop
net-smtp
marcel (1.0.2)
method_source (1.0.0)
mini_mime (1.1.5)
minitest (5.20.0)
msgpack (1.7.2)
net-imap (0.3.7)
mutex_m (0.2.0)
net-imap (0.4.8)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
net-protocol (0.2.2)
timeout
net-smtp (0.3.3)
net-smtp (0.4.0)
net-protocol
nio4r (2.5.9)
nokogiri (1.15.5-arm64-darwin)
Expand All @@ -194,36 +208,42 @@ GEM
nio4r (~> 2.0)
racc (1.7.3)
rack (2.2.8)
rack-session (1.0.2)
rack (< 3)
rack-test (2.1.0)
rack (>= 1.3)
rails (7.0.8)
actioncable (= 7.0.8)
actionmailbox (= 7.0.8)
actionmailer (= 7.0.8)
actionpack (= 7.0.8)
actiontext (= 7.0.8)
actionview (= 7.0.8)
activejob (= 7.0.8)
activemodel (= 7.0.8)
activerecord (= 7.0.8)
activestorage (= 7.0.8)
activesupport (= 7.0.8)
rackup (1.0.0)
rack (< 3)
webrick
rails (7.1.2)
actioncable (= 7.1.2)
actionmailbox (= 7.1.2)
actionmailer (= 7.1.2)
actionpack (= 7.1.2)
actiontext (= 7.1.2)
actionview (= 7.1.2)
activejob (= 7.1.2)
activemodel (= 7.1.2)
activerecord (= 7.1.2)
activestorage (= 7.1.2)
activesupport (= 7.1.2)
bundler (>= 1.15.0)
railties (= 7.0.8)
railties (= 7.1.2)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
railties (7.0.8)
actionpack (= 7.0.8)
activesupport (= 7.0.8)
method_source
railties (7.1.2)
actionpack (= 7.1.2)
activesupport (= 7.1.2)
irb
rackup (>= 1.0.0)
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.1.0)
rdoc (6.6.1)
Expand Down Expand Up @@ -288,6 +308,7 @@ GEM
rubocop-capybara (~> 2.17)
rubocop-factory_bot (~> 2.22)
ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5)
sentry-rails (5.15.0)
railties (>= 5.0)
sentry-ruby (~> 5.15.0)
Expand All @@ -301,12 +322,13 @@ GEM
simplecov_json_formatter (0.1.4)
stringio (3.1.0)
thor (1.3.0)
timeout (0.4.0)
timeout (0.4.1)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
tzinfo-data (1.2023.3)
tzinfo (>= 1.0.0)
unicode-display_width (2.5.0)
webrick (1.8.1)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
Expand All @@ -333,7 +355,7 @@ DEPENDENCIES
paper_trail
pg (~> 1.5)
puma (~> 6.4)
rails (~> 7.0.8)
rails (~> 7.1.2)
reverse_markdown (~> 2.1)
rspec-rails
rubocop-govuk
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/access_tokens_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def caller_identity
private

def token_params
params.permit(:owner, :description)
params.permit(:owner, :description, :permissions)
end

def token_deactivate_params
Expand Down
19 changes: 5 additions & 14 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class ApplicationController < ActionController::API
include ActionController::HttpAuthentication::Token::ControllerMethods
include ActionController::HttpAuthentication::Token

rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from ActiveRecord::RecordInvalid, with: :invalid_record
Expand Down Expand Up @@ -38,20 +38,11 @@ def authenticate_using_old_env_vars
end

def authenticate_using_access_tokens
if request.headers["X-Api-Token"].present?
token = request.headers["X-Api-Token"]
(request.headers["X-Api-Token"].presence || token_and_options(request)&.first).try! do |token|
@access_token = AccessToken.active.find_by_token_digest(Digest::SHA256.hexdigest(token))
if @access_token.present?
@access_token.update!(last_accessed_at: Time.zone.now)
true
else
false
end
else
authenticate_with_http_token do |token|
@access_token = AccessToken.active.find_by_token_digest(Digest::SHA256.hexdigest(token))
@access_token.update!(last_accessed_at: Time.zone.now) if @access_token.present?
end
return nil unless @access_token.present? && AccessTokenPolicy.new(@access_token, request).request?

@access_token.update!(last_accessed_at: Time.zone.now)
end
end

Expand Down
5 changes: 5 additions & 0 deletions app/models/access_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ class AccessToken < ApplicationRecord

scope :active, -> { where(deactivated_at: nil) }

enum :permissions, {
all: "all",
readonly: "readonly",
}, suffix: true, validate: true

def generate_token
users_token = SecureRandom.uuid
self.token_digest = Digest::SHA256.hexdigest(users_token)
Expand Down
12 changes: 12 additions & 0 deletions app/policies/access_token_policy.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class AccessTokenPolicy
attr_reader :access_token, :request

def initialize(access_token, request)
@access_token = access_token
@request = request
end

def request?
access_token.all_permissions? || request.get?
end
end
14 changes: 8 additions & 6 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
module FormsApi
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0
config.load_defaults 7.1

# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.
# Common ones are `templates`, `generators`, or `middleware`, for example.
config.autoload_lib(ignore: %w[assets tasks])

# Configuration for the application, engines, and railties goes here.
#
Expand All @@ -45,11 +50,8 @@ class Application < Rails::Application
# Use JSON log formatter for better support in Splunk. To use conventional
# logging use the Logger::Formatter.new.
config.log_formatter = JsonLogFormatter.new

if ENV["RAILS_LOG_TO_STDOUT"].present?
config.logger = ActiveSupport::Logger.new($stdout)
config.logger.formatter = config.log_formatter
end
config.logger = ActiveSupport::Logger.new($stdout)
config.logger.formatter = config.log_formatter

# Lograge is used to format the standard HTTP request logging
config.lograge.enabled = true
Expand Down
6 changes: 6 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true

# Highlight code that enqueued background job in logs.
config.active_job.verbose_enqueue_logs = true

# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true

Expand All @@ -62,6 +65,9 @@
# Uncomment if you wish to allow Action Cable access from any origin.
# config.action_cable.disable_request_forgery_protection = true

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

# Add docker hostname for connecting from docker containers to host
# so we can run other components in docker and the API locally
config.hosts << ".host.docker.internal"
Expand Down
Loading