consul 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of consul might be problematic. Click here for more details.

data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ doc
2
+ pkg
3
+ *.gem
4
+ .idea
5
+ spec/app_root/log/*
6
+
7
+
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'rails', '<3.0.0'
4
+ gem 'consul', :path => '.'
5
+ gem 'rspec', '=1.3.1'
6
+ gem 'rspec-rails', '=1.3.3'
7
+ gem 'jeweler'
8
+ gem 'ruby-debug'
9
+
data/Gemfile.lock ADDED
@@ -0,0 +1,58 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ consul (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ actionmailer (2.3.10)
10
+ actionpack (= 2.3.10)
11
+ actionpack (2.3.10)
12
+ activesupport (= 2.3.10)
13
+ rack (~> 1.1.0)
14
+ activerecord (2.3.10)
15
+ activesupport (= 2.3.10)
16
+ activeresource (2.3.10)
17
+ activesupport (= 2.3.10)
18
+ activesupport (2.3.10)
19
+ columnize (0.3.2)
20
+ gemcutter (0.6.1)
21
+ git (1.2.5)
22
+ jeweler (1.4.0)
23
+ gemcutter (>= 0.1.0)
24
+ git (>= 1.2.5)
25
+ rubyforge (>= 2.0.0)
26
+ json_pure (1.4.6)
27
+ linecache (0.43)
28
+ rack (1.1.0)
29
+ rails (2.3.10)
30
+ actionmailer (= 2.3.10)
31
+ actionpack (= 2.3.10)
32
+ activerecord (= 2.3.10)
33
+ activeresource (= 2.3.10)
34
+ activesupport (= 2.3.10)
35
+ rake (>= 0.8.3)
36
+ rake (0.8.7)
37
+ rspec (1.3.1)
38
+ rspec-rails (1.3.3)
39
+ rack (>= 1.0.0)
40
+ rspec (= 1.3.1)
41
+ ruby-debug (0.10.4)
42
+ columnize (>= 0.1)
43
+ ruby-debug-base (~> 0.10.4.0)
44
+ ruby-debug-base (0.10.4)
45
+ linecache (>= 0.3)
46
+ rubyforge (2.0.4)
47
+ json_pure (>= 1.1.7)
48
+
49
+ PLATFORMS
50
+ ruby
51
+
52
+ DEPENDENCIES
53
+ consul!
54
+ jeweler
55
+ rails (< 3.0.0)
56
+ rspec (= 1.3.1)
57
+ rspec-rails (= 1.3.3)
58
+ ruby-debug
data/README.rdoc ADDED
@@ -0,0 +1,213 @@
1
+ = Consul - A scope-based authorization solution
2
+
3
+ Consul is a authorization solution for Ruby on Rails that uses scopes to control what a user can see or edit.
4
+
5
+
6
+ == Status of this project
7
+
8
+ Consul is a new kind of authorization solution for Rails applications that are mainly driven by scopes.
9
+ While Consul has been used in production code, we are still figuring out whether or not it is a good idea.
10
+ Also documentation is still sparse.
11
+
12
+ If you are looking for something less adventurous with a stable API and great documentation, checkout out our
13
+ other authorization solution, {Aegis}[https://github.com/makandra/aegis].
14
+
15
+
16
+ == Describing a power for your application
17
+
18
+ You describe access to your application by putting a <tt>Power</tt> model into <tt>app/models/power.rb</tt>:
19
+
20
+ class Power
21
+ include Consul::Power
22
+
23
+ def initialize(user)
24
+ @user = user
25
+ end
26
+
27
+ power :notes do
28
+ Note.by_author(@user)
29
+ end
30
+
31
+ power :users do
32
+ User if @user.admin?
33
+ end
34
+
35
+ power :dashboard do
36
+ true # not a scope, but a boolean power. This is useful to control access to stuff that doesn't live in the database.
37
+ end
38
+
39
+ end
40
+
41
+
42
+ == Querying a power
43
+
44
+ Common things you might want from a power:
45
+
46
+ 1. Get its scope
47
+ 2. Ask whether it is there
48
+ 3. Raise an error unless it its there
49
+ 4. Ask whether a given record is included in its scope
50
+ 5. Raise an error unless a given record is included in its scope
51
+
52
+ Here is how to do all of that:
53
+
54
+ power = Power.new(user)
55
+ power.notes # => returns an ActiveRecord::Scope
56
+ power.notes? # => returns true if Power#notes returns a scope
57
+ power.notes! # => raises Consul::Powerless unless Power#notes returns a scope
58
+ power.note?(Note.last) # => returns whether the given Note is in the Power#notes scope. Caches the result for subsequent queries.
59
+ power.note!(Note.last) # => raises Consul::Powerless unless the given Note is in the Power#notes scope
60
+
61
+ You can also write power checks like this:
62
+
63
+ power.include?(:notes)
64
+ power.include!(:notes)
65
+ power.include?(:note, Note.last)
66
+ power.include!(:note, Note.last)
67
+
68
+
69
+ == Boolean powers
70
+
71
+ Boolean powers are useful to control access to stuff that doesn't live in the database:
72
+
73
+ class Power
74
+ ...
75
+
76
+ power :dashboard do
77
+ true
78
+ end
79
+
80
+ end
81
+
82
+ You can query it like the other powers:
83
+
84
+ power.dashboard? # => true
85
+ power.dashboard! # => raises Consul::Powerless unless Power#dashboard? returns true
86
+
87
+
88
+ == Role-based permissions
89
+
90
+ Consul has no built-in support for role-based permissions, but you can easily implement it yourself. Let's say your <tt>User</tt> model has a string column <tt>role</tt> which can be <tt>"author"</tt> or "<tt>"admin"</tt>:
91
+
92
+ class Power
93
+ include Consul::Power
94
+
95
+ def initialize(user)
96
+ @user = user
97
+ end
98
+
99
+ power :notes do
100
+ case role
101
+ when :admin then Note
102
+ when :author then Note.by_author
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def role
109
+ @user.role.to_sym
110
+ end
111
+
112
+ end
113
+
114
+
115
+ == Controller integration
116
+
117
+ It is convenient to expose a helper method <tt>current_power</tt> for your controllers and views:
118
+
119
+ class ApplicationController < ActionController::Base
120
+
121
+ private
122
+
123
+ def current_power
124
+ @current_power ||= Power.new(current_user)
125
+ end
126
+
127
+ helper_method :current_power
128
+
129
+ end
130
+
131
+ You can now use power scopes to control access:
132
+
133
+ class NotesController < ApplicationController
134
+
135
+ def show
136
+ @note = current_power.notes.find(params[:id])
137
+ end
138
+
139
+ end
140
+
141
+ Get convenient controller macros by including <tt>Consul::Controller</tt> in your application:
142
+
143
+ class ApplicationController < ActionController::Base
144
+ include Consul::Controller
145
+ end
146
+
147
+ To make sure a power is given before every action in a controller:
148
+
149
+ class NotesController < ApplicationController
150
+ power :notes
151
+ end
152
+
153
+ You can use <tt>:except</tt> and <tt>:only</tt> options like in before filters.
154
+
155
+ You can also map different powers to different actions:
156
+
157
+ class NotesController < ApplicationController
158
+ power :notes, :map => { [:edit, :update, :destroy] => :changable_notes }
159
+ end
160
+
161
+ It is often convenient to map a power scope to a private controller method:
162
+
163
+ class NotesController < ApplicationController
164
+
165
+ power :notes, :as => end_of_association_chain
166
+
167
+ def show
168
+ @note = end_of_association_chain.find(params[:id])
169
+ end
170
+
171
+ end
172
+
173
+ This is especially useful when you are using a RESTful controller library like {resource_controller}[https://github.com/jamesgolick/resource_controller]. The mapped method is aware of the <tt>:map</tt> option.
174
+
175
+ You can force yourself to use a <tt>power</tt> check in every controller. This will raise <tt>Consul::UncheckedPower</tt> if you ever forget it:
176
+
177
+ class ApplicationController < ActionController::Base
178
+ include Consul::Controller
179
+ require_power_check
180
+ end
181
+
182
+ Should you for some obscure reason want to forego the power check:
183
+
184
+ class ApiController < ApplicationController
185
+ skip_power_check
186
+ end
187
+
188
+ == Installation
189
+
190
+ Add the following to your <tt>Gemfile</tt>:
191
+ gem 'consul'
192
+
193
+ Now run
194
+ bundle install
195
+
196
+
197
+ == Rails 3 compatibility
198
+
199
+ We cannot guarantee Rails 3 compatibility at this point.
200
+
201
+
202
+ == Development
203
+
204
+ A Rails 2 test application lives in <tt>spec/app_root</tt>. You can run specs from the project root by saying:
205
+
206
+ bundle exec rake spec
207
+
208
+
209
+ == Credits
210
+
211
+ Henning Koch
212
+
213
+ {makandra.com}[http://makandra.com/]
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+
5
+ task :default => :spec
6
+
7
+ Spec::Rake::SpecTask.new() do |t|
8
+ t.spec_opts = ['--options', "\"spec/spec.opts\""]
9
+ t.spec_files = FileList['spec/**/*_spec.rb']
10
+ end
11
+
12
+ desc 'Generate documentation for the consul gem'
13
+ Rake::RDocTask.new(:rdoc) do |rdoc|
14
+ rdoc.rdoc_dir = 'rdoc'
15
+ rdoc.title = 'consul'
16
+ rdoc.options << '--line-numbers' << '--inline-source'
17
+ rdoc.rdoc_files.include('README')
18
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
+ end
20
+
21
+ begin
22
+ require 'jeweler'
23
+ Jeweler::Tasks.new do |gemspec|
24
+ gemspec.name = "consul"
25
+ gemspec.summary = "Scope-based authorization solution for Rails"
26
+ gemspec.email = "henning.koch@makandra.de"
27
+ gemspec.homepage = "http://github.com/makandra/consul"
28
+ gemspec.description = "Consul is a scope-based authorization solution for Ruby on Rails."
29
+ gemspec.authors = ["Henning Koch"]
30
+ end
31
+ rescue LoadError
32
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
33
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/consul.gemspec ADDED
@@ -0,0 +1,93 @@
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 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{consul}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Henning Koch"]
12
+ s.date = %q{2011-04-12}
13
+ s.description = %q{Consul is a scope-based authorization solution for Ruby on Rails.}
14
+ s.email = %q{henning.koch@makandra.de}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "README.rdoc",
23
+ "Rakefile",
24
+ "VERSION",
25
+ "consul.gemspec",
26
+ "lib/consul.rb",
27
+ "lib/consul/controller.rb",
28
+ "lib/consul/errors.rb",
29
+ "lib/consul/power.rb",
30
+ "lib/consul/spec/matchers.rb",
31
+ "spec/app_root/app/controllers/application_controller.rb",
32
+ "spec/app_root/app/models/client.rb",
33
+ "spec/app_root/app/models/note.rb",
34
+ "spec/app_root/app/models/power.rb",
35
+ "spec/app_root/app/models/user.rb",
36
+ "spec/app_root/config/boot.rb",
37
+ "spec/app_root/config/database.yml",
38
+ "spec/app_root/config/environment.rb",
39
+ "spec/app_root/config/environments/in_memory.rb",
40
+ "spec/app_root/config/environments/mysql.rb",
41
+ "spec/app_root/config/environments/postgresql.rb",
42
+ "spec/app_root/config/environments/sqlite.rb",
43
+ "spec/app_root/config/environments/sqlite3.rb",
44
+ "spec/app_root/config/routes.rb",
45
+ "spec/app_root/db/migrate/001_create_users.rb",
46
+ "spec/app_root/db/migrate/002_create_clients.rb",
47
+ "spec/app_root/db/migrate/003_create_notes.rb",
48
+ "spec/app_root/lib/console_with_fixtures.rb",
49
+ "spec/app_root/log/.gitignore",
50
+ "spec/app_root/script/console",
51
+ "spec/consul/power_spec.rb",
52
+ "spec/rcov.opts",
53
+ "spec/spec.opts",
54
+ "spec/spec_helper.rb"
55
+ ]
56
+ s.homepage = %q{http://github.com/makandra/consul}
57
+ s.rdoc_options = ["--charset=UTF-8"]
58
+ s.require_paths = ["lib"]
59
+ s.rubygems_version = %q{1.3.7}
60
+ s.summary = %q{Scope-based authorization solution for Rails}
61
+ s.test_files = [
62
+ "spec/app_root/db/migrate/001_create_users.rb",
63
+ "spec/app_root/db/migrate/002_create_clients.rb",
64
+ "spec/app_root/db/migrate/003_create_notes.rb",
65
+ "spec/app_root/config/boot.rb",
66
+ "spec/app_root/config/environment.rb",
67
+ "spec/app_root/config/routes.rb",
68
+ "spec/app_root/config/environments/in_memory.rb",
69
+ "spec/app_root/config/environments/mysql.rb",
70
+ "spec/app_root/config/environments/postgresql.rb",
71
+ "spec/app_root/config/environments/sqlite.rb",
72
+ "spec/app_root/config/environments/sqlite3.rb",
73
+ "spec/app_root/lib/console_with_fixtures.rb",
74
+ "spec/app_root/app/controllers/application_controller.rb",
75
+ "spec/app_root/app/models/client.rb",
76
+ "spec/app_root/app/models/note.rb",
77
+ "spec/app_root/app/models/power.rb",
78
+ "spec/app_root/app/models/user.rb",
79
+ "spec/consul/power_spec.rb",
80
+ "spec/spec_helper.rb"
81
+ ]
82
+
83
+ if s.respond_to? :specification_version then
84
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
85
+ s.specification_version = 3
86
+
87
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
88
+ else
89
+ end
90
+ else
91
+ end
92
+ end
93
+
data/lib/consul.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'consul/power'
2
+ require 'consul/errors'
3
+ require 'consul/controller'
@@ -0,0 +1,79 @@
1
+ module Consul
2
+ module Controller
3
+
4
+ def self.included(base)
5
+ base.send :include, InstanceMethods
6
+ base.send :extend, ClassMethods
7
+ end
8
+
9
+ module ClassMethods
10
+
11
+ private
12
+
13
+ def require_power_check(options = {})
14
+ before_filter :unchecked_power, options
15
+ end
16
+
17
+ def skip_power_check(options = {})
18
+ skip_before_filter :unchecked_power, options
19
+ end
20
+
21
+ def power(*args)
22
+
23
+ args_copy = args.dup
24
+ options = args_copy.extract_options!
25
+ default_power = args_copy.shift # might be nil
26
+
27
+ filter_options = options.slice(:except, :only)
28
+ skip_power_check filter_options
29
+
30
+ power_method = options[:power] || :current_power
31
+ actions_map = (options[:map] || {})
32
+
33
+ direct_access_method = options[:as]
34
+
35
+ # Store arguments for testing
36
+ @consul_power_args = args
37
+
38
+ before_filter :check_power, filter_options
39
+
40
+ private
41
+
42
+ define_method :check_power do
43
+ send(power_method).include!(power_for_action)
44
+ end
45
+
46
+ define_method direct_access_method do
47
+ send(power_method).send(power_for_action)
48
+ end if direct_access_method
49
+
50
+ define_method :power_for_action do
51
+ key = actions_map.keys.detect do |actions|
52
+ Array(actions).collect(&:to_s).include?(action_name)
53
+ end
54
+ if key
55
+ actions_map[key]
56
+ elsif default_power
57
+ default_power
58
+ else
59
+ raise Consul::UnmappedAction, "Could not map the action ##{action_name} to a power"
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ module InstanceMethods
68
+
69
+ private
70
+
71
+ def unchecked_permissions
72
+ raise Consul::UncheckedPower, "This controller does not check against a power"
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,5 @@
1
+ module Consul
2
+ class Powerless < StandardError; end
3
+ class UncheckedPower < StandardError; end
4
+ class UnmappedAction < StandardError; end
5
+ end
@@ -0,0 +1,52 @@
1
+ module Consul
2
+ module Power
3
+
4
+ def self.included(base)
5
+ base.extend ActiveSupport::Memoizable
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ def include?(name, *args)
10
+ args = args.dup
11
+ record = args.shift
12
+ power_value = send(name)
13
+ if record.nil? || boolean_or_nil?(power_value)
14
+ !!power_value
15
+ else
16
+ power_ids_name = self.class.power_ids_name(name)
17
+ send(power_ids_name, *args).include?(record.id)
18
+ end
19
+ end
20
+
21
+ def include!(*args)
22
+ include?(*args) or raise Consul::Powerless.new("No power to #{args.inspect}")
23
+ end
24
+
25
+ private
26
+
27
+ def boolean_or_nil?(value)
28
+ [TrueClass, FalseClass, NilClass].include?(value.class)
29
+ end
30
+
31
+ module ClassMethods
32
+
33
+ def power(name, &block)
34
+ define_method(name, &block)
35
+ define_method("#{name.to_s}?") { |*args| include?(name, *args) }
36
+ define_method("#{name.to_s}!") { |*args| include!(name, *args) }
37
+ define_method("#{name.to_s.singularize}?") { |*args| include?(name, *args) }
38
+ define_method("#{name.to_s.singularize}!") { |*args| include!(name, *args) }
39
+ ids_method = power_ids_name(name)
40
+ define_method(ids_method) { |*args| send(name, *args).scoped(:select => 'id').collect(&:id) }
41
+ memoize ids_method
42
+ name
43
+ end
44
+
45
+ def power_ids_name(name)
46
+ "#{name.to_s.singularize}_ids"
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,40 @@
1
+ module Aegis
2
+ module Spec
3
+ module Matchers
4
+
5
+ class CheckPower
6
+
7
+ def initialize(*args)
8
+ @expected_args = args
9
+ end
10
+
11
+ def matches?(controller)
12
+ @controller_class = controller.class
13
+ @actual_args = @controller_class.instance_variable_get('@consul_power_args')
14
+ @actual_args == @expected_args
15
+ end
16
+
17
+ def failure_message
18
+ "expected #{@controller_class} to check against power #{@expected_args.inspect} but it checked against #{@actual_args.inspect}"
19
+ end
20
+
21
+ def negative_failure_message
22
+ "expected #{@controller_class} to not check against power #{@expected_args.inspect}"
23
+ end
24
+
25
+ def description
26
+ description = "check against power #{@expected_args.inspect}"
27
+ description
28
+ end
29
+
30
+ end
31
+
32
+ def check_power(*args)
33
+ CheckPower.new(*args)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
39
+
40
+ ActiveSupport::TestCase.send :include, Aegis::Spec::Matchers
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+
3
+ end
@@ -0,0 +1,13 @@
1
+ class Client < ActiveRecord::Base
2
+
3
+ has_many :notes
4
+
5
+ named_scope :active, :conditions => ["deleted = ? OR deleted IS NULL", false]
6
+
7
+ def foo
8
+ with_scope do
9
+
10
+ end
11
+ end
12
+
13
+ end
@@ -0,0 +1,7 @@
1
+ class Note < ActiveRecord::Base
2
+
3
+ belongs_to :client
4
+
5
+ validates_presence_of :client_id
6
+
7
+ end
@@ -0,0 +1,28 @@
1
+ class Power
2
+ include Consul::Power
3
+
4
+ def initialize(user)
5
+ @user = user
6
+ end
7
+
8
+ power :clients do
9
+ Client.active
10
+ end
11
+
12
+ power :client_notes do |client|
13
+ client.notes
14
+ end
15
+
16
+ power :admin do
17
+ false
18
+ end
19
+
20
+ power :moderator do
21
+ nil
22
+ end
23
+
24
+ power :dashboard do
25
+ true
26
+ end
27
+
28
+ end
@@ -0,0 +1,8 @@
1
+ class User < ActiveRecord::Base
2
+
3
+ def power
4
+ @power ||= Power.new(self)
5
+ end
6
+
7
+ end
8
+
@@ -0,0 +1,114 @@
1
+ # Allow customization of the rails framework path
2
+ RAILS_FRAMEWORK_ROOT = (ENV['RAILS_FRAMEWORK_ROOT'] || "#{File.dirname(__FILE__)}/../../../../../../vendor/rails") unless defined?(RAILS_FRAMEWORK_ROOT)
3
+
4
+ # Don't change this file!
5
+ # Configure your app in config/environment.rb and config/environments/*.rb
6
+
7
+ RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
8
+
9
+ module Rails
10
+ class << self
11
+ def boot!
12
+ unless booted?
13
+ preinitialize
14
+ pick_boot.run
15
+ end
16
+ end
17
+
18
+ def booted?
19
+ defined? Rails::Initializer
20
+ end
21
+
22
+ def pick_boot
23
+ (vendor_rails? ? VendorBoot : GemBoot).new
24
+ end
25
+
26
+ def vendor_rails?
27
+ File.exist?(RAILS_FRAMEWORK_ROOT)
28
+ end
29
+
30
+ def preinitialize
31
+ load(preinitializer_path) if File.exist?(preinitializer_path)
32
+ end
33
+
34
+ def preinitializer_path
35
+ "#{RAILS_ROOT}/config/preinitializer.rb"
36
+ end
37
+ end
38
+
39
+ class Boot
40
+ def run
41
+ load_initializer
42
+ Rails::Initializer.run(:set_load_path)
43
+ end
44
+ end
45
+
46
+ class VendorBoot < Boot
47
+ def load_initializer
48
+ require "#{RAILS_FRAMEWORK_ROOT}/railties/lib/initializer"
49
+ Rails::Initializer.run(:install_gem_spec_stubs)
50
+ end
51
+ end
52
+
53
+ class GemBoot < Boot
54
+ def load_initializer
55
+ self.class.load_rubygems
56
+ load_rails_gem
57
+ require 'initializer'
58
+ end
59
+
60
+ def load_rails_gem
61
+ if version = self.class.gem_version
62
+ gem 'rails', version
63
+ else
64
+ gem 'rails'
65
+ end
66
+ rescue Gem::LoadError => load_error
67
+ $stderr.puts %(Missing the Rails #{version} gem. Please `gem install -v=#{version} rails`, update your RAILS_GEM_VERSION setting in config/environment.rb for the Rails version you do have installed, or comment out RAILS_GEM_VERSION to use the latest version installed.)
68
+ exit 1
69
+ end
70
+
71
+ class << self
72
+ def rubygems_version
73
+ Gem::RubyGemsVersion rescue nil
74
+ end
75
+
76
+ def gem_version
77
+ if defined? RAILS_GEM_VERSION
78
+ RAILS_GEM_VERSION
79
+ elsif ENV.include?('RAILS_GEM_VERSION')
80
+ ENV['RAILS_GEM_VERSION']
81
+ else
82
+ parse_gem_version(read_environment_rb)
83
+ end
84
+ end
85
+
86
+ def load_rubygems
87
+ require 'rubygems'
88
+ min_version = '1.1.1'
89
+ unless rubygems_version >= min_version
90
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
91
+ exit 1
92
+ end
93
+
94
+ rescue LoadError
95
+ $stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
96
+ exit 1
97
+ end
98
+
99
+ def parse_gem_version(text)
100
+ $1 if text =~ /^[^#]*RAILS_GEM_VERSION\s*=\s*["']([!~<>=]*\s*[\d.]+)["']/
101
+ end
102
+
103
+ private
104
+ def read_environment_rb
105
+ environment_rb = "#{RAILS_ROOT}/config/environment.rb"
106
+ environment_rb = "#{HELPER_RAILS_ROOT}/config/environment.rb" unless File.exists?(environment_rb)
107
+ File.read(environment_rb)
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ # All that for this:
114
+ Rails.boot!
@@ -0,0 +1,21 @@
1
+ in_memory:
2
+ adapter: sqlite3
3
+ database: ":memory:"
4
+ verbosity: quiet
5
+ sqlite:
6
+ adapter: sqlite
7
+ dbfile: plugin_test.sqlite.db
8
+ sqlite3:
9
+ adapter: sqlite3
10
+ dbfile: plugin_test.sqlite3.db
11
+ postgresql:
12
+ adapter: postgresql
13
+ username: postgres
14
+ password: postgres
15
+ database: plugin_test
16
+ mysql:
17
+ adapter: mysql
18
+ host: localhost
19
+ username: root
20
+ password:
21
+ database: plugin_test
@@ -0,0 +1,14 @@
1
+ require File.join(File.dirname(__FILE__), 'boot')
2
+
3
+ Rails::Initializer.run do |config|
4
+ config.cache_classes = false
5
+ config.whiny_nils = true
6
+ config.action_controller.session = { :key => "_myapp_session", :secret => "gwirofjweroijger8924rt2zfwehfuiwehb1378rifowenfoqwphf23" }
7
+ config.plugin_locators.unshift(
8
+ Class.new(Rails::Plugin::Locator) do
9
+ def plugins
10
+ [Rails::Plugin.new(File.expand_path('.'))]
11
+ end
12
+ end
13
+ ) unless defined?(PluginTestHelper::PluginLocator)
14
+ end
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,7 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+
3
+ map.resources :properties do |properties|
4
+ properties.resources :reviews
5
+ end
6
+
7
+ end
@@ -0,0 +1,11 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :users
5
+ end
6
+
7
+ def self.down
8
+ drop_table :users
9
+ end
10
+
11
+ end
@@ -0,0 +1,13 @@
1
+ class CreateClients < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :clients do |t|
5
+ t.boolean :deleted
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :clients
11
+ end
12
+
13
+ end
@@ -0,0 +1,13 @@
1
+ class CreateNotes < ActiveRecord::Migration
2
+
3
+ def self.up
4
+ create_table :notes do |t|
5
+ t.integer :client_id
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :notes
11
+ end
12
+
13
+ end
@@ -0,0 +1,4 @@
1
+ # Loads fixtures into the database when running the test app via the console
2
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(Rails.root, '../fixtures/*.{yml,csv}'))).each do |fixture_file|
3
+ Fixtures.create_fixtures(File.join(Rails.root, '../fixtures'), File.basename(fixture_file, '.*'))
4
+ end
@@ -0,0 +1,7 @@
1
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
2
+ libs = " -r irb/completion"
3
+ libs << " -r test/test_helper"
4
+ libs << " -r console_app"
5
+ libs << " -r console_with_helpers"
6
+ libs << " -r console_with_fixtures"
7
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe Consul::Power do
4
+
5
+ before :each do
6
+ @user = User.create!
7
+ @deleted_client = Client.create!(:deleted => true)
8
+ @client1 = Client.create!
9
+ @client1_note1 = @client1.notes.create!
10
+ @client1_note2 = @client1.notes.create!
11
+ @client2 = Client.create!
12
+ @client2_note1 = @client2.notes.create!
13
+ @client2_note2 = @client2.notes.create!
14
+ end
15
+
16
+ describe 'example scenario' do
17
+
18
+ it 'should work as expected' do
19
+ Client.active.should == [@client1, @client2]
20
+ @client1.notes.should == [@client1_note1, @client1_note2]
21
+ end
22
+
23
+ end
24
+
25
+ describe 'scope methods' do
26
+
27
+ it 'should return the registered scope' do
28
+ @user.power.clients.all.should == [@client1, @client2]
29
+ end
30
+
31
+ it 'should allow to register scopes with arguments' do
32
+ @user.power.client_notes(@client1).should == [@client1_note1, @client1_note2]
33
+ end
34
+
35
+ end
36
+
37
+ describe 'scope_ids methods' do
38
+
39
+ it 'should return record ids that match the registered scope' do
40
+ @user.power.client_ids.should == [@client1.id, @client2.id]
41
+ end
42
+
43
+ it 'should cache scope ids' do
44
+ @user.power.should_receive(:clients).once.and_return(double('scope').as_null_object)
45
+ 2.times { @user.power.client_ids }
46
+ end
47
+
48
+ end
49
+
50
+ describe 'include?' do
51
+
52
+ it 'should return true if a given record belongs to a scope' do
53
+ @user.power.client?(@client1).should be_true
54
+ end
55
+
56
+ it 'should return false if a given record does not belong to a scope' do
57
+ @user.power.client?(@deleted_client).should be_false
58
+ end
59
+
60
+ it 'should only trigger a single query for multiple checks on the same scope' do
61
+ ActiveRecord::Base.connection.should_receive(:select_all).once.and_return([]) #.and_return(double('connection').as_null_object)
62
+ @user.power.client?(@client1)
63
+ @user.power.client?(@deleted_client)
64
+ end
65
+
66
+ it 'should return true when the queried power returns a scope (which might or might not match records)' do
67
+ @user.power.clients?.should be_true
68
+ end
69
+
70
+ it 'should return true when the queried power is not a scope, but returns true' do
71
+ @user.power.dashboard?.should be_true
72
+ end
73
+
74
+ it 'should return false when the queried power is not a scope, but returns false' do
75
+ @user.power.admin?.should be_false
76
+ end
77
+
78
+ it 'should return false when the queried power is not a scope, but returns nil' do
79
+ @user.power.moderator?.should be_false
80
+ end
81
+
82
+ end
83
+
84
+ describe 'include!' do
85
+
86
+ it 'should raise Consul::Powerless when the given record belongs to a scope' do
87
+ expect { @user.power.client!(@deleted_client) }.to raise_error(Consul::Powerless)
88
+ end
89
+
90
+ it 'should not raise Consul::Powerless when the given record does not belong to a scope' do
91
+ expect { @user.power.client!(@client1) }.to_not raise_error
92
+ end
93
+
94
+ it 'should not raise Consul::Powerless when the queried power returns a scope (which might or might not match records)' do
95
+ expect { @user.power.clients! }.to_not raise_error
96
+ end
97
+
98
+ it 'should not raise Consul::Powerless when the queried power is not a scope, but returns true' do
99
+ expect { @user.power.dashboard! }.to_not raise_error
100
+ end
101
+
102
+ it 'should raise Consul::Powerless when the queried power is not a scope, but returns false' do
103
+ expect { @user.power.admin! }.to raise_error
104
+ end
105
+
106
+ it 'should raise Consul::Powerless when the queried power is not a scope, but returns nil' do
107
+ expect { @user.power.moderator! }.to raise_error
108
+ end
109
+
110
+ end
111
+
112
+ end
data/spec/rcov.opts ADDED
@@ -0,0 +1,2 @@
1
+ --exclude "spec/*,gems/*"
2
+ --rails
data/spec/spec.opts ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,20 @@
1
+ $: << File.join(File.dirname(__FILE__), "/../lib" )
2
+
3
+ # Set the default environment to sqlite3's in_memory database
4
+ ENV['RAILS_ENV'] ||= 'in_memory'
5
+
6
+ # Load the Rails environment and testing framework
7
+ require "#{File.dirname(__FILE__)}/app_root/config/environment"
8
+ require "#{File.dirname(__FILE__)}/../lib/consul"
9
+ require 'spec/rails'
10
+
11
+ # Undo changes to RAILS_ENV
12
+ silence_warnings {RAILS_ENV = ENV['RAILS_ENV']}
13
+
14
+ # Run the migrations
15
+ ActiveRecord::Migrator.migrate("#{Rails.root}/db/migrate")
16
+
17
+ Spec::Runner.configure do |config|
18
+ config.use_transactional_fixtures = true
19
+ config.use_instantiated_fixtures = false
20
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: consul
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Henning Koch
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-04-12 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Consul is a scope-based authorization solution for Ruby on Rails.
23
+ email: henning.koch@makandra.de
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.rdoc
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - Gemfile.lock
34
+ - README.rdoc
35
+ - Rakefile
36
+ - VERSION
37
+ - consul.gemspec
38
+ - lib/consul.rb
39
+ - lib/consul/controller.rb
40
+ - lib/consul/errors.rb
41
+ - lib/consul/power.rb
42
+ - lib/consul/spec/matchers.rb
43
+ - spec/app_root/app/controllers/application_controller.rb
44
+ - spec/app_root/app/models/client.rb
45
+ - spec/app_root/app/models/note.rb
46
+ - spec/app_root/app/models/power.rb
47
+ - spec/app_root/app/models/user.rb
48
+ - spec/app_root/config/boot.rb
49
+ - spec/app_root/config/database.yml
50
+ - spec/app_root/config/environment.rb
51
+ - spec/app_root/config/environments/in_memory.rb
52
+ - spec/app_root/config/environments/mysql.rb
53
+ - spec/app_root/config/environments/postgresql.rb
54
+ - spec/app_root/config/environments/sqlite.rb
55
+ - spec/app_root/config/environments/sqlite3.rb
56
+ - spec/app_root/config/routes.rb
57
+ - spec/app_root/db/migrate/001_create_users.rb
58
+ - spec/app_root/db/migrate/002_create_clients.rb
59
+ - spec/app_root/db/migrate/003_create_notes.rb
60
+ - spec/app_root/lib/console_with_fixtures.rb
61
+ - spec/app_root/log/.gitignore
62
+ - spec/app_root/script/console
63
+ - spec/consul/power_spec.rb
64
+ - spec/rcov.opts
65
+ - spec/spec.opts
66
+ - spec/spec_helper.rb
67
+ has_rdoc: true
68
+ homepage: http://github.com/makandra/consul
69
+ licenses: []
70
+
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --charset=UTF-8
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.3.7
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: Scope-based authorization solution for Rails
101
+ test_files:
102
+ - spec/app_root/db/migrate/001_create_users.rb
103
+ - spec/app_root/db/migrate/002_create_clients.rb
104
+ - spec/app_root/db/migrate/003_create_notes.rb
105
+ - spec/app_root/config/boot.rb
106
+ - spec/app_root/config/environment.rb
107
+ - spec/app_root/config/routes.rb
108
+ - spec/app_root/config/environments/in_memory.rb
109
+ - spec/app_root/config/environments/mysql.rb
110
+ - spec/app_root/config/environments/postgresql.rb
111
+ - spec/app_root/config/environments/sqlite.rb
112
+ - spec/app_root/config/environments/sqlite3.rb
113
+ - spec/app_root/lib/console_with_fixtures.rb
114
+ - spec/app_root/app/controllers/application_controller.rb
115
+ - spec/app_root/app/models/client.rb
116
+ - spec/app_root/app/models/note.rb
117
+ - spec/app_root/app/models/power.rb
118
+ - spec/app_root/app/models/user.rb
119
+ - spec/consul/power_spec.rb
120
+ - spec/spec_helper.rb