Skip to content

Commit

Permalink
feature: implicit authorization (#3292)
Browse files Browse the repository at this point in the history
* feature: implicit authorization

* wip

* improve authorize_association_for

* more improvements on authorize_association_for

* rm `raise_error_on_missing_policy_method`

* fix logic

* fix logic

* lint

* fix elseif

* fix method_name

* lint

* implicit authorization

* callable implicit_authorization

* implicit_authorization as attr_writer
  • Loading branch information
Paul-Bob authored Oct 9, 2024
1 parent 71383ad commit acfc6d0
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 33 deletions.
2 changes: 2 additions & 0 deletions app/controllers/avo/associations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ def authorize_if_defined(method, record = @record)

if @authorization.has_method?(method.to_sym)
@authorization.authorize_action method.to_sym
elsif Avo.configuration.authorization_client.present? && Avo.configuration.implicit_authorization
raise Avo::NotAuthorizedError.new
end
end

Expand Down
58 changes: 26 additions & 32 deletions lib/avo/concerns/checks_assoc_authorization.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,44 +5,38 @@ module ChecksAssocAuthorization

# Ex: A Post has many Comments
def authorize_association_for(policy_method)
policy_result = true
# Use the related_name as the base of the association
association_name = @reflection&.name
return true if association_name.blank?

if @reflection.present?
# Fetch the appropriate resource
reflection_resource = field.resource
# Fetch the record
# Hydrate the resource with the record if we have one
reflection_resource.hydrate(record: @parent_record) if @parent_record.present?
# Use the related_name as the base of the association
association_name = @reflection.name
# Fetch the appropriate resource
reflection_resource = field.resource

if association_name.present?
method_name = :"#{policy_method}_#{association_name}?".to_sym
# Hydrate the resource with the record if we have one
reflection_resource.hydrate(record: @parent_record) if @parent_record.present?

# Use the policy methods from the parent (Post)
service = reflection_resource.authorization
# Some policy methods should get the parent record in order to have the necessary information to do the authorization
# Example: Post->has_many->Comments
#
# When you want to authorize the creation/attaching of a Comment, you don't have the Comment instance.
# But you do have the Post instance and you can get that in your policy to authorize against.
record = if policy_method.in? [:view, :create, :attach, :act_on]
# Use the parent record (Post)
reflection_resource.record
else
# Override the record with the child record (Comment)
resource.record
end

if service.has_method?(method_name, raise_exception: false)
# Some policy methods should get the parent record in order to have the necessary information to do the authorization
# Example: Post->has_many->Comments
#
# When you want to authorize the creation/attaching of a Comment, you don't have the Comment instance.
# But you do have the Post instance and you can get that in your policy to authorize against.
parent_policy_methods = [:view, :create, :attach, :act_on]
# Use the policy methods from the parent (Post)
service = reflection_resource.authorization
method_name = :"#{policy_method}_#{association_name}?".to_sym

record = if parent_policy_methods.include?(policy_method)
# Use the parent record (Post)
reflection_resource.record
else
# Override the record with the child record (Comment)
resource.record
end
policy_result = service.authorize_action(method_name, record: record, raise_exception: false)
end
end
if service.has_method?(method_name, raise_exception: false)
service.authorize_action(method_name, record:, raise_exception: false)
else
!Avo.configuration.implicit_authorization
end

policy_result
end
end
end
Expand Down
6 changes: 6 additions & 0 deletions lib/avo/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class Configuration
attr_writer :logger
attr_writer :turbo
attr_writer :pagination
attr_writer :implicit_authorization
attr_accessor :timezone
attr_accessor :per_page
attr_accessor :per_page_steps
Expand Down Expand Up @@ -69,6 +70,7 @@ def initialize
@license_key = nil
@current_user = proc {}
@authenticate = proc {}
@implicit_authorization = false
@authorization_methods = {
index: "index?",
show: "show?",
Expand Down Expand Up @@ -252,6 +254,10 @@ def pagination
def default_locale
@locale || I18n.default_locale
end

def implicit_authorization
Avo::ExecutionContext.new(target: @implicit_authorization).handle
end
end

def self.configuration
Expand Down
2 changes: 1 addition & 1 deletion lib/avo/fields/has_base_field.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def authorized?
if service.has_method? method
service.authorize_action(method, raise_exception: false)
else
true
!Avo.configuration.implicit_authorization
end
end

Expand Down
1 change: 1 addition & 0 deletions lib/generators/avo/templates/initializer/avo.tt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Avo.configure do |config|
# }
# config.raise_error_on_missing_policy = false
config.authorization_client = nil
config.implicit_authorization = true

## == Localization ==
# config.locale = 'en-US'
Expand Down

0 comments on commit acfc6d0

Please sign in to comment.