Permalink
Browse files

* added restricted config which tells the guard when to call the give…

…n block

* a given block can filter the allowed groups for the action/resource

* the check method from the rails controller always returns the set of allowed groups
  • Loading branch information...
1 parent 6d4748c commit ca664bcc95e80d5c114cf9193f7d9f1165388bd1 @mkristian committed Mar 12, 2012
@@ -2,7 +2,7 @@
module Ixtlan
module Guard
- class GuardNG
+ class Guard
attr_reader :superuser
@@ -13,6 +13,10 @@ def initialize(options = {})
@logger = options[:logger]
end
+ def superuser_name
+ @superuser[0]
+ end
+
def block_groups(groups)
@blocked_groups = (groups || []).collect { |g| g.to_s}
@blocked_groups.delete(@superuser)
@@ -33,14 +37,19 @@ def logger
end
end
- def allowed_groups(resource_name, action, current_group_names)
- allowed = @config.allowed_groups(resource_name, action) - blocked_groups + @superuser
- if allowed.member?('*')
- # keep superuser in current_groups if in there
- current_group_names - (blocked_groups - @superuser)
- else
- allowed & current_group_names
- end
+ def allowed_groups_and_restricted(resource_name,
+ action,
+ current_group_names)
+ allowed, restricted =
+ @config.allowed_groups_and_restricted(resource_name, action)
+ allowed = allowed - blocked_groups + @superuser
+ result = if allowed.member?('*')
+ # keep superuser in current_groups if in there
+ current_group_names - (blocked_groups - @superuser)
+ else
+ allowed & current_group_names
+ end
+ [result, restricted]
end
def group_map(current_groups)
@@ -59,37 +68,40 @@ def group_map(current_groups)
end
private :group_map
- def allowed?(resource_name, action, current_groups, association = nil, &block)
+ def check(resource_name, action, current_groups, &block)
+ action = action.to_s
group_map = group_map(current_groups)
- allowed_group_names = allowed_groups(resource_name, action, group_map.keys)
+ allowed_group_names, restricted =
+ allowed_groups_and_restricted(resource_name, action, group_map.keys)
+
logger.debug { "guard #{resource_name}##{action}: #{allowed_group_names.size > 0}" }
+
if allowed_group_names.size > 0
- if block || association
- group_allowed?(group_map, allowed_group_names, association, &block)
- else
- true
+ groups = allowed_group_names.collect { |name| group_map[name] }
+ # call block to filter groups if restricted applies
+ if restricted && !allowed_group_names.member?(superuser_name)
+ raise "no block given to filter groups" unless block
+ except = restricted['except'] || []
+ only = restricted['only'] || [action]
+ if !except.member?(action) && only.member?(action)
+ groups = block.call(groups)
+ end
end
+
+ # nil means 'access denied', i.e. there are no allowed groups
+ groups if groups.size > 0
else
unless @config.has_guard?(resource_name)
raise ::Ixtlan::Guard::GuardException.new("no guard config for '#{resource_name}'")
else
- false
+ # nil means 'access denied', i.e. there are no allowed groups
+ nil
end
end
end
- def group_allowed?(group_map, allowed_group_names, association, &block)
- g = allowed_group_names.detect do |group_name|
- block.call(group_map[group_name], association)
- end if association && block
- logger.debug do
- if g
- "found group #{g} for #{association}"
- else
- "no group found for #{association}"
- end
- end
- g != nil
+ def allowed?(resource, action, groups, &block)
+ check(resource, action, groups, &block) != nil
end
def permissions(current_groups, &block)
@@ -101,20 +113,26 @@ def permissions(current_groups, &block)
perm = Node.new(:permission)
perm[:resource] = resource
perm[:actions] = nodes
- default_actions = actions.delete('defaults') || []
- default_actions = group_map.keys & (default_actions + @superuser) unless default_actions.member?('*')
+
+ restricted = actions.delete('restricted')
+
+ # setup default_groups
+ default_groups = actions.delete('defaults') || []
+ default_groups = group_map.keys & (default_groups + @superuser) unless default_groups.member?('*')
+
deny = if actions.size == 0
# no actions
- # deny = false: !default_actions.member?('*')
- # deny = true: default_actions.member?('*') || current_group_names.member?(@superuser[0])
- default_actions.member?('*') || group_map.keys.member?(@superuser[0]) || !group_map.keys.detect {|g| default_actions.member? g }.nil?
+ # deny = false: !default_groups.member?('*')
+ # deny = true: default_groups.member?('*') || current_group_names.member?(@superuser[0])
+ default_groups.member?('*') || group_map.keys.member?(@superuser[0]) || !group_map.keys.detect {|g| default_groups.member? g }.nil?
else
# actions
- # deny = false : default_actions == []
- # deny = true : default_actions.member?('*')
- default_actions.size != 0 || default_actions.member?('*')
+ # deny = false : default_groups == []
+ # deny = true : default_groups.member?('*')
+ default_groups.size != 0 || default_groups.member?('*')
end
perm[:deny] = deny
+
actions.each do |action, groups|
group_names = groups.collect { |g| g.is_a?(Hash) ? g.keys : g }.flatten if groups
node = Node.new(:action)
@@ -125,22 +143,29 @@ def permissions(current_groups, &block)
names = group_map.keys & ((group_names || []) + @superuser)
names.collect { |name| group_map[name] }
end
+
if (deny && allowed_groups.size == 0) || (!deny && allowed_groups.size > 0)
node[:name] = action
if block
if allowed_groups.size > 0
- node.content.merge!(block.call(resource, action, allowed_groups) || {})
+ assos = block.call(resource, allowed_groups)
+ node[:associations] = assos if assos && assos.size > 0
else
- perm.content.merge!(block.call(resource, action, group_map.values) || {})
+ assos = block.call(resource, group_map.values)
+ perm[:associations] = assos if assos && assos.size > 0
end
end
nodes << node
+ elsif deny && allowed_groups.size > 0 && block
+ assos = block.call(resource, group_map.values)
+ perm[:associations] = assos if assos && assos.size > 0
end
end
# TODO is that right like this ?
- # only default_actions, i.e. no actions !!!
+ # only default_groups, i.e. no actions !!!
if block && actions.size == 0 && deny
- perm.content.merge!(block.call(resource, nil, group_map.values) || {})
+ assos = block.call(resource, group_map.values)
+ perm[:associations] = assos if assos && assos.size > 0
end
perms << perm
end
@@ -9,13 +9,14 @@ def initialize(options = {})
raise GuardException.new("guards directory does not exists: #{@guards_dir}") unless File.directory?(@guards_dir)
end
- def allowed_groups(resource, action)
+ def allowed_groups_and_restricted(resource, action)
if resource && action
- resource = resource.to_s
- groups = send(@load_method, resource)
- groups[action.to_s] || groups["defaults"] || []
+ groups = send(@load_method, resource.to_s)
+ restricted = groups.delete('restricted')
+ [groups[action.to_s] || groups["defaults"] || [],
+ restricted == true ? {} : restricted]
else
- []
+ [[], nil]
end
end
@@ -35,31 +35,21 @@ def guard
Rails.application.config.guard
end
- def allowed?(action, association = nil, &block)
- group_method = respond_to?(:current_user_groups) ? :current_user_groups : :groups_for_current_user
- guard.allowed?(params[:controller],
- action,
- send(group_method) || []
- association,
- &block)
+ def allowed?(action, controller = params[:controller], &block)
+ group_method = respond_to?(:current_groups) ? :current_groups : :groups_for_current_user
+ guard.check(controller,
+ action,
+ send(group_method) || [],
+ &block) != nil
end
- def check(association = nil, &block)
- unless allowed?(params[:action], association, &block)
- if association
- raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}##{association.class}(#{association.id})'")
- else
- raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}'")
- end
+ def check(&block)
+ unless allowed?(params[:action], &block)
+ raise ::Ixtlan::Guard::PermissionDenied.new("permission denied for '#{params[:controller]}##{params[:action]}'")
end
true
end
alias :authorize :check
-
- def authorization
- warn "DEPRECATED: use 'authorize' instead"
- check
- end
end
end
end
@@ -71,8 +61,12 @@ def self.included(base)
end
module InstanceMethods #:nodoc:
- def allowed?(resource, action)
- controller.send(:guard).allowed?(resource, action, controller.send(:groups_for_current_user))
+ def allowed?(action, resource = nil)
+ if resource
+ controller.allowed?(action, resource)
+ else
+ controller.allowed?(action)
+ end
end
end
end
@@ -1,5 +1,5 @@
require 'rails'
-require 'ixtlan/guard/guard_ng'
+require 'ixtlan/guard/guard'
require 'ixtlan/guard/guard_rails'
require 'logger'
require 'fileutils'
@@ -20,7 +20,7 @@ class Railtie < Rails::Railtie
}
options[:logger] = logger unless defined?(Slf4r)
FileUtils.mkdir_p(app.config.guards_dir)
- app.config.guard = Ixtlan::Guard::GuardNG.new(options)
+ app.config.guard = Ixtlan::Guard::Guard.new(options)
::ActionController::Base.send(:include, Ixtlan::ActionController::Guard)
::ActionController::Base.send(:before_filter, :authorize)
View
@@ -1,5 +1,5 @@
require 'spec_helper'
-require 'ixtlan/guard/guard_ng'
+require 'ixtlan/guard/guard'
require 'logger'
require 'fileutils'
@@ -12,11 +12,11 @@ def $logger.debug(&block)
# info("\n\t[debug] " + block.call)
end
-describe Ixtlan::Guard::GuardNG do
+describe Ixtlan::Guard::Guard do
context "without caching" do
def not_cached
- $not_cached ||= Ixtlan::Guard::GuardNG.new(:guards_dir => File.dirname($target),
+ $not_cached ||= Ixtlan::Guard::Guard.new(:guards_dir => File.dirname($target),
:logger => $logger )
end
@@ -37,7 +37,7 @@ def not_cached
context "with caching" do
def cached
- $cached ||= Ixtlan::Guard::GuardNG.new(:guards_dir => File.dirname($target),
+ $cached ||= Ixtlan::Guard::Guard.new(:guards_dir => File.dirname($target),
:logger => $logger,
:cache => true)
end
Oops, something went wrong.

0 comments on commit ca664bc

Please sign in to comment.