aclatraz 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -1,22 +1,11 @@
1
- ## MAC OS
2
1
  .DS_Store
3
-
4
- ## TEXTMATE
5
2
  *.tmproj
6
3
  tmtags
7
-
8
- ## EMACS
9
4
  *~
10
5
  \#*
11
6
  .\#*
12
-
13
- ## VIM
14
7
  *.swp
15
-
16
- ## PROJECT::GENERAL
17
8
  coverage
18
9
  rdoc
19
10
  pkg
20
-
21
- ## PROJECT::SPECIFIC
22
11
  *.rdb
@@ -1,3 +1,9 @@
1
+ == Version 0.1.4
2
+
3
+ * Added MongoDB store
4
+ * Cleaner and more understandable specs
5
+ * ACL bugfixes
6
+
1
7
  == Version 0.1.3
2
8
 
3
9
  * Added Cassandra store
data/Rakefile CHANGED
@@ -1,69 +1,71 @@
1
- require 'rubygems'
2
- require 'rake'
3
-
4
- begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
7
- gem.name = "aclatraz"
8
- gem.email = "kriss.kowalik@gmail.com"
9
- gem.homepage = "http://github.com/nu7hatch/aclatraz"
10
- gem.authors = ["Kriss 'nu7hatch' Kowalik"]
11
- gem.summary = %Q{Flexible access control that doesn't sucks!}
12
- gem.description = <<-DESCR
13
- Extremaly fast, flexible and intuitive access control mechanism,
14
- powered by fast key value stores like Redis.
15
- DESCR
16
- gem.add_dependency "dictionary", "~> 1.0"
17
- gem.add_development_dependency "rspec", "~> 2.0"
18
- gem.add_development_dependency "mocha", "~> 0.9"
19
- gem.add_development_dependency "redis", "~> 2.0"
20
- gem.add_development_dependency "riak-client", "~> 0.8"
21
- gem.add_development_dependency "cassandra", "~> 0.8"
22
- end
23
- Jeweler::GemcutterTasks.new
24
- rescue LoadError
25
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
26
- end
27
-
1
+ # -*- ruby -*-
2
+ $:.unshift(File.expand_path('../lib', __FILE__))
3
+ require 'aclatraz/version'
28
4
  require 'rspec/core/rake_task'
5
+ require 'rake/rdoctask'
6
+
29
7
  RSpec::Core::RakeTask.new(:spec) do |t|
30
8
  t.pattern = 'spec/**/*_spec.rb'
31
- t.rspec_opts = %q[--colour --backtrace]
9
+ t.rspec_opts = %q[-c -b]
32
10
  end
33
11
 
34
12
  RSpec::Core::RakeTask.new(:rcov) do |t|
35
13
  t.rcov = true
36
- t.rspec_opts = %q[--colour --backtrace]
37
- t.rcov_opts = %q[--exclude "spec" --text-report]
14
+ t.rspec_opts = %q[-c -b]
15
+ t.rcov_opts = %q[-T -x "spec"]
38
16
  end
39
17
 
40
- task :spec => :check_dependencies
41
- task :default => :spec
42
-
43
- require 'rake/rdoctask'
44
18
  Rake::RDocTask.new do |rdoc|
45
- version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
19
  rdoc.rdoc_dir = 'rdoc'
47
- rdoc.title = "ACLatraz #{version}"
20
+ rdoc.title = "ACLatraz #{Aclatraz.version}"
48
21
  rdoc.rdoc_files.include('README*')
49
22
  rdoc.rdoc_files.include('lib/**/*.rb')
50
23
  end
51
24
 
25
+ task :default => :spec
26
+
27
+ desc "Build current version as a rubygem"
28
+ task :build do
29
+ `gem build aclatraz.gemspec`
30
+ `mv aclatraz-*.gem pkg/`
31
+ end
32
+
33
+ desc "Relase current version to rubygems.org"
34
+ task :release => :build do
35
+ `git tag -am "Version bump to #{Aclatraz.version}" v#{Aclatraz.version}`
36
+ `git push origin master`
37
+ `git push origin master --tags`
38
+ `gem push pkg/aclatraz-#{Aclatraz.version}.gem`
39
+ end
40
+
52
41
  namespace :benchmark do
53
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
42
+ require 'aclatraz'
54
43
  require 'benchmark'
55
- require "aclatraz"
56
44
 
45
+ benchmarks = File.expand_path("../spec/alcatraz_bm.rb", __FILE__)
46
+
47
+ desc "Redis store benchmarks"
57
48
  task :redis do
58
49
  Aclatraz.init(:redis)
59
- require File.dirname(__FILE__)+"/spec/alcatraz_bm"
50
+ load benchmarks
60
51
  end
52
+
53
+ desc "Cassandra store benchmarks"
61
54
  task :cassandra do
62
55
  Aclatraz.init(:cassandra, "Super1", "Keyspace1")
63
- require File.dirname(__FILE__)+"/spec/alcatraz_bm"
56
+ load benchmarks
64
57
  end
58
+
59
+ desc "Riak store benchmarks"
65
60
  task :riak do
66
61
  Aclatraz.init(:riak, "roles")
67
- require File.dirname(__FILE__)+"/spec/alcatraz_bm"
62
+ load benchmarks
63
+ end
64
+
65
+ desc "MongoDB store benchmarks"
66
+ task :mongo do
67
+ require 'mongo'
68
+ Aclatraz.init(:mongo, "roles", Mongo::Connection.new.db("aclatraz_test"))
69
+ load benchmarks
68
70
  end
69
71
  end
@@ -1,95 +1,24 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
- # -*- encoding: utf-8 -*-
1
+ # -*- ruby -*-
2
+ $:.unshift(File.expand_path('../lib', __FILE__))
3
+ require 'aclatraz/version'
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = %q{aclatraz}
8
- s.version = "0.1.3"
6
+ s.name = 'aclatraz'
7
+ s.version = Aclatraz.version
8
+ s.homepage = 'http://github.com/nu7hatch/aclatraz'
9
+ s.email = ['chris@nu7hat.ch']
10
+ s.authors = ['Chris Kowalik']
11
+ s.summary = %q{Flexible access control mechanism!}
12
+ s.description = %q{Extremaly fast, flexible and intuitive access control mechanism, powered by fast key value stores like Redis.}
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
15
+ s.require_paths = %w[lib]
16
+ s.extra_rdoc_files = %w[LICENSE README.rdoc CHANGELOG.rdoc TODO.rdoc]
9
17
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Kriss 'nu7hatch' Kowalik"]
12
- s.date = %q{2010-10-16}
13
- s.description = %q{ Extremaly fast, flexible and intuitive access control mechanism,
14
- powered by fast key value stores like Redis.
15
- }
16
- s.email = %q{kriss.kowalik@gmail.com}
17
- s.extra_rdoc_files = [
18
- "LICENSE",
19
- "README.rdoc"
20
- ]
21
- s.files = [
22
- ".document",
23
- ".gitignore",
24
- "CHANGELOG.rdoc",
25
- "LICENSE",
26
- "README.rdoc",
27
- "Rakefile",
28
- "TODO.rdoc",
29
- "VERSION",
30
- "aclatraz.gemspec",
31
- "examples/dinner.rb",
32
- "lib/aclatraz.rb",
33
- "lib/aclatraz/acl.rb",
34
- "lib/aclatraz/guard.rb",
35
- "lib/aclatraz/helpers.rb",
36
- "lib/aclatraz/store.rb",
37
- "lib/aclatraz/store/cassandra.rb",
38
- "lib/aclatraz/store/redis.rb",
39
- "lib/aclatraz/store/riak.rb",
40
- "lib/aclatraz/suspect.rb",
41
- "spec/aclatraz/acl_spec.rb",
42
- "spec/aclatraz/guard_spec.rb",
43
- "spec/aclatraz/helpers_spec.rb",
44
- "spec/aclatraz/stores_spec.rb",
45
- "spec/aclatraz/suspect_spec.rb",
46
- "spec/aclatraz_spec.rb",
47
- "spec/alcatraz_bm.rb",
48
- "spec/spec_helper.rb"
49
- ]
50
- s.homepage = %q{http://github.com/nu7hatch/aclatraz}
51
- s.rdoc_options = ["--charset=UTF-8"]
52
- s.require_paths = ["lib"]
53
- s.rubygems_version = %q{1.3.7}
54
- s.summary = %q{Flexible access control that doesn't sucks!}
55
- s.test_files = [
56
- "spec/alcatraz_bm.rb",
57
- "spec/spec_helper.rb",
58
- "spec/aclatraz/guard_spec.rb",
59
- "spec/aclatraz/helpers_spec.rb",
60
- "spec/aclatraz/acl_spec.rb",
61
- "spec/aclatraz/stores_spec.rb",
62
- "spec/aclatraz/suspect_spec.rb",
63
- "spec/aclatraz_spec.rb",
64
- "examples/dinner.rb"
65
- ]
66
-
67
- if s.respond_to? :specification_version then
68
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
69
- s.specification_version = 3
70
-
71
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
72
- s.add_runtime_dependency(%q<dictionary>, ["~> 1.0"])
73
- s.add_development_dependency(%q<rspec>, ["~> 2.0"])
74
- s.add_development_dependency(%q<mocha>, ["~> 0.9"])
75
- s.add_development_dependency(%q<redis>, ["~> 2.0"])
76
- s.add_development_dependency(%q<riak-client>, ["~> 0.8"])
77
- s.add_development_dependency(%q<cassandra>, ["~> 0.8"])
78
- else
79
- s.add_dependency(%q<dictionary>, ["~> 1.0"])
80
- s.add_dependency(%q<rspec>, ["~> 2.0"])
81
- s.add_dependency(%q<mocha>, ["~> 0.9"])
82
- s.add_dependency(%q<redis>, ["~> 2.0"])
83
- s.add_dependency(%q<riak-client>, ["~> 0.8"])
84
- s.add_dependency(%q<cassandra>, ["~> 0.8"])
85
- end
86
- else
87
- s.add_dependency(%q<dictionary>, ["~> 1.0"])
88
- s.add_dependency(%q<rspec>, ["~> 2.0"])
89
- s.add_dependency(%q<mocha>, ["~> 0.9"])
90
- s.add_dependency(%q<redis>, ["~> 2.0"])
91
- s.add_dependency(%q<riak-client>, ["~> 0.8"])
92
- s.add_dependency(%q<cassandra>, ["~> 0.8"])
93
- end
18
+ s.add_runtime_dependency 'dictionary', ['~> 1.0']
19
+ s.add_development_dependency 'rspec', ["~> 2.0"]
20
+ s.add_development_dependency 'mocha', [">= 0.9"]
21
+ s.add_development_dependency 'redis', [">= 2.0"]
22
+ s.add_development_dependency 'riak-client', [">= 0.8"]
23
+ s.add_development_dependency 'cassandra', [">= 0.8"]
94
24
  end
95
-
@@ -1,12 +1,13 @@
1
1
  require 'dictionary'
2
2
 
3
- require 'aclatraz/helpers'
4
- require 'aclatraz/store'
5
- require 'aclatraz/acl'
6
- require 'aclatraz/guard'
7
- require 'aclatraz/suspect'
8
-
9
3
  module Aclatraz
4
+ require 'aclatraz/helpers'
5
+ require 'aclatraz/store'
6
+ require 'aclatraz/acl'
7
+ require 'aclatraz/guard'
8
+ require 'aclatraz/suspect'
9
+ require 'aclatraz/version'
10
+
10
11
  # Raised when suspect don't have permission to execute action
11
12
  class AccessDenied < Exception; end
12
13
 
@@ -4,9 +4,9 @@ module Aclatraz
4
4
  # All permissions defined in this action.
5
5
  attr_reader :permissions
6
6
 
7
- def initialize(parent, &block) # :nodoc:
7
+ def initialize(parent, &block)
8
8
  @parent = parent
9
- @permissions = Dictionary.new
9
+ @permissions = Dictionary.new {|h,k| h[k] = false}.merge(parent.permissions)
10
10
  instance_eval(&block) if block_given?
11
11
  end
12
12
 
@@ -54,9 +54,7 @@ module Aclatraz
54
54
  end
55
55
 
56
56
  def clone(parent) # :nodoc:
57
- cloned = self.class.new(parent)
58
- cloned.instance_variable_set("@permissions", Hash.new(permissions))
59
- cloned
57
+ self.class.new(parent)
60
58
  end
61
59
  end # Action
62
60
 
@@ -66,7 +64,7 @@ module Aclatraz
66
64
  # Current suspected object.
67
65
  attr_accessor :suspect
68
66
 
69
- def initialize(suspect, &block) # :nodoc:
67
+ def initialize(suspect, &block)
70
68
  @actions = {}
71
69
  @suspect = suspect
72
70
  evaluate(&block) if block_given?
@@ -87,7 +85,7 @@ module Aclatraz
87
85
 
88
86
  # List of permissions defined in default action.
89
87
  def permissions
90
- @actions[:_] ? @actions[:_].permissions : Dictionary.new
88
+ @actions[:_] ? @actions[:_].permissions : Dictionary.new {|h,k| h[k] = false}
91
89
  end
92
90
 
93
91
  # Syntactic sugar for actions <tt>actions[action]</tt>.
@@ -109,7 +107,7 @@ module Aclatraz
109
107
  # end
110
108
  # end
111
109
  def on(action, &block)
112
- raise ArgumentError, "No block given" unless block_given?
110
+ raise ArgumentError, "No block given!" unless block_given?
113
111
  if @actions.key?(action)
114
112
  @actions[action].instance_eval(&block)
115
113
  else
@@ -3,6 +3,7 @@ module Aclatraz
3
3
  autoload :Redis, 'aclatraz/store/redis'
4
4
  autoload :Riak, 'aclatraz/store/riak'
5
5
  autoload :Cassandra, 'aclatraz/store/cassandra'
6
+ autoload :Mongo, 'aclatraz/store/mongo'
6
7
  #autoload :Memcached, 'aclatraz/store/memcached'
7
8
  #autoload :TokyoCabinet, 'aclatraz/store/tokyocabinet'
8
9
  end # Store
@@ -1,3 +1,5 @@
1
+ require 'cassandra'
2
+
1
3
  module Aclatraz
2
4
  module Store
3
5
  # List of global roles are stored in `roles => all` key. Each suspect has its
@@ -16,9 +18,6 @@ module Aclatraz
16
18
  SUSPECT_ROLES_KEY = "suspect.%s"
17
19
 
18
20
  def initialize(*args)
19
- require 'cassandra' rescue raise LoadError, \
20
- "You must install the `casssandra` gem to use Cassandra store"
21
-
22
21
  @family = args.shift
23
22
  @backend = if args.first.respond_to?(:keyspace)
24
23
  args.first
@@ -0,0 +1,56 @@
1
+ require 'mongo'
2
+
3
+ module Aclatraz
4
+ module Store
5
+ # For MongoDB, each role is stored in separate row:
6
+ #
7
+ # {"roles" => [
8
+ # {"suspect" => 1, "role" => "admin" },
9
+ # {"suspect" => 2, "role" => "manager/ClassName" },
10
+ # {"suspect" => 3, "role" => "owner/ClassName/1" }
11
+ # ]}
12
+ class Mongo
13
+ include Aclatraz::Helpers
14
+
15
+ ROLE_KEY = "role"
16
+ SUSPECT_KEY = "suspect"
17
+
18
+ def initialize(collection, mongo)
19
+ @backend = mongo
20
+ @collection = collection
21
+ end
22
+
23
+ def set(role, suspect, object=nil)
24
+ @backend[@collection].insert(make_doc(role, suspect, object))
25
+ end
26
+
27
+ def roles(suspect=nil)
28
+ if suspect
29
+ @backend[@collection].find(SUSPECT_KEY => suspect_id(suspect)).map {|row| row[ROLE_KEY] }
30
+ else
31
+ @backend[@collection].find.map {|row| row[ROLE_KEY] }
32
+ end.compact.uniq
33
+ end
34
+
35
+ def check(role, suspect, object=nil)
36
+ @backend[@collection].find(make_doc(role, suspect, object)).map.empty? == false or begin
37
+ object && !object.is_a?(Class) ? check(role, suspect, object.class) : false
38
+ end
39
+ end
40
+
41
+ def delete(role, suspect, object=nil)
42
+ @backend[@collection].remove(make_doc(role, suspect, object))
43
+ end
44
+
45
+ def clear
46
+ @backend[@collection].remove
47
+ end
48
+
49
+ private
50
+
51
+ def make_doc(role, suspect, object)
52
+ { SUSPECT_KEY => suspect_id(suspect), ROLE_KEY => pack(role.to_s, object) }
53
+ end
54
+ end # Mongo
55
+ end # Store
56
+ end # Aclatraz
@@ -1,3 +1,5 @@
1
+ require 'redis'
2
+
1
3
  module Aclatraz
2
4
  module Store
3
5
  # List of global roles are stored in ROLES set. Each suspect has its
@@ -15,9 +17,6 @@ module Aclatraz
15
17
  SUSPECT_ROLES_KEY = "suspect.%s.roles"
16
18
 
17
19
  def initialize(*args)
18
- require 'redis' rescue raise LoadError, \
19
- "You must install the `redis` gem to use Redis store"
20
-
21
20
  @backend = if args.first.respond_to?(:sadd)
22
21
  args.first
23
22
  else
@@ -1,3 +1,5 @@
1
+ require 'riak'
2
+
1
3
  module Aclatraz
2
4
  module Store
3
5
  # The most optimal way to store roles in riak database is pack everything
@@ -10,9 +12,6 @@ module Aclatraz
10
12
  include Aclatraz::Helpers
11
13
 
12
14
  def initialize(bucket_name, *args)
13
- require "riak" rescue raise LoadError, \
14
- "You must install the `riak-client` gem to use Riak store"
15
-
16
15
  @backend = if args.first.respond_to?(:bucket)
17
16
  args.first.bucket(bucket_name)
18
17
  else
@@ -1,45 +1,49 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Aclatraz ACL" do
3
+ describe "Aclatraz ACL" do
4
+ subject { Aclatraz::ACL }
4
5
  before(:all) { Aclatraz.init(:redis, "redis://localhost:6379/0") }
5
6
 
6
- it "should properly store suspect" do
7
- acl = Aclatraz::ACL.new(:bar) {}
8
- acl.suspect.should == :bar
7
+ it "should properly set suspect" do
8
+ acl = subject.new(:suspect) {}
9
+ acl.suspect.should == :suspect
9
10
  end
10
11
 
11
12
  it "should properly store flat access control lists" do
12
- acl = Aclatraz::ACL.new(:foo) {}
13
- acl.actions[:_].allow :foo
14
- acl.permissions[:foo].should be_true
15
- acl.actions[:_].deny :foo
16
- acl.permissions[:foo].should be_false
17
- acl.actions[:_].allow :foo => :bar
18
- acl.permissions[{:foo=>:bar}].should be_true
13
+ acl = subject.new(:suspect) {}
14
+ acl.actions[:_].allow :admin
15
+ acl.permissions[:admin].should be_true
16
+ acl.actions[:_].deny :admin
17
+ acl.permissions[:admin].should be_false
18
+ acl.actions[:_].allow :owner_of => :book
19
+ acl.permissions[{:owner_of => :book}].should be_true
19
20
  end
20
21
 
21
- it "should allow for define seperated lists which are inherit from main block" do
22
- acl = Aclatraz::ACL.new(:foo) do
23
- allow :foo
24
- on(:spam) { allow :spam }
25
- on(:eggs) { allow :eggs }
26
- on(:spam) { allow :boo }
22
+ it "should allow for grouping permissions in namespaces, which are inherit from main block" do
23
+ acl = subject.new(:suspect) do
24
+ allow :admin
25
+ on(:library) { allow :librarian }
26
+ on(:kitchen) { allow :cooker }
27
+ on(:kennel) { allow :dog }
27
28
  end
28
29
 
29
- acl.permissions[:foo].should be_true
30
- acl.permissions[:spam].should_not be_true
31
- acl.permissions[:eggs].should_not be_true
32
- acl.permissions[:boo].should_not be_true
33
- acl[:spam].permissions[:foo].should be_nil
34
- acl[:spam].permissions[:spam].should be_true
35
- acl[:spam].permissions[:eggs].should be_nil
36
- acl[:spam].permissions[:boo].should be_true
37
- acl[:eggs].permissions[:foo].should be_nil
38
- acl[:eggs].permissions[:eggs].should be_true
39
- acl[:eggs].permissions[:spam].should be_nil
30
+ acl.permissions[:admin].should be_true
31
+ acl.permissions[:librarian].should be_false
32
+ acl.permissions[:cooker].should be_false
33
+ acl.permissions[:dog].should be_false
34
+
35
+ acl[:library].permissions[:librarian].should be_true
36
+ acl[:library].permissions[:cooker].should be_false
37
+ acl[:library].permissions[:dog].should be_false
38
+ acl[:library].permissions[:admin].should be_false
39
+
40
+ acl[:kitchen].permissions[:cooker].should be_true
41
+ acl[:kitchen].permissions[:librarian].should be_false
42
+ acl[:kitchen].permissions[:dog].should be_false
43
+ acl[:kitchen].permissions[:admin].should be_false
40
44
  end
41
45
 
42
- it "should raise ArgumentError when no block given" do
43
- lambda { Aclatraz::ACL.new }.should raise_error(ArgumentError)
46
+ it "should raise error when no block given" do
47
+ lambda { subject.new }.should raise_error(ArgumentError)
44
48
  end
45
49
  end