add permission checks
authorTom Clegg <tom@clinicalfuture.com>
Thu, 24 Jan 2013 22:26:23 +0000 (14:26 -0800)
committerTom Clegg <tom@clinicalfuture.com>
Thu, 24 Jan 2013 22:26:23 +0000 (14:26 -0800)
app/controllers/application_controller.rb
app/controllers/users_controller.rb [new file with mode: 0644]
app/models/log.rb
app/models/metadatum.rb
app/models/orvos_model.rb
app/models/user.rb
config/routes.rb

index 893472c64a936f1f08613aadeceb8c3bfd4d8b1f..74e277f14ccda55e53fbb9eb9f7757b52dd09e54 100644 (file)
@@ -51,8 +51,8 @@ class ApplicationController < ActionController::Base
   def index
     @objects ||= model_class.
       joins("LEFT JOIN metadata permissions ON permissions.tail=#{table_name}.uuid AND permissions.head=#{model_class.sanitize current_user.uuid} AND permissions.metadata_class='permission' AND permissions.name='visible_to'").
-      where("#{table_name}.created_by_user=? OR permissions.head IS NOT NULL",
-            current_user.uuid)
+      where("#{table_name}.created_by_user=? OR #{table_name}.uuid=? OR permissions.head IS NOT NULL",
+            current_user.uuid, current_user.uuid)
     if params[:where]
       where = params[:where]
       where = JSON.parse(where) if where.is_a?(String)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
new file mode 100644 (file)
index 0000000..3e74dea
--- /dev/null
@@ -0,0 +1,2 @@
+class UsersController < ApplicationController
+end
index f3ff5ea99acf7aeec37484e6a7e01bec3cf42afe..44445d5fab935270c01be4f8a0393c81abca4478 100644 (file)
@@ -1,4 +1,4 @@
-class Log < ActiveRecord::Base
+class Log < OrvosModel
   include AssignUuid
   include KindAndEtag
   include CommonApiTemplate
index bfb58f258590b7aa5544831d1cd9a3e5f84c2afa..098e3225eb849f6f0c50922297e818cc1e35c762 100644 (file)
@@ -3,6 +3,8 @@ class Metadatum < OrvosModel
   include KindAndEtag
   include CommonApiTemplate
   serialize :info, Hash
+  before_create :permission_to_attach_to_objects
+  before_update :permission_to_attach_to_objects
 
   api_accessible :superuser, :extend => :common do |t|
     t.add :tail_kind
@@ -18,4 +20,37 @@ class Metadatum < OrvosModel
     @info ||= Hash.new
     super
   end
+
+  protected
+
+  def permission_to_attach_to_objects
+    # Anonymous users cannot write metadata
+    return false if !current_user
+
+    # All users can write metadata that doesn't affect permissions
+    return true if self.metadata_class != 'permission'
+
+    # Administrators can grant permissions
+    return true if current_user.is_admin
+
+    # All users can grant permissions on objects they created themselves
+    head_obj = self.class.
+      kind_class(self.head_kind).
+      where('uuid=?',head_uuid).
+      first
+    if head_obj
+      return true if head_obj.created_by_user == current_user.uuid
+    end
+
+    # Users with "can_manage" permission on an object can grant
+    # permissions on that object
+    has_manage_permission = self.class.
+      where('metadata_class=? AND name=? AND tail=? AND head=?',
+            'permission', 'can_manage', current_user.uuid, self.head).
+      count > 0
+    return true if has_manage_permission
+
+    # Default = deny.
+    false
+  end
 end
index e288ebf0d862684e58be52deb03d36480759e74a..fb799dc718986428486920b77de92af290d22fc5 100644 (file)
@@ -10,6 +10,7 @@ class OrvosModel < ActiveRecord::Base
   attr_protected :modified_by_client
   attr_protected :modified_at
   before_create :initialize_created_by_fields
+  before_update :permission_to_update
   before_update :update_modified_by_fields
 
   def self.kind_class(kind)
@@ -31,6 +32,17 @@ class OrvosModel < ActiveRecord::Base
 
   protected
 
+  def permission_to_update
+    return false unless current_user
+    self.created_by_user == current_user.uuid or
+      current_user.is_admin or
+      current_user.uuid == self.uuid or
+      Metadatum.where(metadata_class: 'permission',
+                      name: 'can_write',
+                      tail: self.uuid,
+                      head: current_user.uuid).count > 0
+  end
+
   def update_modified_by_fields
     if self.changed?
       self.modified_at = Time.now
index f374c086a55ab821b86c4b5dc495942b8d2014d1..a09a44756a64df5393f795d0aadcf777b0232cfe 100644 (file)
@@ -1,9 +1,10 @@
-class User < ActiveRecord::Base
+class User < OrvosModel
   include AssignUuid
   include KindAndEtag
   include CommonApiTemplate
   serialize :prefs, Hash
   has_many :api_client_authorizations
+  before_update :prevent_privilege_escalation
 
   api_accessible :superuser, :extend => :common do |t|
     t.add :email
@@ -19,4 +20,16 @@ class User < ActiveRecord::Base
     "#{first_name} #{last_name}"
   end
 
+  protected
+
+  def prevent_privilege_escalation
+    if self.is_admin_changed?
+      if current_user.uuid == self.uuid
+        if self.is_admin != self.is_admin_was
+          self.is_admin = self.is_admin_was
+        end
+      end
+    end
+    true
+  end
 end
index b5e09253858c970cc213b61d52ce834e6b98b66c..b592c58a071f3a37a96143dde2f11fd0ab115c26 100644 (file)
@@ -1,8 +1,6 @@
 Server::Application.routes.draw do
   resources :api_client_authorizations
-
   resources :api_clients
-
   resources :logs
   resources :projects
   resources :specimens
@@ -80,6 +78,7 @@ Server::Application.routes.draw do
       resources :specimens
       resources :projects
       resources :logs
+      resources :users
       match '/schema' => 'schema#show'
       match '/nodes/:uuid/ping' => 'nodes#ping', :as => :ping_node
       match '/metadata/:tail_kind/:tail' => 'metadata#index'