# Copyright (c) 2012 Bingo Entreprenøren AS
# Copyright (c) 2012 Teknobingo Scandinavia AS
# Copyright (c) 2012 Knut I. Stenmark
# Copyright (c) 2012 Patrick Hanevold
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
module Trust
# = Trust Controller
module Controller
autoload :Resource, 'trust/controller/resource'
autoload :Properties, 'trust/controller/properties'
extend ActiveSupport::Concern
module ClassMethods
# Returns the controller Trust::Controller::Properties.
# If no properties are instantiated, it will be instantiated
#
# == Delegated methods
#
# The following methods are delegated to properties. See Trust::Controller::Properties for details
# * belongs_to - define one or more associations to parents
# * actions - acion definitions outside the restful actions
# * model - Redefine the model used in the controller (if it's name does not match the
# controller_path)
#
def properties
@properties ||= Trust::Controller::Properties.instantiate(self)
end
delegate :belongs_to, :actions, :model, :to => :properties
# Enables authorization in controller
#
# +trustee+ accepts +:off+ or a hash of +callback+ options such as +:except+ and +:only+
#
# +trustee+ automatically calls the class methods: +set_user+, +load_resource+ and +access_control+
#
# +trustee+ will raise an Trust::AccessDenied exception if the user is not permitted the action
#
# ==== Examples
#
# # enable permission check for all restful actions
# class AccountsController < ApplicationController
# login_required
# trustee
# end
#
# # disable all permission check
# class PasswordController < ApplicationController
# # assuming login_required and trustee has been in your application controller
# trustee :off
# end
#
# # enable permission check and loading for only :new and :create action
# class AccountsController < ApplicationController
# login_required
# trustee :only => [:new, :create]
# end
#
# ==== Caching Trust::AccessDenied exception
# Normally an exception handler is included in the ApplicationController. Example:
# class ApplicationController < ActionController::Base
# rescue_from Trust::AccessDenied do |exception|
# redirect_to root_url, :alert => exception.message
# end
# end
def trustee(*args)
module_eval do
include TrustInstanceMethods
set_user *args
load_resource *args
access_control *args
helper_method :can?, :resource
end
end
# Enable or disable +before_filter+ callback for setting the current user
#
# === Arguments:
#
# :off - switch callback off
# :only - only include these actions
# :except - except these actions
def set_user(*args)
_filter_setting(:set_user, *args)
end
# Enable or disable +before_filter+ callback for setting the loading resource
#
# === Arguments:
#
# :off - switch callback off
# :only - only include these actions
# :except - except these actions
def load_resource(*args)
_filter_setting(:load_resource, *args)
end
# Enable or disable +before_filter+ callback for setting the access control, i.e. verifying permissions
# for the logged in user
#
# === Arguments:
#
# :off - switch callback off
# :only - only include these actions
# :except - except these actions
def access_control(*args)
_filter_setting(:access_control, *args)
end
private
def _filter_setting(method, *args)
options = args.extract_options!
skip_before_filter method
unless args.include? :off
before_filter method, options
end
end
end
module TrustInstanceMethods
# Returns the controller Trust::Controller::Properties.
# If no properties are instantiated, it will be instantiated.
#
# == Delegated methods
#
# The following methods are delegated to properties. See Trust::Controller::Properties for details
# * belongs_to - define one or more associations to parents
# * actions - acion definitions outside the restful actions
# * model - Redefine the model used in the controller (if it's name does not match the
# controller_path)
#
def properties
self.class.properties
end
# Sets the current user. It assumes +current_user+ is defined.
#
# This method is triggered as a callback on +before_filter+.
# You may override this method.
#
# ==== Example
#
# def set_user
# Trust::Authorization.user = Thread[:current_user]
# end
def set_user
Trust::Authorization.user = current_user
end
# Returns the Trust::Controller::Resource resource for the controller.
#
# Available as a helper in views.
# See {Trust::Controller::Resource} for relevant methods.
def resource
@resource ||= Trust::Controller::Resource.new(self, self.class.properties, action_name, params, request)
end
# Loads the resource which basically means loading the instance and eventual parent defined through +belongs_to+
#
# This method is triggered as a callback on +before_filter+
# See {Trust::Controller::Resource} for more information
def load_resource
resource.load
end
# Performs the actual access_control.
#
# This method is triggered as a callback on +before_filter+
def access_control
Trust::Authorization.authorize!(action_name, resource.instance || resource.klass, resource.parent)
end
# Tests for current users permissions.
#
# If access control is not sufficient in controller, you may use this method.
# Also available as a helper in views.
#
# ==== Examples
# can? :edit # does the current user have permission to edit the current resource?
# # If there is a nested resource, the parent is automatically associated
# can? :edit, @customer # does the current user have permission to edit the given customer?
# # Parent is also passed on here.
# can? :edit, @account, @client # is current user allowed to edit the account associated with the client?
def can?(action_name, subject = resource.instance || resource.relation.new, parent = resource.parent)
Trust::Authorization.authorized?(action_name, subject, parent)
end
end
end
end