lib/zen/helper/acl.rb in zen-0.2.4.1 vs lib/zen/helper/acl.rb in zen-0.2.5

- old
+ new

@@ -1,133 +1,161 @@ #:nodoc: module Ramaze #:nodoc: module Helper ## - # This helper provides an easy way of working with the ACL system that - # ships with Zen. Using this helper you can restrict access to methods, - # view elements and pretty much everything else based on the user's - # permissions. + # This helper provides an easy way of working with the ACL system that ships with Zen. + # Using this helper you can restrict access to methods, view elements and pretty much + # everything else based on the user's permissions. # - # In order to use the ACL helper you'll need to define a trait named - # "extension_identifier" in your classes. Once this trait have been set you - # can use the "user_authorized?" method to verify the permissions of the current user. - # The first parameter is an array of required permissions, - # the second a boolean that indicates if either all or just a single permission must - # be set. + # In order to restrict certain actions to only those with the correct permissions you + # can use the method "user_authorized?". This method takes a list of required + # permissions and when the user has the correct permissions it will return true: # - # For more information about the ACL system you should read the documentation - # in the ACL controller, Users::Controllers::AccessRules(). + # user_authorized?([:read]) # => true # + # The method has 3 parameters: a list of permissions, a boolean that indicates whether + # all of them or just a single one is required and a third argument that can be used + # to manually specify the controller to validate against rather than the current node. + # + # user_authorized?([:read], true 'FoobarController') + # # @author Yorick Peterse # @since 0.1 - # @see Users::Controllers::AccessRules() + # @see Users::Controller::AccessRules() # module ACL + ## - # Retrieves all permissions for the current user - # along with the permissions set for all groups the user - # belongs to. Rather than loading a new instance of the User model - # we'll retrieve the model from the session variable set by the - # User helper provided by Ramaze. Doing this saves us a few queries. + # Builds a hash containing the permissions for all controllers. First all group + # based rules will be retrieved. If the user is in a super group he'll gain full + # access. However, if there's a user specific rule it will overwrite the rules set + # for the group. This means that if a group allows something but a user rule doesn't + # the user won't be able to gain access to the resource. # # @author Yorick Peterse # @since 0.1 - # @return [Mixed] returns a hash containing all rules per identifier along - # with a boolean that indicates if the user is in a super group. + # @return [Hash] # def extension_permissions - m = session[:user] - user_groups = m.user_groups - super_group = false - rules = [] - ordered_rules = {} - - user_groups.each do |group| - rules += group.access_rules - - if group.super_group == true - super_group = true - end + if session[:access_rules] + return session[:access_rules] end - - m.access_rules.each do |rule| - rules.push(rule) - end - - rules.each do |rule| - if !ordered_rules.key?(rule.extension) - ordered_rules[rule.extension] = [] - end - - [:create_access, :read_access, :update_access, :delete_access].each do |perm| - if rule.send(perm) === true or super_group == true - perm = perm.to_s.gsub!('_access', '').to_sym - - if !ordered_rules[rule.extension].include?(perm) - ordered_rules[rule.extension].push(perm) - end + + user = session[:user] + user_groups = user.user_groups + @used_rules = {} + available_rules = [:create_access, :read_access, :update_access, :delete_access] + + # First all group rules should be built + user_groups.each do |group| + # If it's a super group we'll add all rules + if group.super_group === true + ::Zen::Package::Controllers.each do |controller| + @used_rules[controller.to_s] = [:create, :read, :update, :delete] end end + + group.access_rules.each do |rule| + process_permissions(rule, available_rules) + end end - - return ordered_rules, super_group + + # Process all user specific rules + user.access_rules.each do |rule| + process_permissions(rule, available_rules) + end + + # Store the rules in the user's session so that they don't have to be re-processed + # every time this method is called. + session[:access_rules] = @used_rules + + return @used_rules end ## - # Checks if the user has the specified permissions for the current - # extension that was called. Returns true if this is the case and false - # otherwise. + # Checks if the user has the specified permissions for the current extension that + # was called. Returns true if this is the case and false otherwise. # # @author Yorick Peterse - # @param [Array] reqs Array of permissions that are required. - # @param [Boolean] require_all Boolean that specifies that the user - # should have ALL specified permissios. Setting this to false causes - # this method to return true if any of the permissions are set for the - # current user. - # @param [String] identifier A custom identifier to use for validating the user's - # permissions instead of using a class trait. - # @return [Boolean] + # @param [Array] required Array of permissions that are required. + # @param [Boolean] require_all Boolean that specifies that the user should have + # ALL specified permissios. Setting this to false causes this method to return true + # if any of the permissions are set for the current user. + # @param [String] controller When set this will overwrite the controller name of + # action.node. This is useful when you want to check the permissions of a different + # controller than the current one. + # @return [TrueClass] # - def user_authorized?(reqs, require_all = true, identifier = nil) - # Retrieve the identifier from the class trait if we didn't already have one - if identifier.nil? - identifier = ancestral_trait.values_at(:extension_identifier) - identifier = identifier[0] - end + def user_authorized?(required, require_all = true, controller = nil) + # Get the ACL list + rules = extension_permissions - # Still don't have an identifier? - if identifier.nil? - raise "You need to specify an extension identifier" + if !controller + controller = action.node.to_s end - - # Get the ACL list - rules = self.extension_permissions - super_group = rules[1] - rules = rules[0] - - # Super groups have full access - if super_group == true - return true - end - - # Deny access if the identifier is not found - if !rules.key?(identifier) + + if !rules.key?(controller) return false end - - # Verify the permissions - perms = rules[identifier] - - reqs.each do |req| - if require_all == false and perms.include?(req) + + required.each do |req| + if require_all === false and rules[controller].include?(req) return true - elsif !perms.include?(req) + elsif !rules[controller].include?(req) return false end end - + return true + end + + private + + ## + # Extracts and stores all the permissions from a given rule. + # + # @author Yorick Peterse + # @since 0.2.5 + # @param [Users::Model::AccessRule] rule Database record containing the details of + # a single rule. + # @param [Array] available_rules All the available rules that can be used. + # + def process_permissions(rule, available_rules) + available_rules.each do |available_rule| + # Add the rule to the list + if rule.send(available_rule) === true + method = :push + # Remove the rule + else + method = :delete + end + + available_rule = available_rule.to_s.gsub('_access', '').to_sym + controllers = [] + + # Process all controllers + if rule.controller === '*' + ::Zen::Package[rule.package].controllers.each do |name, controller| + controllers.push(controller.to_s) + end + # Process a single controller + else + controllers.push(rule.controller) + end + + # Add the rules for all the controllers + controllers.each do |c| + @used_rules[c] ||= [] + + if method === :push and @used_rules[c].include?(available_rule) + next + end + + # Add or remove the permission + @used_rules[c].send(method, available_rule) + end + end end end end end