joshuaclayton-sentinel 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +10 -7
- data/Rakefile +2 -2
- data/lib/sentinel/controller.rb +18 -15
- data/lib/sentinel/sentinel.rb +11 -7
- data/sentinel.gemspec +2 -2
- data/shoulda_macros/sentinel.rb +30 -28
- data/test/functional/sentinel_controller_test.rb +20 -15
- data/test/partial_rails/controllers/forums_controller.rb +21 -18
- data/test/partial_rails/forum_sentinel.rb +12 -10
- data/test/unit/sentinel_test.rb +8 -8
- metadata +4 -3
data/README.textile
CHANGED
@@ -19,11 +19,13 @@ class ForumSentinel < Sentinel::Sentinel
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def viewable?
|
22
|
-
|
22
|
+
return true if self.forum.public? || current_user_admin?
|
23
|
+
(current_user? && self.forum.members.include?(self.current_user))
|
23
24
|
end
|
24
25
|
|
25
26
|
def editable?
|
26
|
-
|
27
|
+
return true if current_user_admin?
|
28
|
+
(current_user? && self.forum.owner == self.current_user)
|
27
29
|
end
|
28
30
|
|
29
31
|
def destroyable?
|
@@ -198,13 +200,14 @@ class SentinelControllerTest < ActionController::TestCase
|
|
198
200
|
end
|
199
201
|
|
200
202
|
sentinel_context({:creatable? => false}) do
|
201
|
-
should_deny_access_to "get :new",
|
202
|
-
|
203
|
+
should_deny_access_to "get :new",
|
204
|
+
"post :create, :forum => {:name => 'My New Forum'}",
|
205
|
+
:with => :redirect_to_index
|
203
206
|
end
|
204
207
|
|
205
208
|
sentinel_context({:creatable? => true}) do
|
206
|
-
should_grant_access_to "get :new"
|
207
|
-
|
209
|
+
should_grant_access_to "get :new",
|
210
|
+
"post :create, :forum => {:name => 'My New Forum'}"
|
208
211
|
end
|
209
212
|
end
|
210
213
|
</code></pre>
|
@@ -221,4 +224,4 @@ h2. Questions or Comments?
|
|
221
224
|
|
222
225
|
If you like this plugin but have ideas, tweaks, fixes, or issues, shoot me a message on Github or fork/send a pull request. This is alpha software, so I'm pretty open to change.
|
223
226
|
|
224
|
-
Copyright (c) 2009 Joshua Clayton, released under the MIT license
|
227
|
+
Copyright (c) 2009 Joshua Clayton, released under the MIT license
|
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'rake'
|
|
3
3
|
require 'echoe'
|
4
4
|
require 'rake/rdoctask'
|
5
5
|
|
6
|
-
Echoe.new("sentinel", "0.1.
|
6
|
+
Echoe.new("sentinel", "0.1.5") do |p|
|
7
7
|
p.description = "Simple authorization for Rails"
|
8
8
|
p.url = "http://github.com/joshuaclayton/sentinel"
|
9
9
|
p.author = "Joshua Clayton"
|
@@ -19,4 +19,4 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
19
19
|
rdoc.options << '--line-numbers' << '--inline-source'
|
20
20
|
rdoc.rdoc_files.include('README')
|
21
21
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
-
end
|
22
|
+
end
|
data/lib/sentinel/controller.rb
CHANGED
@@ -1,60 +1,63 @@
|
|
1
1
|
module Sentinel
|
2
2
|
module Controller
|
3
|
-
|
3
|
+
|
4
4
|
def self.included(base)
|
5
5
|
base.class_inheritable_writer :sentinel, :instance_writer => false
|
6
6
|
base.class_inheritable_accessor :access_denied, :access_granted
|
7
|
-
|
7
|
+
|
8
8
|
base.send :include, InstanceMethods
|
9
9
|
base.extend ClassMethods
|
10
|
-
|
10
|
+
|
11
11
|
base.class_eval do
|
12
12
|
helper_method :sentinel
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
base.on_denied_with do
|
16
16
|
respond_to do |format|
|
17
|
-
format.html
|
17
|
+
format.html do
|
18
|
+
render :text => "You do not have the proper privileges to access this page.",
|
19
|
+
:status => :unauthorized
|
20
|
+
end
|
18
21
|
format.any { head :unauthorized }
|
19
22
|
end
|
20
23
|
end
|
21
|
-
|
24
|
+
|
22
25
|
base.with_access do
|
23
26
|
true
|
24
27
|
end
|
25
28
|
end
|
26
|
-
|
29
|
+
|
27
30
|
module InstanceMethods
|
28
31
|
def sentinel
|
29
32
|
self.instance_eval(&self.class.sentinel)
|
30
33
|
end
|
31
34
|
end
|
32
|
-
|
35
|
+
|
33
36
|
module ClassMethods
|
34
37
|
def controls_access_with(&block)
|
35
38
|
self.sentinel = block
|
36
39
|
end
|
37
|
-
|
40
|
+
|
38
41
|
def sentinel
|
39
42
|
read_inheritable_attribute(:sentinel)
|
40
43
|
end
|
41
|
-
|
44
|
+
|
42
45
|
def on_denied_with(name = :default, &block)
|
43
46
|
self.access_denied ||= {}
|
44
47
|
self.access_denied[name] = block
|
45
48
|
end
|
46
|
-
|
49
|
+
|
47
50
|
def with_access(&block)
|
48
51
|
self.access_granted = block
|
49
52
|
end
|
50
|
-
|
53
|
+
|
51
54
|
def grants_access_to(*args, &block)
|
52
55
|
options = args.extract_options!
|
53
|
-
|
56
|
+
|
54
57
|
block = args.shift if args.first.respond_to?(:call)
|
55
58
|
sentinel_method = args.first
|
56
59
|
denied_handler = options.delete(:denies_with) || :default
|
57
|
-
|
60
|
+
|
58
61
|
before_filter(options) do |controller|
|
59
62
|
if block
|
60
63
|
if (block.arity == 1 ? controller.sentinel : controller).instance_eval(&block)
|
@@ -71,4 +74,4 @@ module Sentinel
|
|
71
74
|
end
|
72
75
|
end
|
73
76
|
end
|
74
|
-
end
|
77
|
+
end
|
data/lib/sentinel/sentinel.rb
CHANGED
@@ -7,23 +7,27 @@ module Sentinel
|
|
7
7
|
self.send("#{key}=", attributes[key]) if self.respond_to?("#{key}=")
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def [](temporary_overrides)
|
12
12
|
temporary_overrides.keys.each do |key|
|
13
13
|
create_accessor_for_attribute(key)
|
14
14
|
end
|
15
|
-
|
15
|
+
|
16
16
|
returning self.clone do |duplicate|
|
17
17
|
temporary_overrides.keys.each do |key|
|
18
|
-
|
18
|
+
if self.respond_to?("#{key}=")
|
19
|
+
duplicate.send("#{key}=", temporary_overrides[key])
|
20
|
+
end
|
19
21
|
end
|
20
22
|
end
|
21
23
|
end
|
22
|
-
|
24
|
+
|
23
25
|
private
|
24
|
-
|
26
|
+
|
25
27
|
def create_accessor_for_attribute(attribute)
|
26
|
-
|
28
|
+
unless self.respond_to?(attribute) || self.respond_to?("#{attribute}=")
|
29
|
+
self.class_eval { attr_accessor attribute }
|
30
|
+
end
|
27
31
|
end
|
28
32
|
end
|
29
|
-
end
|
33
|
+
end
|
data/sentinel.gemspec
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{sentinel}
|
5
|
-
s.version = "0.1.
|
5
|
+
s.version = "0.1.5"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Joshua Clayton"]
|
9
|
-
s.date = %q{2009-
|
9
|
+
s.date = %q{2009-07-27}
|
10
10
|
s.description = %q{Simple authorization for Rails}
|
11
11
|
s.email = %q{joshua.clayton@gmail.com}
|
12
12
|
s.extra_rdoc_files = ["lib/sentinel/controller.rb", "lib/sentinel/sentinel.rb", "lib/sentinel.rb", "README.textile"]
|
data/shoulda_macros/sentinel.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Sentinel
|
2
2
|
module Shoulda
|
3
|
-
|
3
|
+
|
4
4
|
def sentinel_context(options = {}, &block)
|
5
5
|
context "When sentinel is set up to #{options.inspect}" do
|
6
6
|
setup do
|
@@ -11,64 +11,66 @@ module Sentinel
|
|
11
11
|
assert_equal options[key], @controller.sentinel.send(key)
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
merge_block(&block)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def denied_with(denied_name, &block)
|
20
20
|
context "denied_with #{denied_name}" do
|
21
|
-
without_before_filters do # this strips out any other preconditions so we can properly test the handler
|
21
|
+
without_before_filters do # this strips out any other preconditions so we can properly test the handler
|
22
22
|
setup do
|
23
23
|
action = "action_#{Digest::MD5.hexdigest(Time.now.to_s.split(//).sort_by {rand}.join)}"
|
24
24
|
@controller.class.grants_access_to lambda { false }, :only => [action.to_sym], :denies_with => denied_name
|
25
25
|
get action.to_sym
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
merge_block(&block)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
def without_before_filters(&block)
|
34
34
|
context "" do
|
35
35
|
setup do
|
36
36
|
@filter_chain = @controller.class.filter_chain
|
37
37
|
@controller.class.write_inheritable_attribute("filter_chain", ActionController::Filters::FilterChain.new)
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
teardown do
|
41
41
|
@controller.class.write_inheritable_attribute("filter_chain", @filter_chain)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
merge_block(&block)
|
45
45
|
end
|
46
46
|
end
|
47
|
-
|
48
|
-
def should_grant_access_to(
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
|
48
|
+
def should_grant_access_to(*args)
|
49
|
+
args.each do |command|
|
50
|
+
context "performing `#{command}`" do
|
51
|
+
should "allow access" do
|
52
|
+
granted = @controller.class.read_inheritable_attribute(:access_granted)
|
53
|
+
@controller.class.expects(:access_granted).at_least(1).returns(granted)
|
54
|
+
eval command
|
55
|
+
end
|
54
56
|
end
|
55
57
|
end
|
56
58
|
end
|
57
|
-
|
59
|
+
|
58
60
|
def should_deny_access_to(*args)
|
59
61
|
options = args.extract_options!
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
args.each do |command|
|
63
|
+
context "performing `#{command}`" do
|
64
|
+
should "call the proper denied handler" do
|
65
|
+
denied_with = options[:with] || :default
|
66
|
+
handler = @controller.class.read_inheritable_attribute(:access_denied)[denied_with]
|
67
|
+
@controller.class.access_denied.expects(:[]).with(denied_with).returns(handler)
|
68
|
+
eval command
|
69
|
+
end
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
71
|
-
|
73
|
+
|
72
74
|
def should_not_guard(command)
|
73
75
|
context "performing `#{command}`" do
|
74
76
|
setup do
|
@@ -78,14 +80,14 @@ module Sentinel
|
|
78
80
|
def rescue_action(e) raise e end; # force the controller to reraise the exception error
|
79
81
|
end
|
80
82
|
end
|
81
|
-
|
83
|
+
|
82
84
|
should "not use guard with a sentinel" do
|
83
85
|
eval command
|
84
86
|
end
|
85
87
|
end
|
86
88
|
end
|
87
|
-
|
89
|
+
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
|
-
Test::Unit::TestCase.extend(Sentinel::Shoulda)
|
93
|
+
Test::Unit::TestCase.extend(Sentinel::Shoulda)
|
@@ -3,50 +3,55 @@ require 'test_helper'
|
|
3
3
|
class SentinelControllerTest < ActionController::TestCase
|
4
4
|
include ActionView::Helpers::UrlHelper
|
5
5
|
include ActionView::Helpers::TagHelper
|
6
|
-
|
6
|
+
|
7
7
|
def setup
|
8
8
|
@controller = ForumsController.new
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
sentinel_context do
|
12
12
|
without_before_filters do
|
13
13
|
should_not_guard "get :index"
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
sentinel_context({:viewable? => true}) do
|
18
18
|
should_grant_access_to "get :show"
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
sentinel_context({:creatable? => false}) do
|
22
|
-
should_deny_access_to "get :new",
|
23
|
-
|
22
|
+
should_deny_access_to "get :new",
|
23
|
+
"post :create, :forum => {:name => 'My New Forum'}",
|
24
|
+
:with => :redirect_to_index
|
24
25
|
end
|
25
|
-
|
26
|
+
|
26
27
|
sentinel_context({:creatable? => true}) do
|
27
|
-
should_grant_access_to
|
28
|
-
|
28
|
+
should_grant_access_to "get :new",
|
29
|
+
"post :create, :forum => {:name => 'My New Forum'}"
|
29
30
|
end
|
30
|
-
|
31
|
+
|
31
32
|
sentinel_context({:viewable? => false}) do
|
32
33
|
should_deny_access_to "get :show", :with => :sentinel_unauthorized
|
33
34
|
end
|
34
|
-
|
35
|
+
|
35
36
|
context "A controller-global grants_access_to that denies access" do
|
36
|
-
# this ensures that, even with a failing grants_access_to,
|
37
|
+
# this ensures that, even with a failing grants_access_to,
|
38
|
+
# we can properly test all denied_with handlers
|
37
39
|
setup do
|
38
40
|
@controller.stubs(:stubbed_method).returns(false)
|
39
41
|
end
|
40
|
-
|
42
|
+
|
41
43
|
denied_with :redirect_to_index do
|
42
|
-
should_redirect_to("forums root")
|
44
|
+
should_redirect_to("forums root") do
|
45
|
+
url_for(:controller => "forums", :action => "secondary_index")
|
46
|
+
end
|
43
47
|
end
|
44
48
|
|
45
49
|
denied_with :sentinel_unauthorized do
|
46
50
|
should_respond_with :forbidden
|
47
51
|
|
48
52
|
should "render text as response" do
|
49
|
-
assert_equal
|
53
|
+
assert_equal "This is an even more unique default restricted warning",
|
54
|
+
@response.body
|
50
55
|
end
|
51
56
|
end
|
52
57
|
end
|
@@ -2,68 +2,71 @@ class ForumsController < ApplicationController
|
|
2
2
|
controls_access_with do
|
3
3
|
ForumSentinel.new :current_user => current_user, :forum => @forum
|
4
4
|
end
|
5
|
-
|
5
|
+
|
6
6
|
grants_access_to lambda { stubbed_method }, :denies_with => :redirect_to_index
|
7
|
-
|
7
|
+
|
8
8
|
grants_access_to :denies_with => :sentinel_unauthorized do
|
9
9
|
stubbed_method_two
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
grants_access_to :reorderable?, :only => [:reorder]
|
13
13
|
grants_access_to :creatable?, :only => [:new, :create], :denies_with => :redirect_to_index
|
14
14
|
grants_access_to :viewable?, :only => [:show], :denies_with => :sentinel_unauthorized
|
15
15
|
grants_access_to :destroyable?, :only => [:destroy]
|
16
|
-
|
16
|
+
|
17
17
|
on_denied_with :redirect_to_index do
|
18
18
|
redirect_to url_for(:controller => "forums", :action => "secondary_index")
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
on_denied_with :sentinel_unauthorized do
|
22
22
|
respond_to do |wants|
|
23
|
-
wants.html
|
23
|
+
wants.html do
|
24
|
+
render :text => "This is an even more unique default restricted warning",
|
25
|
+
:status => :forbidden
|
26
|
+
end
|
24
27
|
wants.any { head :forbidden }
|
25
28
|
end
|
26
29
|
end
|
27
|
-
|
30
|
+
|
28
31
|
def index
|
29
32
|
handle_successfully
|
30
33
|
end
|
31
|
-
|
34
|
+
|
32
35
|
def secondary_index
|
33
36
|
handle_successfully
|
34
37
|
end
|
35
|
-
|
38
|
+
|
36
39
|
def new
|
37
40
|
handle_successfully
|
38
41
|
end
|
39
|
-
|
42
|
+
|
40
43
|
def show
|
41
44
|
handle_successfully
|
42
45
|
end
|
43
|
-
|
46
|
+
|
44
47
|
def edit
|
45
48
|
handle_successfully
|
46
49
|
end
|
47
|
-
|
50
|
+
|
48
51
|
def update
|
49
52
|
handle_successfully
|
50
53
|
end
|
51
|
-
|
54
|
+
|
52
55
|
def delete
|
53
56
|
handle_successfully
|
54
57
|
end
|
55
|
-
|
58
|
+
|
56
59
|
private
|
57
|
-
|
60
|
+
|
58
61
|
def handle_successfully
|
59
62
|
render :text => "forums"
|
60
63
|
end
|
61
|
-
|
64
|
+
|
62
65
|
def stubbed_method
|
63
66
|
true
|
64
67
|
end
|
65
|
-
|
68
|
+
|
66
69
|
def stubbed_method_two
|
67
70
|
true
|
68
71
|
end
|
69
|
-
end
|
72
|
+
end
|
@@ -2,30 +2,32 @@ class ForumSentinel < Sentinel::Sentinel
|
|
2
2
|
def creatable?
|
3
3
|
current_user_admin?
|
4
4
|
end
|
5
|
-
|
5
|
+
|
6
6
|
def reorderable?
|
7
7
|
current_user_admin?
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def viewable?
|
11
|
-
|
11
|
+
return true if self.forum.public? || current_user_admin?
|
12
|
+
(current_user? && self.forum.members.include?(self.current_user))
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
def editable?
|
15
|
-
|
16
|
+
return true if current_user_admin?
|
17
|
+
(current_user? && self.forum.owner == self.current_user)
|
16
18
|
end
|
17
|
-
|
19
|
+
|
18
20
|
def destroyable?
|
19
21
|
editable?
|
20
22
|
end
|
21
|
-
|
23
|
+
|
22
24
|
private
|
23
|
-
|
25
|
+
|
24
26
|
def current_user?
|
25
27
|
!self.current_user.nil?
|
26
28
|
end
|
27
|
-
|
29
|
+
|
28
30
|
def current_user_admin?
|
29
31
|
current_user? && self.current_user.admin?
|
30
32
|
end
|
31
|
-
end
|
33
|
+
end
|
data/test/unit/sentinel_test.rb
CHANGED
@@ -5,36 +5,36 @@ class SentinelTest < ActiveSupport::TestCase
|
|
5
5
|
setup do
|
6
6
|
@sentinel = Sentinel::Sentinel
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
should "create attr_accessor's for each valid key" do
|
10
10
|
sentinel = @sentinel.new(:user => {:name => "John", :active => true}, :forum => {:name => "My Forum"})
|
11
11
|
assert_equal({:name => "John", :active => true}, sentinel.user)
|
12
12
|
assert_equal({:name => "My Forum"}, sentinel.forum)
|
13
|
-
|
13
|
+
|
14
14
|
sentinel.user = sentinel.forum = nil
|
15
15
|
assert_nil sentinel.user
|
16
16
|
assert_nil sentinel.forum
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
should "not create attr_accessors for methods that already exist" do
|
20
20
|
sentinel = @sentinel.new(:class => "fake", :to_s => "one", :user => "real")
|
21
21
|
assert_equal sentinel.user, "real"
|
22
22
|
assert_equal sentinel.class, Sentinel::Sentinel
|
23
23
|
assert_not_equal sentinel.to_s, "one"
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
should "reassign predefined attribute values if set" do
|
27
27
|
@sentinel.attr_accessor_with_default :message, "simple message"
|
28
28
|
assert_equal "simple message", @sentinel.new.message
|
29
29
|
assert_equal "complex message", @sentinel.new(:message => "complex message").message
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
context "When overriding attributes" do
|
34
34
|
setup do
|
35
35
|
@sentinel = Sentinel::Sentinel
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
should "only override for that specific instance" do
|
39
39
|
sentinel = @sentinel.new(:user => "assigned", :forum => nil)
|
40
40
|
assert_equal "assigned", sentinel.user
|
@@ -42,13 +42,13 @@ class SentinelTest < ActiveSupport::TestCase
|
|
42
42
|
assert_nil sentinel[:user => nil].user
|
43
43
|
assert_equal "forum", sentinel[:forum => "forum"].forum
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
should "define an attr_accessor if the attribute doesn't exist" do
|
47
47
|
sentinel = @sentinel.new
|
48
48
|
assert_raise NoMethodError do
|
49
49
|
sentinel.name
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
assert_equal "taken", sentinel[:name => "taken"].name
|
53
53
|
assert_nil sentinel.name
|
54
54
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: joshuaclayton-sentinel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Clayton
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-07-27 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- test/unit/sentinel_test.rb
|
69
69
|
has_rdoc: true
|
70
70
|
homepage: http://github.com/joshuaclayton/sentinel
|
71
|
+
licenses:
|
71
72
|
post_install_message:
|
72
73
|
rdoc_options:
|
73
74
|
- --line-numbers
|
@@ -93,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
94
|
requirements: []
|
94
95
|
|
95
96
|
rubyforge_project: sentinel
|
96
|
-
rubygems_version: 1.
|
97
|
+
rubygems_version: 1.3.5
|
97
98
|
signing_key:
|
98
99
|
specification_version: 3
|
99
100
|
summary: Simple authorization for Rails
|