policy_machine 0.0.1 → 0.0.2

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.
@@ -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