Skip to content

Commit

Permalink
move connector extensions out of app/
Browse files Browse the repository at this point in the history
  • Loading branch information
akostadinov committed Dec 19, 2024
1 parent b8c8143 commit 7ca7b8a
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 141 deletions.
127 changes: 1 addition & 126 deletions app/lib/api_authentication/by_access_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,52 +16,10 @@ def current_user

before_action :verify_access_token_scopes
around_action :enforce_access_token_permission
rescue_from ApiAuthentication::ByAccessToken::Error,
rescue_from ApiAuthentication::Error,
with: :show_access_key_permission_error
end

module ReadOnlyTransaction
def read_only_transaction?
::ApiAuthentication::ByAccessToken::PermissionEnforcer.read_only?
end
end

module ConnectionExtension
extend ActiveSupport::Concern

included do
prepend TransactionMethods
end

module TransactionMethods
include ReadOnlyTransaction

def begin_db_transaction
transaction = ::ApiAuthentication::ByAccessToken::PermissionEnforcer.start_transaction
transaction ? execute(transaction) : super
end
end
end

module OracleEnhancedConnectionExtension
extend ActiveSupport::Concern

included do
prepend TransactionMethods
end

module TransactionMethods
include ReadOnlyTransaction

def begin_db_transaction
super

transaction = ::ApiAuthentication::ByAccessToken::PermissionEnforcer.set_transaction
execute(transaction) if transaction
end
end
end

protected

module ClassMethods
Expand Down Expand Up @@ -134,89 +92,6 @@ def verify_write_permission
raise PermissionError unless authenticated_token.try(:permission) == PermissionEnforcer::READ_WRITE
end

Error = Class.new(StandardError)

ScopeError = Class.new(Error)
PermissionError = Class.new(Error)


module PermissionEnforcer

extend self

READ_ONLY = 'ro'
READ_WRITE = 'rw'

def set_transaction
case level
when READ_ONLY then 'SET TRANSACTION READ ONLY'
when READ_WRITE then 'SET TRANSACTION READ WRITE'
end
end

def start_transaction
case level
when READ_ONLY then 'START TRANSACTION READ ONLY'
when READ_WRITE then 'START TRANSACTION READ WRITE'
end
end

class EnforceError < StandardError
end

def enforce(access_token, &block)
self.level = access_token&.permission

return yield unless requires_transaction?

if connection.transaction_open?
raise "Can't use read-only Access Token with transactional fixtures" if Rails.env.test?

error = EnforceError.new("couldn't open new transaction to enforce read-only access token")
System::ErrorReporting.report_error(error)
end

connection.transaction(requires_new: true, &block)
rescue ActiveRecord::StatementInvalid => error
if error.message =~ /read(-|\s)only transaction/i
raise PermissionError, error.message, caller
else
raise
end
ensure
Rails.logger.info "PermissionEnforcer#ensure clear level"
self.level = nil
end

def read_only?
level == READ_ONLY
end

private

def requires_transaction?
case level
when READ_ONLY then true
when READ_WRITE then false
end
end

THREAD_VARIABLE = :__permission_enforcer_level

def level=(level)
Rails.logger.info "PermissionEnforcer: level = #{level}"
Thread.current[THREAD_VARIABLE] = level
end

def level
Thread.current[THREAD_VARIABLE]
end

def connection
ActiveRecord::Base.connection
end
end

private

def access_token
Expand Down
28 changes: 14 additions & 14 deletions config/initializers/access_token_authentication.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# frozen_string_literal: true

Rails.application.config.to_prepare do
ActiveSupport.on_load(:active_record) do
if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
ActiveRecord::ConnectionAdapters::Mysql2Adapter.class_eval do
include ApiAuthentication::ByAccessToken::ConnectionExtension
end
ActiveSupport.on_load(:active_record) do
require "api_authentication.rb"

if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
ActiveRecord::ConnectionAdapters::Mysql2Adapter.class_eval do
include ApiAuthentication::ConnectorExtensions::ConnectionExtension
end
end

if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
include ApiAuthentication::ByAccessToken::OracleEnhancedConnectionExtension
end
if defined?(ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter)
ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
include ApiAuthentication::ConnectorExtensions::OracleEnhancedConnectionExtension
end
end

if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
include ApiAuthentication::ByAccessToken::ConnectionExtension
end
if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
include ApiAuthentication::ConnectorExtensions::ConnectionExtension
end
end
end
130 changes: 130 additions & 0 deletions lib/api_authentication.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# frozen_string_literal: true

module ApiAuthentication

Error = Class.new(StandardError)

ScopeError = Class.new(Error)
PermissionError = Class.new(Error)

module PermissionEnforcer

extend self

READ_ONLY = 'ro'
READ_WRITE = 'rw'

def set_transaction
case level
when READ_ONLY then 'SET TRANSACTION READ ONLY'
when READ_WRITE then 'SET TRANSACTION READ WRITE'
end
end

def start_transaction
case level
when READ_ONLY then 'START TRANSACTION READ ONLY'
when READ_WRITE then 'START TRANSACTION READ WRITE'
end
end

class EnforceError < StandardError
end

def enforce(access_token, &block)
self.level = access_token&.permission

return yield unless requires_transaction?

if connection.transaction_open?
raise "Can't use read-only Access Token with transactional fixtures" if Rails.env.test?

error = EnforceError.new("couldn't open new transaction to enforce read-only access token")
System::ErrorReporting.report_error(error)
end

connection.transaction(requires_new: true, &block)
rescue ActiveRecord::StatementInvalid => error
if error.message =~ /read(-|\s)only transaction/i
raise PermissionError, error.message, caller
else
raise
end
ensure
Rails.logger.info "PermissionEnforcer#ensure clear level"
self.level = nil
end

def read_only?
level == READ_ONLY
end

private

def requires_transaction?
case level
when READ_ONLY then true
when READ_WRITE then false
end
end

THREAD_VARIABLE = :__permission_enforcer_level

def level=(level)
Rails.logger.info "PermissionEnforcer: level = #{level}"
Thread.current[THREAD_VARIABLE] = level
end

def level
Thread.current[THREAD_VARIABLE]
end

def connection
ActiveRecord::Base.connection
end
end

module ConnectorExtensions
module ReadOnlyTransaction
def read_only_transaction?
::ApiAuthentication::PermissionEnforcer.read_only?
end
end

module ConnectionExtension
extend ActiveSupport::Concern

included do
prepend TransactionMethods
end

module TransactionMethods
include ReadOnlyTransaction

def begin_db_transaction
transaction = ::ApiAuthentication::PermissionEnforcer.start_transaction
transaction ? execute(transaction) : super
end
end
end

module OracleEnhancedConnectionExtension
extend ActiveSupport::Concern

included do
prepend TransactionMethods
end

module TransactionMethods
include ReadOnlyTransaction

def begin_db_transaction
super

transaction = ::ApiAuthentication::PermissionEnforcer.set_transaction
execute(transaction) if transaction
end
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def test_authenticated_token_correct_scope

def test_rescue_handlers
handlers = {
'ApiAuthentication::ByAccessToken::Error' => :show_access_key_permission_error
'ApiAuthentication::Error' => :show_access_key_permission_error
}
assert_equal handlers, rescue_handlers.to_h
end
Expand Down

0 comments on commit 7ca7b8a

Please sign in to comment.