policy_machine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ # 0.0.2
2
+
3
+ * Fix: Operation sets now silently remove duplicates
4
+ * Transactional rollback available in active_record and in_memory
5
+ * Can now generate a list of all privileges a user has on an object with `#scoped_privileges`
6
+
7
+ # 0.0.1
8
+
9
+ * Initial open source release.
@@ -1,4 +1,4 @@
1
- Copyright 2013 YOURNAME
1
+ Copyright 2013 Medidata Solutions Worldwide
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
@@ -147,6 +147,25 @@ class PolicyMachine
147
147
  privileges
148
148
  end
149
149
 
150
+ ##
151
+ # Returns an array of all privileges encoded in this
152
+ # policy machine for the given user (attribute) on the given
153
+ # object (attribute).
154
+ #
155
+ # TODO: might make privilege a class of its own
156
+ def scoped_privileges(user_or_attribute, object_or_attribute)
157
+ if policy_machine_storage_adapter.respond_to?(:scoped_privileges)
158
+ policy_machine_storage_adapter.scoped_privileges(user_or_attribute.stored_pe, object_or_attribute.stored_pe).map do |op|
159
+ operation = PM::Operation.convert_stored_pe_to_pe(op, policy_machine_storage_adapter, PM::Operation)
160
+ [user_or_attribute, operation, object_or_attribute]
161
+ end
162
+ else
163
+ operations.grep(->operation{is_privilege?(user_or_attribute, operation, object_or_attribute)}) do |op|
164
+ [user_or_attribute, op, object_or_attribute]
165
+ end
166
+ end
167
+ end
168
+
150
169
  ##
151
170
  # Returns an array of all user_attributes a PM::User is assigned to,
152
171
  # directly or indirectly.
@@ -64,7 +64,7 @@ module PM
64
64
 
65
65
  new_assoc = pm_storage_adapter.add_association(
66
66
  user_attribute_pe.stored_pe,
67
- operation_set.map(&:stored_pe),
67
+ Set.new(operation_set.map(&:stored_pe)),
68
68
  object_attribute_pe.stored_pe,
69
69
  policy_machine_uuid
70
70
  )
@@ -106,6 +106,10 @@ module PM
106
106
  stored_pe.respond_to?(meth, include_private) || super
107
107
  end
108
108
 
109
+ def inspect
110
+ "#<#{self.class} #{unique_identifier}>"
111
+ end
112
+
109
113
  protected
110
114
  def allowed_assignee_classes
111
115
  raise "Must override this method in a subclass"
@@ -21,7 +21,9 @@ module PolicyMachineStorageAdapter
21
21
  has_many :assignments, foreign_key: :parent_id, dependent: :destroy
22
22
  has_many :children, through: :assignments, dependent: :destroy #this doesn't actually destroy the children, just the assignment
23
23
  has_many :transitive_closure, foreign_key: :ancestor_id
24
+ has_many :inverse_transitive_closure, class_name: :"PolicyMachineStorageAdapter::ActiveRecord::TransitiveClosure", foreign_key: :descendant_id
24
25
  has_many :descendants, through: :transitive_closure
26
+ has_many :ancestors, through: :inverse_transitive_closure
25
27
  attr_accessible :unique_identifier, :policy_machine_uuid, :extra_attributes
26
28
  attr_accessor :extra_attributes_hash
27
29
  before_save :serialize_extra_attributes_hash
@@ -294,8 +296,40 @@ module PolicyMachineStorageAdapter
294
296
  PolicyElement.transaction(&block)
295
297
  end
296
298
 
299
+ ## Optimized version of PolicyMachine#scoped_privileges
300
+ # Returns all operations the user has on the object
301
+ def scoped_privileges(user_or_attribute, object_or_attribute)
302
+ policy_classes_containing_object = policy_classes_for_object_attribute(object_or_attribute)
303
+ if policy_classes_containing_object.count < 2
304
+ scoped_privileges_single_policy_class(user_or_attribute, object_or_attribute)
305
+ else
306
+ scoped_privileges_multiple_policy_classes(user_or_attribute, object_or_attribute, policy_classes_containing_object)
307
+ end
308
+ end
309
+
297
310
  private
298
311
 
312
+ def scoped_privileges_single_policy_class(user_or_attribute, object_or_attribute)
313
+ associations = class_for_type('policy_element_association').where(
314
+ object_attribute_id: object_or_attribute.descendants | [object_or_attribute],
315
+ user_attribute_id: user_or_attribute.descendants | [user_or_attribute]
316
+ ).includes(:operations)
317
+
318
+ associations.flat_map(&:operations).uniq
319
+ end
320
+
321
+ def scoped_privileges_multiple_policy_classes(user_or_attribute, object_or_attribute, policy_classes_containing_object)
322
+ base_scope = class_for_type('policy_element_association').where(
323
+ object_attribute_id: object_or_attribute.descendants | [object_or_attribute],
324
+ user_attribute_id: user_or_attribute.descendants | [user_or_attribute]
325
+ )
326
+ operations_for_policy_classes = policy_classes_containing_object.map do |pc|
327
+ associations = base_scope.where(object_attribute_id: pc.ancestors).includes(:operations)
328
+ associations.flat_map(&:operations)
329
+ end
330
+ operations_for_policy_classes.inject(:&) || []
331
+ end
332
+
299
333
  def assert_persisted_policy_element(*arguments)
300
334
  arguments.each do |argument|
301
335
  raise ArgumentError, "expected policy elements, got #{argument}" unless argument.is_a?(PolicyElement)
@@ -163,6 +163,14 @@ module PolicyMachineStorageAdapter
163
163
  # all the block's changes to be rolled back. Should raise NotImplementedError if the
164
164
  # persistence layer does not support this.
165
165
  def transaction
166
+
167
+ end
168
+
169
+ ## Optimized version of PolicyMachine#scoped_privileges
170
+ # Return all operations the user has on the object
171
+ # Optional: only add this method if you can do it better than policy_machine.rb
172
+ def scoped_privileges(user_or_attribute, object_or_attribute)
173
+
166
174
  end
167
175
 
168
176
  end
@@ -1,11 +1,12 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "policy_machine"
3
- s.version = "0.0.1"
3
+ s.version = "0.0.2"
4
4
  s.summary = "Policy Machine!"
5
5
  s.description = "A ruby implementation of the Policy Machine authorization formalism."
6
6
  s.authors = ['Matthew Szenher', 'Aaron Weiner']
7
7
  s.email = s.authors.map{|name|name.sub(/(.).* (.*)/,'\1\2@mdsol.com')}
8
8
  s.homepage = 'https://github.com/mdsol/the_policy_machine'
9
+ s.license = 'MIT'
9
10
  s.files = `git ls-files`.split("\n")
10
11
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
11
12
  s.require_paths = ["lib"]
@@ -1,11 +1,12 @@
1
- require_relative '../test/test_helper.rb'
2
-
3
1
  require 'simplecov'
4
2
  SimpleCov.start do
5
3
  add_group 'lib', 'lib'
6
4
  add_filter 'spec'
5
+ add_filter 'test'
7
6
  end
8
7
 
8
+ require_relative '../test/test_helper.rb'
9
+
9
10
  require 'rspec'
10
11
  require 'debugger'
11
12
 
@@ -19,4 +19,18 @@ def assert_pm_privilege_expectations(actual_privileges, expected_privileges)
19
19
  end
20
20
 
21
21
  actual_privileges.count.should == expected_privileges.size
22
+ assert_pm_scoped_privilege_expectations
23
+ end
24
+
25
+ # Make sure all scoped_privileges calls behave as expected
26
+ def assert_pm_scoped_privilege_expectations
27
+ users_or_attributes = policy_machine.users | policy_machine.user_attributes
28
+ objects_or_attributes = policy_machine.objects | policy_machine.object_attributes
29
+ users_or_attributes.product(objects_or_attributes) do |u, o|
30
+ expected_scoped_privileges = policy_machine.operations.grep(->op{policy_machine.is_privilege?(u, op, o)}) do |op|
31
+ [u, op, o]
32
+ end
33
+ policy_machine.scoped_privileges(u,o).should =~ expected_scoped_privileges
34
+ end
35
+
22
36
  end
@@ -187,6 +187,12 @@ shared_examples "a policy machine" do
187
187
  it 'allows an association to be made between an existing user_attribute, operation set and object attribute (returns true)' do
188
188
  policy_machine.add_association(@user_attribute, @operation_set, @object_attribute).should be_true
189
189
  end
190
+
191
+ it 'handles non-unique operation sets' do
192
+ @operation_set << @operation1.dup
193
+ policy_machine.add_association(@user_attribute, @operation_set, @object_attribute).should be_true
194
+ end
195
+
190
196
  end
191
197
  end
192
198
 
@@ -2,7 +2,6 @@
2
2
  ENV["RAILS_ENV"] = "test"
3
3
 
4
4
  require File.expand_path("../dummy/config/environment.rb", __FILE__)
5
- require "rails/test_help"
6
5
 
7
6
  Rails.backtrace_cleaner.remove_silencers!
8
7
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: policy_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-10-28 00:00:00.000000000 Z
13
+ date: 2013-11-01 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -148,6 +148,7 @@ executables: []
148
148
  extensions: []
149
149
  extra_rdoc_files: []
150
150
  files:
151
+ - CHANGELOG.md
151
152
  - CONTRIBUTING.md
152
153
  - Gemfile
153
154
  - MIT-LICENSE
@@ -174,8 +175,8 @@ files:
174
175
  - spec/spec_helper.rb
175
176
  - spec/support/neography_helpers.rb
176
177
  - spec/support/policy_machine_helpers.rb
177
- - spec/support/shared_examples_policy_machine_spec.rb
178
- - spec/support/shared_examples_policy_machine_storage_adapter_spec.rb
178
+ - spec/support/shared_examples_policy_machine.rb
179
+ - spec/support/shared_examples_shared_examples_policy_machine_storage_adapter.rb
179
180
  - spec/support/shared_examples_storage_adapter_public_methods.rb
180
181
  - spec/support/storage_adapter_helpers.rb
181
182
  - test/dummy/Rakefile
@@ -204,7 +205,8 @@ files:
204
205
  - test/policy_machine_test.rb
205
206
  - test/test_helper.rb
206
207
  homepage: https://github.com/mdsol/the_policy_machine
207
- licenses: []
208
+ licenses:
209
+ - MIT
208
210
  post_install_message:
209
211
  rdoc_options: []
210
212
  require_paths:
@@ -238,8 +240,8 @@ test_files:
238
240
  - spec/spec_helper.rb
239
241
  - spec/support/neography_helpers.rb
240
242
  - spec/support/policy_machine_helpers.rb
241
- - spec/support/shared_examples_policy_machine_spec.rb
242
- - spec/support/shared_examples_policy_machine_storage_adapter_spec.rb
243
+ - spec/support/shared_examples_policy_machine.rb
244
+ - spec/support/shared_examples_shared_examples_policy_machine_storage_adapter.rb
243
245
  - spec/support/shared_examples_storage_adapter_public_methods.rb
244
246
  - spec/support/storage_adapter_helpers.rb
245
247
  - test/dummy/Rakefile