diff --git a/lib/aclatraz.rb b/lib/aclatraz.rb index fec0ee9..568c29f 100644 --- a/lib/aclatraz.rb +++ b/lib/aclatraz.rb @@ -13,7 +13,7 @@ module Aclatraz # Initialize Aclatraz system with given datastore. # - # Aclatraz.init :redis, "redis://localhost:6379/0" + # Aclatraz.init :redis, :host => "127.0.0.1", :database => 0 # Aclatraz.init :tokyocabinet, "./permissions.tch" # Aclatraz.init MyCustomDatastore, :option => 1 # ... def self.init(store, *args) diff --git a/lib/aclatraz/helpers.rb b/lib/aclatraz/helpers.rb index f5d592a..730454b 100644 --- a/lib/aclatraz/helpers.rb +++ b/lib/aclatraz/helpers.rb @@ -34,5 +34,11 @@ def pack(role, object=nil) end data.join("/") end + + # Resolve the given class name to a Class object + def resolve_class(name) + name.split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)} + end + end # Helpers end # Aclatraz diff --git a/lib/aclatraz/store/redis.rb b/lib/aclatraz/store/redis.rb index 399ee6d..d6009be 100644 --- a/lib/aclatraz/store/redis.rb +++ b/lib/aclatraz/store/redis.rb @@ -43,6 +43,34 @@ def roles(suspect=nil) end end + def permissions(for_role, suspect, object=nil) + given_klass = (object.nil? || object.is_a?(Class)) ? object : object.class + + permissions = @backend.smembers(SUSPECT_ROLES_KEY % suspect_id(suspect)).map { |role| + role = unpack(role) + if role.size > 1 && role[0] == for_role + if 3 == role.size + klass = resolve_class(role[1]) + if (given_klass.nil? || klass == given_klass) + [klass, role[2]] # return the object id + else + nil + end + else + klass = resolve_class(role[1]) + if (given_klass.nil? || klass == given_klass) + klass # return the class + else + nil + end + end + else + nil + end + } + permissions.compact.uniq + end + def check(role, suspect, object=nil) @backend.sismember(SUSPECT_ROLES_KEY % suspect_id(suspect), pack(role.to_s, object)) or object && !object.is_a?(Class) ? check(role, suspect, object.class) : false diff --git a/lib/aclatraz/suspect.rb b/lib/aclatraz/suspect.rb index cfd1bcc..e0fcdb1 100644 --- a/lib/aclatraz/suspect.rb +++ b/lib/aclatraz/suspect.rb @@ -60,6 +60,48 @@ def all Aclatraz.store.roles(suspect) end alias_method :list, :all + + # Clears all roles assigned to the current object and returns the list. + # + # ==== Examples + # + # suspect.roles.assign(:foo) + # suspect.roles.assign(:bar) + # suspect.roles.clear # => ["foo", "bar"] + # suspect.roles.has?(:foo) # => false + # suspect.roles.has?(:bar) # => false + def clear + all.each do |role| + delete(role) + end + end + alias_method :delete_all, :clear + alias_method :remove_all, :clear + + # Enumerates all objects on which explicit permissions for the given role + # have been granted via suspect.roles.add(:role, object) + # + # This method does not return the objects that permissions were granted for to avoid + # costly single retrieval of potentially hundreds of objects from a store. Instead a + # tupel of [Class, Id] is returned for each individual object permissions were granted + # on and the Class is returned if permissions were granted on a Class. + # + # You can enumerate permissions granted on a type of objects by passing the Class or + # one instance of the Class as second parameter + # + # ==== Examples + # + # suspect.roles.assign(:author, Page.find(15)) + # suspect.roles.assign(:author, BlogEntry.find(15)) + # suspect.roles.assign(:author, Book) + # suspect.roles.permissions(:author) => [[Page, 15], [BlogEntry, 15], Book] + # suspect.roles.permissions(:author, Page) # => [[Page, 15]] + # suspect.roles.permissions(:author, Page.find(1)) # => [[Page, 15]] + # + def permissions(role, object=nil) + Aclatraz.store.permissions(role, suspect, object) + end + end # Roles class SemanticRoles diff --git a/spec/aclatraz/acl_spec.rb b/spec/aclatraz/acl_spec.rb index 7ebaf80..f31063d 100644 --- a/spec/aclatraz/acl_spec.rb +++ b/spec/aclatraz/acl_spec.rb @@ -2,7 +2,7 @@ describe "Aclatraz ACL" do subject { Aclatraz::ACL } - before(:all) { Aclatraz.init(:redis, "redis://localhost:6379/0") } + before(:all) {Aclatraz.init :redis, :host => "127.0.0.1", :database => 0 } it "should properly set suspect" do acl = subject.new(:suspect) {} diff --git a/spec/aclatraz/guard_spec.rb b/spec/aclatraz/guard_spec.rb index 34576f5..8c60241 100644 --- a/spec/aclatraz/guard_spec.rb +++ b/spec/aclatraz/guard_spec.rb @@ -3,7 +3,7 @@ describe "Aclatraz guard" do subject { Class.new(StubGuarded) } let(:suspect) { @suspect ||= StubSuspect.new } - before(:all) { Aclatraz.init(:redis, "redis://localhost:6379/0") } + before(:all) { Aclatraz.init :redis, :host => "127.0.0.1", :database => 0 } define_method(:deny_access) { raise_error(Aclatraz::AccessDenied) } it "#acl_guard? should be true" do diff --git a/spec/aclatraz/stores_spec.rb b/spec/aclatraz/stores_spec.rb index 70bb271..609f052 100644 --- a/spec/aclatraz/stores_spec.rb +++ b/spec/aclatraz/stores_spec.rb @@ -55,12 +55,12 @@ let(:target) { StubTarget.new } context "for Redis store", :store => 'redis' do - subject { Aclatraz.init(:redis, "redis://localhost:6379/0") } + subject { Aclatraz.init :redis, :host => "127.0.0.1", :database => 0 } it_should_behave_like :store do it "should respect persistent connection given on initalize" do Aclatraz.instance_variable_set("@store", nil) - Aclatraz.init(:redis, Redis.new("redis://localhost:6379/0")) + Aclatraz.init :redis, :host => "127.0.0.1", :database => 0 Aclatraz.store.instance_variable_get('@backend').should be_kind_of(Redis) Aclatraz.store.instance_variable_get('@backend').ping.should be_true end diff --git a/spec/aclatraz/suspect_spec.rb b/spec/aclatraz/suspect_spec.rb index 915b679..a0563f5 100644 --- a/spec/aclatraz/suspect_spec.rb +++ b/spec/aclatraz/suspect_spec.rb @@ -1,7 +1,7 @@ require File.dirname(__FILE__) + '/../spec_helper' describe "Aclatraz suspect" do - before(:all) { Aclatraz.init(:redis, "redis://localhost:6379/0") } + before(:all) { Aclatraz.init :redis, :host => "127.0.0.1", :database => 0 } subject { StubSuspect.new } let(:target) { StubTarget.new } @@ -27,6 +27,11 @@ end it "should allow to get list of roles assigned to user" do + subject.roles.clear + subject.roles.assign(:first) + subject.roles.assign(:second, StubTarget) + subject.roles.assign(:third, target) + (subject.roles.all - ["first", "second", "third"]) .should be_empty end @@ -40,6 +45,11 @@ subject.roles.has?(:third, target).should be_false end + it "should properly clear all given permissions" do + subject.roles.clear + subject.roles.all.should be_empty + end + context "syntactic sugars" do it "should properly set given role" do subject.is.first!