cbac 0.3.1 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +60 -44
- data/Rakefile +2 -2
- data/cbac.gemspec +31 -31
- data/generators/cbac/cbac_generator.rb +27 -6
- data/generators/cbac/templates/config/cbac.pristine +2 -0
- data/generators/cbac/templates/controllers/permissions_controller.rb +21 -2
- data/generators/cbac/templates/controllers/upgrade_controller.rb +24 -0
- data/generators/cbac/templates/fixtures/cbac_memberships.yml +1 -1
- data/generators/cbac/templates/migrate/create_cbac_from_scratch.rb +59 -0
- data/generators/cbac/templates/migrate/create_cbac_upgrade_path.rb +31 -0
- data/generators/cbac/templates/tasks/cbac.rake +345 -0
- data/generators/cbac/templates/views/layouts/cbac.html.erb +2 -1
- data/generators/cbac/templates/views/memberships/index.html.erb +1 -1
- data/generators/cbac/templates/views/permissions/index.html.erb +14 -6
- data/generators/cbac/templates/views/upgrade/index.html.erb +32 -0
- data/lib/cbac.rb +23 -12
- data/lib/cbac/cbac_pristine/pristine.rb +133 -0
- data/lib/cbac/cbac_pristine/pristine_file.rb +158 -0
- data/lib/cbac/cbac_pristine/pristine_permission.rb +194 -0
- data/lib/cbac/cbac_pristine/pristine_role.rb +42 -0
- data/lib/cbac/known_permission.rb +14 -0
- data/lib/cbac/permission.rb +1 -1
- data/lib/cbac/privilege.rb +44 -0
- data/lib/cbac/privilege_set.rb +5 -4
- data/lib/cbac/privilege_set_record.rb +3 -1
- data/spec/cbac_pristine_file_spec.rb +329 -0
- data/spec/cbac_pristine_permission_spec.rb +358 -0
- data/spec/cbac_pristine_role_spec.rb +85 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +12 -0
- data/tasks/cbac.rake +345 -19
- data/test/test_cbac_privilege.rb +54 -0
- metadata +43 -9
- data/generators/cbac/templates/migrate/create_cbac.rb +0 -40
@@ -0,0 +1,194 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'pristine_role'))
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'pristine_permission'))
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
module Cbac
|
6
|
+
module CbacPristine
|
7
|
+
class PristinePermission < ActiveRecord::Base
|
8
|
+
set_table_name 'cbac_staged_permissions'
|
9
|
+
|
10
|
+
belongs_to :pristine_role, :class_name => "Cbac::CbacPristine::PristineRole"
|
11
|
+
|
12
|
+
def privilege_set
|
13
|
+
Cbac::PrivilegeSetRecord.first(:conditions => {:name => privilege_set_name})
|
14
|
+
end
|
15
|
+
|
16
|
+
def operation_string
|
17
|
+
case operation
|
18
|
+
when '+'
|
19
|
+
return "add"
|
20
|
+
when '-'
|
21
|
+
return "revoke"
|
22
|
+
else
|
23
|
+
return "unknown"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
#convert this pristine line to a yml statement which can be used to create a yml fixtures file
|
28
|
+
#executing this statement will result in one cbac_permission in the DB
|
29
|
+
def to_yml_fixture(fixture_id = nil)
|
30
|
+
raise ArgumentError, "Error: cannot convert line #{line_number.to_s} to yml because the role is not specified" if pristine_role.nil?
|
31
|
+
raise ArgumentError, "Error: cannot convert line #{line_number.to_s} to yml because the privilege_set_name is not specified" if privilege_set_name.blank?
|
32
|
+
|
33
|
+
fixture_id = line_number if fixture_id.nil?
|
34
|
+
|
35
|
+
yml = "cbac_permission_00" << fixture_id.to_s << ":\n"
|
36
|
+
yml << " id: " << fixture_id.to_s << "\n"
|
37
|
+
yml << " context_role: "
|
38
|
+
yml << pristine_role.name if pristine_role.role_type == PristineRole.ROLE_TYPES[:context]
|
39
|
+
yml << "\n"
|
40
|
+
yml << " generic_role_id: " << pristine_role.role_id.to_s << "\n"
|
41
|
+
yml << " privilege_set_id: <%= Cbac::PrivilegeSetRecord.find(:first, :conditions => {:name => '" << privilege_set_name << "'}).id %>\n"
|
42
|
+
yml << " created_at: " << Time.now.strftime("%Y-%m-%d %H:%M:%S") << "\n"
|
43
|
+
yml << " updated_at: " << Time.now.strftime("%Y-%m-%d %H:%M:%S") << "\n"
|
44
|
+
yml << "\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
# checks if the current cbac permissions contains a permission which is exactly like this one
|
48
|
+
def cbac_permission_exists?
|
49
|
+
if pristine_role.role_type == PristineRole.ROLE_TYPES[:context]
|
50
|
+
Cbac::Permission.count(:joins => [:privilege_set], :conditions => {:cbac_privilege_set => {:name => privilege_set_name}, :context_role => pristine_role.name}) > 0
|
51
|
+
else
|
52
|
+
Cbac::Permission.count(:joins => [:generic_role, :privilege_set], :conditions => {:cbac_privilege_set => {:name => privilege_set_name}, :cbac_generic_roles => {:name => pristine_role.name}}) > 0
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# checks if a pristine permission with the same properties(except line_number) exists in the database
|
57
|
+
def exists?
|
58
|
+
Cbac::CbacPristine::PristinePermission.count(:conditions => {:privilege_set_name => privilege_set_name, :pristine_role_id => pristine_role_id, :operation => operation}) > 0
|
59
|
+
end
|
60
|
+
|
61
|
+
# checks if a pristine permission with the exact same properties(except line_number), but the reverse operation exists in the database
|
62
|
+
def reverse_exists?
|
63
|
+
Cbac::CbacPristine::PristinePermission.count(:conditions => {:privilege_set_name => privilege_set_name, :pristine_role_id => pristine_role_id, :operation => reverse_operation}) > 0
|
64
|
+
end
|
65
|
+
|
66
|
+
# delete the pristine permission with the reverse operation of this one
|
67
|
+
def delete_reverse_permission
|
68
|
+
reverse_permission = Cbac::CbacPristine::PristinePermission.first(:conditions => {:privilege_set_name => privilege_set_name, :pristine_role_id => pristine_role_id, :operation => reverse_operation})
|
69
|
+
reverse_permission.delete
|
70
|
+
end
|
71
|
+
|
72
|
+
# get the reverse operation of this one
|
73
|
+
def reverse_operation
|
74
|
+
case operation
|
75
|
+
when '+'
|
76
|
+
return '-'
|
77
|
+
when '-'
|
78
|
+
return '+'
|
79
|
+
when 'x', '=>'
|
80
|
+
raise NotImplementedError, "Error: using an x or => in a pristine file is not implemented yet"
|
81
|
+
else
|
82
|
+
raise ArgumentError, "Error: invalid operation #{operation} is used in the pristine file"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# checks if the known_permissions table has an entry for this permission
|
87
|
+
def known_permission_exists?
|
88
|
+
Cbac::KnownPermission.count(:conditions => {:permission_type => pristine_role.known_permission_type, :permission_number => line_number}) > 0
|
89
|
+
end
|
90
|
+
|
91
|
+
# accept this permission and apply to the current cbac permission set
|
92
|
+
def accept
|
93
|
+
case operation
|
94
|
+
when '+'
|
95
|
+
handle_grant_permission
|
96
|
+
when '-'
|
97
|
+
handle_revoke_permission
|
98
|
+
when 'x', '=>'
|
99
|
+
raise NotImplementedError, "Error: using an x or => in a pristine file is not implemented yet"
|
100
|
+
else
|
101
|
+
raise ArgumentError, "Error: invalid operation #{operation} is used in the pristine file"
|
102
|
+
end
|
103
|
+
PristinePermission.delete(id) unless id.nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
# reject this permission, but register it as a known permission. The user actually rejected this himself.
|
107
|
+
def reject
|
108
|
+
register_change
|
109
|
+
PristinePermission.delete(id) unless id.nil?
|
110
|
+
end
|
111
|
+
|
112
|
+
# add this permission to the cbac permission set, unless it already exists
|
113
|
+
def handle_grant_permission
|
114
|
+
return if cbac_permission_exists?
|
115
|
+
|
116
|
+
permission = Cbac::Permission.new
|
117
|
+
permission.privilege_set = privilege_set
|
118
|
+
|
119
|
+
if pristine_role.role_type == PristineRole.ROLE_TYPES[:context]
|
120
|
+
permission.context_role = pristine_role.name
|
121
|
+
else
|
122
|
+
generic_role = Cbac::GenericRole.first(:conditions => {:name => pristine_role.name})
|
123
|
+
permission.generic_role = generic_role.nil? ? Cbac::GenericRole.create(:name => pristine_role.name, :remarks => "Autogenerated by Cbac loading / upgrade system") : generic_role
|
124
|
+
end
|
125
|
+
|
126
|
+
register_change if permission.save
|
127
|
+
permission
|
128
|
+
end
|
129
|
+
|
130
|
+
# revoke this permission from the current permission set, raises an error if it doesn't exist yet
|
131
|
+
def handle_revoke_permission
|
132
|
+
raise ArgumentError, "Error: trying to revoke permission #{privilege_set_name} for #{pristine_role.name}, but this permission does not exist" unless cbac_permission_exists?
|
133
|
+
|
134
|
+
if pristine_role.role_type == PristineRole.ROLE_TYPES[:context]
|
135
|
+
permission = Cbac::Permission.first(:joins => [:privilege_set], :conditions => {:cbac_privilege_set => {:name => privilege_set_name}, :context_role => pristine_role.name})
|
136
|
+
else
|
137
|
+
permission = Cbac::Permission.first(:joins => [:generic_role, :privilege_set], :conditions => {:cbac_privilege_set => {:name => privilege_set_name}, :cbac_generic_roles => {:name => pristine_role.name}})
|
138
|
+
end
|
139
|
+
|
140
|
+
register_change if permission.destroy
|
141
|
+
end
|
142
|
+
|
143
|
+
# register this permission as a known permission
|
144
|
+
def register_change
|
145
|
+
Cbac::KnownPermission.create(:permission_number => line_number, :permission_type => pristine_role.known_permission_type)
|
146
|
+
end
|
147
|
+
|
148
|
+
# add this permission to the staging area
|
149
|
+
def stage
|
150
|
+
raise ArgumentError, "Error: this staged permission already exists. Record with line number #{line_number} is a duplicate permission." if exists?
|
151
|
+
return if known_permission_exists?
|
152
|
+
|
153
|
+
if operation == '-'
|
154
|
+
# if the reverse permission is also staged, remove it and do not add this one
|
155
|
+
if reverse_exists?
|
156
|
+
delete_reverse_permission
|
157
|
+
return
|
158
|
+
end
|
159
|
+
# if this is an attempt to revoke a permission, it should exist as a real cbac permission!
|
160
|
+
save if cbac_permission_exists?
|
161
|
+
elsif operation == '+'
|
162
|
+
# if this is an attempt to add a permission, it MUST not exist yet
|
163
|
+
save unless cbac_permission_exists?
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
|
169
|
+
# clear the staging area of all generic pristine permissions
|
170
|
+
def self.delete_generic_permissions
|
171
|
+
generic_staged_permissions = all(:joins => :pristine_role, :conditions => ["cbac_staged_roles.role_type = ?", PristineRole.ROLE_TYPES[:generic]])
|
172
|
+
generic_staged_permissions.each do |permission|
|
173
|
+
delete(permission.id)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# clear the staging area of all non generic permissions
|
178
|
+
def self.delete_non_generic_permissions
|
179
|
+
staged_permissions = all(:joins => :pristine_role, :conditions => ["cbac_staged_roles.role_type != ?", PristineRole.ROLE_TYPES[:generic]])
|
180
|
+
staged_permissions.each do |permission|
|
181
|
+
delete(permission.id)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def self.count_generic_permissions
|
186
|
+
count(:joins => :pristine_role, :conditions => ["cbac_staged_roles.role_type = ?", PristineRole.ROLE_TYPES[:generic]])
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.count_non_generic_permissions
|
190
|
+
count(:joins => :pristine_role, :conditions => ["cbac_staged_roles.role_type != ?", PristineRole.ROLE_TYPES[:generic]])
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
module Cbac
|
3
|
+
module CbacPristine
|
4
|
+
class PristineRole < ActiveRecord::Base
|
5
|
+
set_table_name "cbac_staged_roles"
|
6
|
+
|
7
|
+
def self.ROLE_TYPES
|
8
|
+
{:context => "context", :generic => "generic", :admin => "administrator"}
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
#convert this cbac role to a yml statement which can be used to create a yml fixtures file
|
13
|
+
#executing this statement will result in one cbac_generic_role in the DB
|
14
|
+
def to_yml_fixture(fixture_id = nil)
|
15
|
+
fixture_id = role_id if fixture_id.nil?
|
16
|
+
|
17
|
+
return '' if role_type == Cbac::CbacPristine::PristineRole.ROLE_TYPES[:context]
|
18
|
+
raise ArgumentError, "cannot convert role #{id.to_s} to yml, because it has no name" if name.blank?
|
19
|
+
|
20
|
+
yml = "cbac_generic_role_00" << fixture_id.to_s << ":\n"
|
21
|
+
yml << " id: " << fixture_id.to_s << "\n"
|
22
|
+
yml << " name: " << name << "\n"
|
23
|
+
yml << " created_at: " << Time.now.strftime("%Y-%m-%d %H:%M:%S") << "\n"
|
24
|
+
yml << " updated_at: " << Time.now.strftime("%Y-%m-%d %H:%M:%S") << "\n"
|
25
|
+
yml << "\n"
|
26
|
+
end
|
27
|
+
|
28
|
+
def known_permission_type
|
29
|
+
# NOTE: known permissions use different type definitions than pristine roles.
|
30
|
+
# They only use the file type to determine if it is a generic or context role.
|
31
|
+
# Context roles include the admin role (same file) while pristine roles use a different type
|
32
|
+
role_type == PristineRole.ROLE_TYPES[:generic] ? Cbac::KnownPermission.PERMISSION_TYPES[:generic] : Cbac::KnownPermission.PERMISSION_TYPES[:context]
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.admin_role(use_db = true)
|
36
|
+
admin_role = use_db ? PristineRole.first(:conditions => {:role_type => PristineRole.ROLE_TYPES[:admin]}) : nil
|
37
|
+
|
38
|
+
admin_role.nil? ? PristineRole.new(:role_id => 1, :role_type => PristineRole.ROLE_TYPES[:admin], :name => "administrator") : admin_role
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Cbac::KnownPermission < ActiveRecord::Base
|
2
|
+
set_table_name "cbac_known_permissions"
|
3
|
+
|
4
|
+
cattr_accessor :PERMISSION_TYPES
|
5
|
+
@@PERMISSION_TYPES = {:context => 0, :generic => 1}
|
6
|
+
|
7
|
+
def self.find_context_permissions(conditions = {})
|
8
|
+
all(:conditions => conditions.merge(:permission_type => @@PERMISSION_TYPES[:context]))
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.find_generic_permissions(conditions = {})
|
12
|
+
all(:conditions => conditions.merge(:permission_type => @@PERMISSION_TYPES[:generic]))
|
13
|
+
end
|
14
|
+
end
|
data/lib/cbac/permission.rb
CHANGED
@@ -2,5 +2,5 @@ class Cbac::Permission < ActiveRecord::Base
|
|
2
2
|
set_table_name "cbac_permissions"
|
3
3
|
|
4
4
|
belongs_to :generic_role, :class_name => "Cbac::GenericRole", :foreign_key => "generic_role_id"
|
5
|
-
belongs_to :
|
5
|
+
belongs_to :privilege_set, :class_name => "Cbac::PrivilegeSetRecord", :foreign_key => "privilege_set_id"
|
6
6
|
end
|
data/lib/cbac/privilege.rb
CHANGED
@@ -7,6 +7,14 @@ class Privilege
|
|
7
7
|
class << self
|
8
8
|
attr_reader :get_resources, :post_resources, :model_attributes, :models
|
9
9
|
|
10
|
+
# The includes hash contains references to inheritence. The key points to the
|
11
|
+
# base class, the value is an array of children.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# If Child inherits from Parent, then the structure would be:
|
15
|
+
# includes[:Parent] = [:Child]
|
16
|
+
attr_reader :includes
|
17
|
+
|
10
18
|
# Links a resource with a PrivilegeSet
|
11
19
|
#
|
12
20
|
# An ArgumentError exception is thrown if the PrivilegeSet does not exist.
|
@@ -22,12 +30,43 @@ class Privilege
|
|
22
30
|
case action_option[0]
|
23
31
|
when "GET"
|
24
32
|
(@get_resources[method] ||= Array.new) << PrivilegeSet.sets[privilege_set]
|
33
|
+
(@includes[privilege_set] || Array.new).each {|child_set| (@get_resources[method] ||= Array.new) << PrivilegeSet.sets[child_set]} unless @includes.nil?
|
25
34
|
when "POST"
|
26
35
|
(@post_resources[method] ||= Array.new) << PrivilegeSet.sets[privilege_set]
|
36
|
+
(@includes[privilege_set] || Array.new).each {|child_set| (@post_resources[method] ||= Array.new) << PrivilegeSet.sets[child_set]} unless @includes.nil?
|
27
37
|
else
|
38
|
+
raise "This should never happen"
|
28
39
|
end
|
29
40
|
end
|
30
41
|
|
42
|
+
# Make a privilege set dependant on other privilege set(s).
|
43
|
+
#
|
44
|
+
# Usage:
|
45
|
+
# Privilege.include :child_set, :base_set
|
46
|
+
# Privilege.include :child_set, [:base_set_1, :base_set_2]
|
47
|
+
#
|
48
|
+
# An ArgumentError exception is thrown if any of the PrivilegeSet methods do not exist.
|
49
|
+
def include(privilege_set, included_privilege_set)
|
50
|
+
@includes = Hash.new if @includes.nil?
|
51
|
+
child_set = privilege_set.to_sym
|
52
|
+
raise ArgumentError, "CBAC: PrivilegeSet does not exist: #{child_set}" unless PrivilegeSet.sets.include?(child_set)
|
53
|
+
included_privilege_set = [included_privilege_set] unless included_privilege_set.is_a?(Enumerable)
|
54
|
+
included_privilege_set.each do |base_set|
|
55
|
+
# Check for existence of PrivilegeSet
|
56
|
+
raise ArgumentError, "CBAC: PrivilegeSet does not exist: #{base_set}" unless PrivilegeSet.sets.include?(base_set)
|
57
|
+
# Adds the references
|
58
|
+
(@includes[base_set.to_sym] ||= Array.new) << child_set
|
59
|
+
# Copies existing resources
|
60
|
+
@get_resources.each do |method, privilege_sets|
|
61
|
+
resource child_set, method, :get if privilege_sets.any? {|set| set.name == base_set.to_s}
|
62
|
+
end
|
63
|
+
@post_resources.each do |method, privilege_sets|
|
64
|
+
resource child_set, method, :post if privilege_sets.any? {|set| set.name == base_set.to_s}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
31
70
|
def model_attribute
|
32
71
|
|
33
72
|
end
|
@@ -39,6 +78,11 @@ class Privilege
|
|
39
78
|
# action_type Valid values for action_type are "get", "post" and "put".
|
40
79
|
# "put" is converted into "post".
|
41
80
|
#
|
81
|
+
# Usage:
|
82
|
+
# Privilege.select "my_controller/action", :get
|
83
|
+
#
|
84
|
+
# Returns an array of PrivilegeSet objects
|
85
|
+
#
|
42
86
|
# If incorrect values are given for action_type the method will raise an
|
43
87
|
# ArgumentError. If the controller and action name are not found, an
|
44
88
|
# exception is being raised.
|
data/lib/cbac/privilege_set.rb
CHANGED
@@ -18,10 +18,11 @@ class Cbac::PrivilegeSet
|
|
18
18
|
@sets = Hash.new if @sets.nil?
|
19
19
|
# check for double creation
|
20
20
|
raise ArgumentError, "CBAC: PrivilegeSet was already defined: #{symbol.to_s}" if @sets.include?(symbol)
|
21
|
-
# Create record if
|
22
|
-
|
23
|
-
record
|
24
|
-
record.
|
21
|
+
# Create record if privilege set doesn't exist
|
22
|
+
record = Cbac::PrivilegeSetRecord.find_or_create_by_name(symbol.to_s)
|
23
|
+
record.set_comment(comment)
|
24
|
+
record.save
|
25
|
+
|
25
26
|
@sets[symbol] = record
|
26
27
|
end
|
27
28
|
end
|
@@ -0,0 +1,329 @@
|
|
1
|
+
require 'spec'
|
2
|
+
require 'cbac/cbac_pristine/pristine_permission'
|
3
|
+
require 'cbac/cbac_pristine/pristine_role'
|
4
|
+
require 'cbac/cbac_pristine/pristine_file'
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
|
6
|
+
include Cbac::CbacPristine
|
7
|
+
|
8
|
+
describe "CbacPristineFile" do
|
9
|
+
before(:each) do
|
10
|
+
@pristine_file = PristineFile.new("cbac.pristine")
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "indicate if a line looks like a pristine line" do
|
14
|
+
|
15
|
+
it "should indicate that a ruby style comment line is not a pristine line" do
|
16
|
+
comment_line = "#this is a comment line in Ruby"
|
17
|
+
|
18
|
+
@pristine_file.is_pristine_permission_line?(comment_line, 1).should be_false
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should raise an error if the line does not look like a pristine line" do
|
22
|
+
line = "this is not pristine line. And it isn't a comment. 1"
|
23
|
+
|
24
|
+
proc{
|
25
|
+
@pristine_file.is_pristine_permission_line?(line, 0)
|
26
|
+
}.should raise_error(SyntaxError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return true in case of a valid pristine line" do
|
30
|
+
line = "0:+:PrivilegeSet(login)ContextRole(everybody)"
|
31
|
+
|
32
|
+
@pristine_file.is_pristine_permission_line?(line, 0).should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should fail if the id of the pristine line contains a character" do
|
36
|
+
line = "0b:+:PrivilegeSet(login)ContextRole(everybody)"
|
37
|
+
|
38
|
+
proc{
|
39
|
+
@pristine_file.is_pristine_permission_line?(line, 0)
|
40
|
+
}.should raise_error(SyntaxError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should succeed if the privilege set name is not provided" do
|
44
|
+
line = "0:+:PrivilegeSet()Admin()"
|
45
|
+
|
46
|
+
@pristine_file.is_pristine_permission_line?(line, 0).should be_true
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should succeed if the context role name is not provided" do
|
50
|
+
line = "0:+:PrivilegeSet(login)ContextRole()"
|
51
|
+
|
52
|
+
@pristine_file.is_pristine_permission_line?(line, 0).should be_true
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "parse the privilege set name from a pristine line" do
|
58
|
+
it "should fail if the privilege set name is not provided" do
|
59
|
+
line = "0:+:PrivilegeSet()Admin()"
|
60
|
+
|
61
|
+
proc{
|
62
|
+
@pristine_file.parse_privilege_set_name(line, 0)
|
63
|
+
}.should raise_error(SyntaxError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should return the name of the privilege set provided in the line" do
|
67
|
+
privilege_set_name = "chat"
|
68
|
+
line = "0:+:PrivilegeSet(#{privilege_set_name})Admin()"
|
69
|
+
|
70
|
+
@pristine_file.parse_privilege_set_name(line, 0).should == privilege_set_name
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should fail if an invalid line is provided" do
|
74
|
+
line = "0:+:ContextRole(toeteraars)"
|
75
|
+
|
76
|
+
proc{
|
77
|
+
@pristine_file.parse_privilege_set_name(line, 0)
|
78
|
+
}.should raise_error(SyntaxError)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "parse the role from a pristine line" do
|
83
|
+
it "should return the admin role if the role is Admin()" do
|
84
|
+
admin_role = PristineRole.new(:role_id => 0, :role_type => PristineRole.ROLE_TYPES[:admin], :name => 'administrators')
|
85
|
+
PristineRole.stub!(:admin_role).and_return(admin_role)
|
86
|
+
line = "0:+:PrivilegeSet(chat)Admin()"
|
87
|
+
|
88
|
+
@pristine_file.parse_role(line, 0).should == admin_role
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should return a context role if the role specified as ContextRole" do
|
92
|
+
line = "0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"
|
93
|
+
|
94
|
+
@pristine_file.parse_role(line, 0).role_type.should == PristineRole.ROLE_TYPES[:context]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should return a context role with specified name if the role specified as ContextRole" do
|
98
|
+
context_role_name = "logged_in_user"
|
99
|
+
line = "0:+:PrivilegeSet(chat)ContextRole(#{context_role_name})"
|
100
|
+
|
101
|
+
@pristine_file.parse_role(line, 0).name.should == context_role_name
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should return an existing context role with specified name if possible" do
|
105
|
+
context_role_name = "logged_in_user"
|
106
|
+
line = "0:+:PrivilegeSet(chat)ContextRole(#{context_role_name})"
|
107
|
+
existing_context_role = PristineRole.create(:name => context_role_name, :role_id => 0, :role_type => PristineRole.ROLE_TYPES[:context])
|
108
|
+
|
109
|
+
@pristine_file.parse_role(line, 0).should == existing_context_role
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should not return an existing context role with specified name if db should not be used" do
|
113
|
+
context_role_name = "logged_in_user"
|
114
|
+
line = "0:+:PrivilegeSet(chat)ContextRole(#{context_role_name})"
|
115
|
+
existing_context_role = PristineRole.create(:name => context_role_name, :role_id => 0, :role_type => PristineRole.ROLE_TYPES[:context])
|
116
|
+
|
117
|
+
@pristine_file.parse_role(line, 0, false).should_not == existing_context_role
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should return a context role with id of 0 if the role specified as ContextRole" do
|
121
|
+
line = "0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"
|
122
|
+
|
123
|
+
@pristine_file.parse_role(line, 0).role_id.should == 0
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should fail if an invalid line is provided" do
|
127
|
+
line = "0:+:PrivilegeSet(toeteraars)"
|
128
|
+
|
129
|
+
proc{
|
130
|
+
@pristine_file.parse_role(line, 0)
|
131
|
+
}.should raise_error(SyntaxError)
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should fail if a generic role is provided for the normal (non-generic) pristine file" do
|
135
|
+
line = "0:+:PrivilegeSet(chat)GenericRole(group_admins)"
|
136
|
+
|
137
|
+
proc{
|
138
|
+
@pristine_file.parse_role(line, 0)
|
139
|
+
}.should raise_error(SyntaxError)
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
it "should return a generic role if a generic pristine file is used" do
|
144
|
+
@pristine_file = GenericPristineFile.new("cbac.pristine")
|
145
|
+
line = "0:+:PrivilegeSet(chat)GenericRole(group_admins)"
|
146
|
+
|
147
|
+
@pristine_file.parse_role(line, 0).role_type.should == PristineRole.ROLE_TYPES[:generic]
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should return an existing generic role if use_db is not specified" do
|
151
|
+
generic_role_name = 'group_admins'
|
152
|
+
@pristine_file = GenericPristineFile.new("cbac.pristine")
|
153
|
+
line = "0:+:PrivilegeSet(chat)GenericRole(#{generic_role_name})"
|
154
|
+
existing_role = PristineRole.create(:role_id => 1, :role_type => PristineRole.ROLE_TYPES[:generic], :name => generic_role_name)
|
155
|
+
|
156
|
+
@pristine_file.parse_role(line, 0).should == existing_role
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should not use an existing role if use_db is set to false" do
|
160
|
+
generic_role_name = 'group_admins'
|
161
|
+
@pristine_file = GenericPristineFile.new("cbac.pristine")
|
162
|
+
line = "0:+:PrivilegeSet(chat)GenericRole(#{generic_role_name})"
|
163
|
+
existing_role = PristineRole.create(:role_id => 1, :role_type => PristineRole.ROLE_TYPES[:generic], :name => generic_role_name)
|
164
|
+
|
165
|
+
@pristine_file.parse_role(line, 0, false).should_not == existing_role
|
166
|
+
end
|
167
|
+
|
168
|
+
it "should fail if an Admin role is used in a generic pristine file" do
|
169
|
+
@pristine_file = GenericPristineFile.new("cbac.pristine")
|
170
|
+
line = "0:+:PrivilegeSet(chat)Admin()"
|
171
|
+
|
172
|
+
proc{
|
173
|
+
@pristine_file.parse_role(line, 0)
|
174
|
+
}.should raise_error(SyntaxError)
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should fail if an context role is used in a generic pristine file" do
|
178
|
+
@pristine_file = GenericPristineFile.new("cbac.pristine")
|
179
|
+
line = "0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"
|
180
|
+
|
181
|
+
proc{
|
182
|
+
@pristine_file.parse_role(line, 0)
|
183
|
+
}.should raise_error(SyntaxError)
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should fail if an invalid line is provided in a generic pristine file" do
|
187
|
+
@pristine_file = GenericPristineFile.new("cbac.pristine")
|
188
|
+
line = "0:+:PrivilegeSet(toeteraars)"
|
189
|
+
|
190
|
+
proc{
|
191
|
+
@pristine_file.parse_role(line, 0)
|
192
|
+
}.should raise_error(SyntaxError)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "parsing a cbac_pristine file" do
|
197
|
+
|
198
|
+
it "should fail if a row number is used twice" do
|
199
|
+
pristine_file_lines = ["0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"]
|
200
|
+
pristine_file_lines.push("0:+:PrivilegeSet(log_in)ContextRole(everybody)")
|
201
|
+
|
202
|
+
File.stub!(:open).and_return(pristine_file_lines)
|
203
|
+
|
204
|
+
pristine_file = PristineFile.new("cbac.pristine")
|
205
|
+
|
206
|
+
proc{
|
207
|
+
pristine_file.parse
|
208
|
+
}.should raise_error(SyntaxError)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should fill the lines array with an object for each file line" do
|
212
|
+
pristine_file_lines = ["0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"]
|
213
|
+
pristine_file_lines.push("1:+:PrivilegeSet(log_in)ContextRole(everybody)")
|
214
|
+
pristine_file_lines.push("2:+:PrivilegeSet(log_out)ContextRole(logged_in_user)")
|
215
|
+
|
216
|
+
File.stub!(:open).and_return(pristine_file_lines)
|
217
|
+
|
218
|
+
pristine_file = PristineFile.new("cbac.pristine")
|
219
|
+
pristine_file.parse
|
220
|
+
|
221
|
+
pristine_file.permissions.length.should == pristine_file_lines.length
|
222
|
+
end
|
223
|
+
|
224
|
+
it "should not create an object for a comment line" do
|
225
|
+
pristine_file_lines = ["0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"]
|
226
|
+
pristine_file_lines.push("1:+:PrivilegeSet(log_in)ContextRole(everybody)")
|
227
|
+
pristine_file_lines.push("#this is a Ruby comment line")
|
228
|
+
|
229
|
+
File.stub!(:open).and_return(pristine_file_lines)
|
230
|
+
|
231
|
+
pristine_file = PristineFile.new("cbac.pristine")
|
232
|
+
pristine_file.parse
|
233
|
+
|
234
|
+
pristine_file.permissions.length.should == 2
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should also add a permission object if permission is revoked (operand - is used)" do
|
238
|
+
pristine_file_lines = ["0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"]
|
239
|
+
pristine_file_lines.push("1:+:PrivilegeSet(log_in)ContextRole(everybody)")
|
240
|
+
pristine_file_lines.push("2:-:PrivilegeSet(chat)ContextRole(logged_in_user)")
|
241
|
+
|
242
|
+
File.stub!(:open).and_return(pristine_file_lines)
|
243
|
+
|
244
|
+
pristine_file = PristineFile.new("cbac.pristine")
|
245
|
+
pristine_file.parse
|
246
|
+
|
247
|
+
pristine_file.permissions.length.should == 3
|
248
|
+
pristine_file.permissions[2].operation.should == '-'
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should fail if a permission is revoked which wasn't added before" do
|
252
|
+
pristine_file_lines = ["0:+:PrivilegeSet(chat)ContextRole(logged_in_user)"]
|
253
|
+
pristine_file_lines.push("1:+:PrivilegeSet(log_in)ContextRole(everybody)")
|
254
|
+
pristine_file_lines.push("2:-:PrivilegeSet(chat)ContextRole(everybody)")
|
255
|
+
|
256
|
+
File.stub!(:open).and_return(pristine_file_lines)
|
257
|
+
|
258
|
+
pristine_file = PristineFile.new("cbac.pristine")
|
259
|
+
proc{
|
260
|
+
pristine_file.parse
|
261
|
+
}.should raise_error(SyntaxError)
|
262
|
+
end
|
263
|
+
|
264
|
+
it "should fail if an x is used as an operand" do
|
265
|
+
pristine_file_lines = ["0:x:PrivilegeSet(chat)ContextRole(logged_in_user)"]
|
266
|
+
File.stub!(:open).and_return(pristine_file_lines)
|
267
|
+
|
268
|
+
pristine_file = PristineFile.new("cbac.pristine")
|
269
|
+
proc{
|
270
|
+
pristine_file.parse
|
271
|
+
}.should raise_error(NotImplementedError)
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should fail if an => is used as an operand" do
|
275
|
+
pristine_file_lines = ["0:=>:PrivilegeSet(chat)ContextRole(logged_in_user)"]
|
276
|
+
File.stub!(:open).and_return(pristine_file_lines)
|
277
|
+
|
278
|
+
pristine_file = PristineFile.new("cbac.pristine")
|
279
|
+
proc{
|
280
|
+
pristine_file.parse
|
281
|
+
}.should raise_error(NotImplementedError)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "permission set" do
|
286
|
+
before(:each) do
|
287
|
+
@context_role = PristineRole.new(:role_id => 0, :role_type => PristineRole.ROLE_TYPES[:context], :name => "logged_in_user")
|
288
|
+
@admin_role = PristineRole.new(:role_id => 1, :role_type => PristineRole.ROLE_TYPES[:admin],:name => "administrator")
|
289
|
+
@pristine_file = PristineFile.new("cbac.pristine")
|
290
|
+
end
|
291
|
+
|
292
|
+
it "should filter out the permissions which were revoked" do
|
293
|
+
permission_to_revoke = PristinePermission.new(:privilege_set_name => "chat", :pristine_role => @context_role, :operation => '+')
|
294
|
+
@pristine_file.permissions.push(permission_to_revoke)
|
295
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => permission_to_revoke.privilege_set_name, :pristine_role => permission_to_revoke.pristine_role, :operation => '-'))
|
296
|
+
|
297
|
+
@pristine_file.permission_set.should_not include(permission_to_revoke)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should not include the revoke permission itself" do
|
301
|
+
revoke_permission = PristinePermission.new(:privilege_set_name => "chat", :pristine_role => @context_role, :operation => '-')
|
302
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => revoke_permission.privilege_set_name, :pristine_role => revoke_permission.pristine_role, :operation => '+'))
|
303
|
+
@pristine_file.permissions.push(revoke_permission)
|
304
|
+
|
305
|
+
@pristine_file.permission_set.should_not include(revoke_permission)
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should contain the permission if it is re-applied" do
|
309
|
+
re_applied_permission = PristinePermission.new(:privilege_set_name => "chat", :pristine_role => @context_role, :operation => '+')
|
310
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => re_applied_permission.privilege_set_name, :pristine_role => re_applied_permission.pristine_role, :operation => '+'))
|
311
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => re_applied_permission.privilege_set_name, :pristine_role => re_applied_permission.pristine_role, :operation => '-'))
|
312
|
+
@pristine_file.permissions.push(re_applied_permission)
|
313
|
+
|
314
|
+
@pristine_file.permission_set.should include(re_applied_permission)
|
315
|
+
end
|
316
|
+
|
317
|
+
it "should raise an error if a permission is revoked which wasn't created before" do
|
318
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => "chat", :pristine_role => @context_role, :operation => '+'))
|
319
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => "login", :pristine_role => @context_role, :operation => '+'))
|
320
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => "blog_read", :pristine_role => @context_role, :operation => '-'))
|
321
|
+
@pristine_file.permissions.push(PristinePermission.new(:privilege_set_name => "update_blog", :pristine_role => @context_role, :operation => '+'))
|
322
|
+
|
323
|
+
proc {
|
324
|
+
@pristine_file.permission_set
|
325
|
+
}.should raise_error(ArgumentError)
|
326
|
+
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|