aclatraz 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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