add helpers: current_group_permissions, groups_i_can(:read). refs #1415
authorTom Clegg <tom@clinicalfuture.com>
Mon, 18 Mar 2013 19:50:22 +0000 (12:50 -0700)
committerTom Clegg <tom@clinicalfuture.com>
Mon, 18 Mar 2013 19:50:22 +0000 (12:50 -0700)
app/helpers/application_helper.rb
app/models/link.rb

index df9ae848172e65c3414eade408210d09aaa23006..ba0c253ec63a652fb12751a65f0ac8676e851045 100644 (file)
@@ -1,7 +1,69 @@
 module ApplicationHelper
 
+  ALL_PERMISSIONS = {read: true, write: true, manage: true}
+
   def current_user
     controller.current_user
   end
 
+  def invalidate_permissions_cache
+    Rails.cache.delete_matched(/^groups_for_user_/)
+  end
+
+  def current_groups
+    return {} unless current_user
+    Rails.cache.fetch "groups_for_user_#{current_user.uuid}" do
+      permissions_from = {}
+      todo = {current_user.uuid => true}
+      done = {}
+      while !todo.empty?
+        lookup_uuids = todo.keys
+        lookup_uuids.each do |uuid| done[uuid] = true end
+        todo = {}
+        Link.where('tail_uuid in (?) and link_class = ? and head_kind = ?',
+                   lookup_uuids,
+                   'permission',
+                   'orvos#group').each do |link|
+          unless done.has_key? link.head_uuid
+            todo[link.head_uuid] = true
+          end
+          link_permissions = {}
+          case link.name
+          when 'can_read'
+            link_permissions = {read:true}
+          when 'can_write'
+            link_permissions = {read:true,write:true}
+          when 'can_manage'
+            link_permissions = ALL_PERMISSIONS
+          end
+          permissions_from[link.tail_uuid] ||= {}
+          permissions_from[link.tail_uuid][link.head_uuid] ||= {}
+          link_permissions.each do |k,v|
+            permissions_from[link.tail_uuid][link.head_uuid][k] ||= v
+          end
+        end
+      end
+      search_permissions(current_user.uuid, permissions_from)
+    end
+  end
+
+  protected
+
+  def search_permissions(start, graph, merged={}, upstream_mask=nil, upstream_path={})
+    nextpaths = graph[start]
+    return merged if !nextpaths
+    return merged if upstream_path.has_key? start
+    upstream_path[start] = true
+    upstream_mask ||= ALL_PERMISSIONS
+    nextpaths.each do |head, mask|
+      merged[head] ||= {}
+      mask.each do |k,v|
+        merged[head][k] ||= v if upstream_mask[k]
+      end
+      search_permissions(head, graph, merged, upstream_mask.select { |k,v| v && merged[head][k] }, upstream_path)
+    end
+    upstream_path.delete start
+    merged
+  end
+
 end
index 153f6c223b497ee725b834b707c69b7649e0f267..55aecdc7cc682c7988f2705b0ea6ceff3fc89f80 100644 (file)
@@ -5,6 +5,9 @@ class Link < OrvosModel
   serialize :properties, Hash
   before_create :permission_to_attach_to_objects
   before_update :permission_to_attach_to_objects
+  after_update :maybe_invalidate_permissions_cache
+  after_create :maybe_invalidate_permissions_cache
+  after_destroy :maybe_invalidate_permissions_cache
 
   attr_accessor :head
   attr_accessor :tail
@@ -58,4 +61,16 @@ class Link < OrvosModel
     # Default = deny.
     false
   end
+
+  def maybe_invalidate_permissions_cache
+    if self.link_class == 'permission'
+      # Clearing the entire permissions cache can generate many
+      # unnecessary queries if many active users are not affected by
+      # this change. In such cases it would be better to search cached
+      # permissions for head_uuid and tail_uuid, and invalidate the
+      # cache for only those users. (This would require a browseable
+      # cache.)
+      invalidate_permissions_cache
+    end
+  end
 end