cbac 0.3.1 → 0.5.1

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