diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2b53930016..57e3e45da4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -54,6 +54,7 @@ class UnprocessableEntity < RuntimeError rescue_from GatewayTimeout, with: :gateway_timeout rescue_from BadGateway, with: :bad_gateway rescue_from Exceptions::NotImplemented, with: :not_implemented + rescue_from Errors::Conjur::ReadOnly::ActionNotPermitted, with: :method_not_allowed rescue_from Sequel::ValidationFailed, with: :validation_failed rescue_from Sequel::NoMatchingRow, with: :no_matching_row rescue_from Sequel::ForeignKeyConstraintViolation, with: :foreign_key_constraint_violation diff --git a/app/controllers/concerns/prepend.rb b/app/controllers/concerns/prepend.rb new file mode 100644 index 0000000000..a459240dce --- /dev/null +++ b/app/controllers/concerns/prepend.rb @@ -0,0 +1,15 @@ +module ReadOnlyPrepender + # Given a list of method symbols, preempt calls to them using a proxy that + # raises an error if read_only is enabled. + def write_protected(*method_names) + method_names.each do |m| + proxy = Module.new do + define_method(m) do |*args| + raise ::Errors::Conjur::ReadOnly::ActionNotPermitted unless !Rails.configuration.read_only + super *args + end + end + self.prepend proxy + end + end + end diff --git a/app/controllers/policies_controller.rb b/app/controllers/policies_controller.rb index 18e3771912..badc8cc08c 100644 --- a/app/controllers/policies_controller.rb +++ b/app/controllers/policies_controller.rb @@ -3,8 +3,11 @@ class PoliciesController < RestController include FindResource include AuthorizeResource + extend ReadOnlyPrepender + before_action :current_user before_action :find_or_create_root_policy + write_protected :put, :patch, :post after_action :publish_event, if: -> { response.successful? } rescue_from Sequel::UniqueConstraintViolation, with: :concurrent_load diff --git a/app/controllers/secrets_controller.rb b/app/controllers/secrets_controller.rb index 0030c5d1e9..8d2d6352a3 100644 --- a/app/controllers/secrets_controller.rb +++ b/app/controllers/secrets_controller.rb @@ -5,8 +5,10 @@ class SecretsController < RestController include FindResource include AuthorizeResource + extend ReadOnlyPrepender before_action :current_user + write_protected :create, :expire def create authorize(:update) diff --git a/app/domain/errors.rb b/app/domain/errors.rb index c86787881d..7635a2e176 100644 --- a/app/domain/errors.rb +++ b/app/domain/errors.rb @@ -57,6 +57,13 @@ module Conjur msg: "Resource '{0-resource}' requested by role '{1-role}' not found", code: "CONJ00123E" ) + + module ReadOnly + ActionNotPermitted = ::Util::TrackableErrorClass.new( + msg: "This action is not permitted when the server is in read-only mode", + code: "CONJ00153E" + ) + end end module Authorization diff --git a/config/environments/appliance.rb b/config/environments/appliance.rb index edb0340ffb..d65f633c8f 100644 --- a/config/environments/appliance.rb +++ b/config/environments/appliance.rb @@ -10,4 +10,5 @@ config.middleware.use(Rack::RememberUuid) config.audit_socket = '/run/conjur/audit.socket' config.audit_database ||= 'postgres://:5433/audit' + config.read_only = false end diff --git a/config/initializers/read_only_mode.rb b/config/initializers/read_only_mode.rb new file mode 100644 index 0000000000..64281ad24d --- /dev/null +++ b/config/initializers/read_only_mode.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +Rails.application.configure do + # Determines whether or not writable API endpoints are enabled. + # + # The `read_only` arguement is a boolean. By default, `read_only` is "Off". + # This means that any of the writable API endpoints will function as intended. + # + # A writable API endpoint is one whose controller method is decorated with the + # `@read_safe` decorator. When `read_only` is "On", such endpoints will return + # an HTTP 405 Method Not Allowed response code. + # + config.read_only = false + end + \ No newline at end of file