-
-
Save bjeanes/a48be85c35614fea4c31a53ae66e287d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Authentication | |
class API | |
def initialize( | |
config_name = nil, | |
env: {}, | |
session: {}, | |
**opts | |
) | |
@opts = opts | |
@session = session | |
@env = env | |
@auth_class = RodauthApp.opts[:rodauths].fetch(config_name) | |
end | |
def internal(**opts, &blk) | |
blk ||= proc { self } | |
internal_request_eval(**opts) do | |
account_from_session if session_value | |
instance_eval(&blk) | |
end | |
end | |
private | |
def method_missing(meth, *args, **opts, &blk) | |
if delegated_class_method?(meth) | |
opts = @opts.merge(opts) | |
opts[:env] = @env.merge(opts[:env] || {}) | |
opts[:session] = @session.merge(opts[:session] || {}) | |
@auth_class.send(meth, *args, **opts, &blk) | |
elsif delegated_instance_method?(meth) | |
internal { send(meth) } | |
else | |
super | |
end | |
end | |
def respond_to_missing?(meth, _include_private = false) | |
delegated_class_method?(meth) || delegated_instance_method?(meth) || super | |
end | |
def delegated_class_method?(meth) | |
# This _should_ only match internal request methods, but this has only been anecdotally demonstrated | |
@auth_class.methods(_ancestors = false).include?(meth) | |
end | |
def delegated_instance_method?(meth) | |
return false unless meth =~ /(_path|_url|_redirect|\?)\z/ | |
@auth_class.instance_methods(_ancestors = true).include?(meth) || | |
@auth_class.private_instance_methods(_ancestors = true).include?(meth) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Authentication | |
class API | |
def initialize( | |
config_or_record = :user, | |
actor: nil, | |
evidence: nil, # deprecated, use :audit_reason | |
env: {}, | |
session: {}, | |
**opts | |
) | |
env = (RequestContext.request&.env || {}).merge(env || {}) | |
actor ||= RequestContext.actor || :missing # don't allow an explicit nil actor | |
opts[:audit_actor] = actor if actor | |
opts[:audit_reason] ||= evidence if evidence | |
opts[:allowed_emails] ||= :none | |
opts.each do |k, v| | |
env.merge!("internal.#{k}" => v) if v.present? | |
end | |
@config_name, @account = resolve_config_name_and_account(config_or_record) | |
@session = session | |
@env = env | |
@auth_class = RodauthApp.opts[:rodauths].fetch(@config_name) | |
end | |
def internal(**opts, &blk) | |
blk ||= proc { self } | |
internal_request_eval(**opts) do | |
account_from_session if session_value | |
instance_eval(&blk) | |
end | |
end | |
private | |
def resolve_config_name_and_account(config_or_record) | |
case config_or_record | |
when :admin, :user, 'admin', 'user' | |
[config_or_record.to_sym, nil] | |
when User, Admin # NOTE: object can be wrapped with Draper; this matches either way | |
config = config_or_record.model_name.param_key.to_sym # this approach for both decorated and naked objects | |
account = Account.find(config_or_record.id) | |
[config, account] | |
when Account | |
account = config_or_record | |
[(account.user ? :user : :admin), account] | |
else | |
raise ArgumentError, 'expected config name or instance of User or Admin' | |
end | |
end | |
def method_missing(meth, *args, **opts, &blk) | |
if delegated_class_method?(meth) | |
opts[:account_id] = @account.id if @account | |
opts[:env] = @env.merge(opts[:env] || {}) | |
opts[:session] = @session.merge(opts[:session] || {}) | |
@auth_class.send(meth, *args, **opts, &blk) | |
elsif delegated_instance_method?(meth) | |
internal { send(meth) } | |
else | |
super | |
end | |
end | |
def respond_to_missing?(meth, _include_private = false) | |
delegated_class_method?(meth) || delegated_instance_method?(meth) || super | |
end | |
def delegated_class_method?(meth) | |
# This _should_ only match internal request methods, but this has only been anecdotally demonstrated | |
@auth_class.methods(_ancestors = false).include?(meth) | |
end | |
def delegated_instance_method?(meth) | |
return false unless meth =~ /(_path|_url|_redirect|\?)\z/ | |
# Hmm I thought `.instance_methods` returned public+private but there are some methods we need that are | |
# only showing up in the latter, so check both. | |
@auth_class.instance_methods(_ancestors = true).include?(meth) || | |
@auth_class.private_instance_methods(_ancestors = true).include?(meth) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment