cbac 0.3.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. data/Manifest +60 -44
  2. data/Rakefile +2 -2
  3. data/cbac.gemspec +31 -31
  4. data/generators/cbac/cbac_generator.rb +27 -6
  5. data/generators/cbac/templates/config/cbac.pristine +2 -0
  6. data/generators/cbac/templates/controllers/permissions_controller.rb +21 -2
  7. data/generators/cbac/templates/controllers/upgrade_controller.rb +24 -0
  8. data/generators/cbac/templates/fixtures/cbac_memberships.yml +1 -1
  9. data/generators/cbac/templates/migrate/create_cbac_from_scratch.rb +59 -0
  10. data/generators/cbac/templates/migrate/create_cbac_upgrade_path.rb +31 -0
  11. data/generators/cbac/templates/tasks/cbac.rake +345 -0
  12. data/generators/cbac/templates/views/layouts/cbac.html.erb +2 -1
  13. data/generators/cbac/templates/views/memberships/index.html.erb +1 -1
  14. data/generators/cbac/templates/views/permissions/index.html.erb +14 -6
  15. data/generators/cbac/templates/views/upgrade/index.html.erb +32 -0
  16. data/lib/cbac.rb +23 -12
  17. data/lib/cbac/cbac_pristine/pristine.rb +133 -0
  18. data/lib/cbac/cbac_pristine/pristine_file.rb +158 -0
  19. data/lib/cbac/cbac_pristine/pristine_permission.rb +194 -0
  20. data/lib/cbac/cbac_pristine/pristine_role.rb +42 -0
  21. data/lib/cbac/known_permission.rb +14 -0
  22. data/lib/cbac/permission.rb +1 -1
  23. data/lib/cbac/privilege.rb +44 -0
  24. data/lib/cbac/privilege_set.rb +5 -4
  25. data/lib/cbac/privilege_set_record.rb +3 -1
  26. data/spec/cbac_pristine_file_spec.rb +329 -0
  27. data/spec/cbac_pristine_permission_spec.rb +358 -0
  28. data/spec/cbac_pristine_role_spec.rb +85 -0
  29. data/spec/rcov.opts +2 -0
  30. data/spec/spec.opts +4 -0
  31. data/spec/spec_helper.rb +12 -0
  32. data/tasks/cbac.rake +345 -19
  33. data/test/test_cbac_privilege.rb +54 -0
  34. metadata +43 -9
  35. 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
@@ -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 :privilegeset, :class_name => "Cbac::PrivilegeSetRecord", :foreign_key => "privilege_set_id"
5
+ belongs_to :privilege_set, :class_name => "Cbac::PrivilegeSetRecord", :foreign_key => "privilege_set_id"
6
6
  end
@@ -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.
@@ -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 privilegeset doesn't exist
22
- Cbac::PrivilegeSetRecord.create(:name => symbol.to_s) if Cbac::PrivilegeSetRecord.find(:first, :conditions => ["name = ?", symbol.to_s]).nil?
23
- record = Cbac::PrivilegeSetRecord.find(:first, :conditions => ["name = ?", symbol.to_s])
24
- record.comment = comment
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
@@ -1,5 +1,7 @@
1
1
  class Cbac::PrivilegeSetRecord < ActiveRecord::Base
2
2
  set_table_name "cbac_privilege_set"
3
3
 
4
- attr_accessor :comment
4
+ def set_comment(comment)
5
+ self.comment = comment if has_attribute?("comment")
6
+ end
5
7
  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