myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [2049] branches/authorization_new: Refactored cod


From: noreply
Subject: [myexperiment-hackers] [2049] branches/authorization_new: Refactored code in "is_authorized" module to enable proper calling of it from different places around the codebase .
Date: Thu, 15 Jan 2009 10:47:15 -0500 (EST)

Revision
2049
Author
alekses6
Date
2009-01-15 10:47:14 -0500 (Thu, 15 Jan 2009)

Log Message

Refactored code in "is_authorized" module to enable proper calling of it from different places around the codebase.

Module renamed to "authorization".

Modified Paths

Added Paths

Removed Paths

Diff

Modified: branches/authorization_new/config/environment.rb (2048 => 2049)


--- branches/authorization_new/config/environment.rb	2009-01-13 18:33:46 UTC (rev 2048)
+++ branches/authorization_new/config/environment.rb	2009-01-15 15:47:14 UTC (rev 2049)
@@ -67,7 +67,7 @@
 # SMTP configuration
 
 require 'smtp_tls'
-require 'is_authorized'
+require 'authorization'
 
 load 'config/environment_private.rb' if FileTest.exist?('config/environment_private.rb')
 

Copied: branches/authorization_new/lib/authorization.rb (from rev 2048, branches/authorization_new/lib/is_authorized.rb) (0 => 2049)


--- branches/authorization_new/lib/authorization.rb	                        (rev 0)
+++ branches/authorization_new/lib/authorization.rb	2009-01-15 15:47:14 UTC (rev 2049)
@@ -0,0 +1,405 @@
+# myExperiment: lib/is_authorized.rb
+# 
+# Copyright (c) 2007 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+module Authorization
+
+  # check the relevant permissions based on 'action' string
+  
+  # 1) action_name - name of the action that is about to happen with the "thing"
+  # 2) thing_type - class name of the thing that needs to be authorized
+  # 3) thing - this is supposed to be an instance of the thing to be authorized, but
+  #            can also accept an ID (since we have the type, too - "thing_type")
+  # 4) user - can be either user instance or the ID (NIL or 0 to indicate anonymous/not logged in user)
+  #
+  # Note: there is no method overloading in Ruby and it's a good idea to have a default "nil" value for "user";
+  #       this leaves no other choice as to have (sometimes) redundant "thing_type" parameter.
+  def Authorization.is_authorized?(action_name, thing_type, thing, user=nil)
+    thing_instance = nil
+    user_instance = nil
+    user_id = nil # if this value will not get updated by input parameters - user will be treated as anonymous
+
+    # ***************************************
+    #      Pre-checks on the Parameters
+    # ***************************************
+
+    # check first if the action that is being executed is known - not authorized otherwise
+    action = ""
+    return false unless action
+    
+    # if thing_type or thing itself are unknown - don't authorise the action
+    return false if (thing_type.blank? || thing.blank?)
+    
+    
+    
+    # some value for "thing" supplied - assume that the object exists; check if it is an instance or the ID
+    if thing.kind_of?(Fixnum)
+      thing_id = thing
+    else
+      thing_instance = thing
+      thing_id = thing.id
+    end
+    
+    if user.kind_of?(User)
+      user_instance = user
+      user_id = user.id
+    elsif user == 0
+      # "Authenticated System" sets current_user to 0 if not logged in (i.e. anonymous user)
+      user_id = nil
+    elsif user.nil? || user.kind_of?(Fixnum)
+      # anonymous user OR only id of the user, not an instance was provided;
+      user_id = user
+    end
+    
+
+    # ***************************************
+    #      Actual Authorization Begins 
+    # ***************************************
+
+    # if (thing_type, ID) pair was supplied instead of a "thing" instance,
+    # need to find the object that needs to be authorized first;
+    # (only do this for object types that are known to require authorization)
+    #
+    # this is required to get "policy_id" for policy-based aurhorized objects (like workflows / blobs / packs)
+    # and to get objects themself for other object types (networks, experiments, jobs, tavernaenactors, runners)
+    if thing_instance.nil? && ["Workflow", "Blob", "Pack", "Network", "Experiment", "Job", "TavernaEnactor", "Runner"].include?(thing_type)
+      thing_instance = find_thing(thing_type, thing_id)
+      
+      unless thing_instance
+        # search didn't yield any results - the "thing" wasn't found; can't authorize unknown objects
+        logger.error("UNEXPECTED ERROR - Couldn't find object to be authorized:(#{thing_type}, #{thing_id}); action: #{action_name}; user: #{user_id}")
+        return false
+      end
+    end
+    
+
+    # initially not authorized, so if all tests fail -
+    # safe result of being not authorized will get returned 
+    is_authorized = false
+    
+    case thing_type
+      when "Workflow", "Blob", "Pack"
+        unless user_id.nil?
+          # access is authorized and no further checks required in two cases:
+          # ** user is the owner of the "thing"
+          return true if is_owner?(user_id, thing_instance)
+          
+          # ** user is admin of the policy associated with the "thing"
+          #    (this means that the user might not have uploaded the "thing", but
+          #     is the one managing the access permissions for it)
+          #
+          #    it's fine if policy will not be found at this step - default one will get
+          #    used further when required
+          policy_id = thing_instance.policy_id
+          policy = get_policy(policy_id)
+          return false unless policy # if policy wasn't found (and default one couldn't be applied) - error; not authorized
+          return true if is_policy_admin?(policy, user_id)
+          
+          
+          # only owners / policy admins are allowed to perform actions categorized as "destroy";
+          # hence "destroy" actions are not authorized below this point
+          return false if action == "destroy"
+          
+          
+          # user is not the owner/admin of the object; action is not of "destroy" class;
+          # next thing - obtain all the permissions that are relevant to the user
+          # (start with individual user permissions; group permissions will only
+          #  be considered if that is required further on)
+          user_permissions = get_user_permissions(user_id, policy_id)
+          
+          # individual user permissions override any other settings;
+          # if several of these are found (which shouldn't be the case),
+          # all are considered, but the one with "highest" access right is
+          # used to make final decision -- that is if at least one of the
+          # user permissions allows to make the action, it will be allowed;
+          # likewise, if none of the permissions allow the action it will
+          # not be allowed
+          unless user_permissions.empty?
+            authorized_by_user_permissions = false
+            user_permissions.each do |p|
+              authorized_by_user_permissions = true if p.attributes["#{action}"]
+            end
+            return authorized_by_user_permissions
+          end
+          
+          
+          # no user permissions found, need to check what is allowed by policy
+          # (if no policy was found, default policy is in use instead)
+          authorized_by_policy = false
+          authorized_by_policy = authorized_by_policy?(policy, thing_instance, action, user_id)
+          return true if authorized_by_policy
+          
+
+          # not authorized by policy, check the group permissions -- the ones
+          # attached to "thing's" policy and belonging to the groups, where
+          # "user" is a member or admin of;
+          #
+          # these cannot limit what is allowed by policy settings, only give more access rights 
+          authorized_by_group_permissions = false
+          group_permissions = get_group_permissions(policy_id)
+          
+          unless group_permissions.empty?
+            group_permissions.each do |p|
+              # check if this permission is applicable to the "user"
+              if p.attributes["#{action}"] && (is_network_member?(user_id, p.contributor_id) || is_network_admin?(user_id, p.contributor_id))
+                authorized_by_group_permissions = true
+                break
+              end
+            end
+            return authorized_by_group_permissions if authorized_by_group_permissions
+          end
+          
+          # user permissions, policy settings and group permissions didn't give the
+          # positive result - decline the action request
+          return false
+        
+        else
+          # this is for cases where trying to authorize anonymous users;
+          # the only possible check - on public policy settings:
+          policy_id = thing_instance.policy_id
+          policy = get_policy(policy_id)
+          return false unless policy # if policy wasn't found (and default one couldn't be applied) - error; not authorized
+          
+          return authorized_by_policy?(policy, thing_instance, action, nil)
+        end
+        
+      when "Network"
+        # TODO
+        # add checks to allow only admin to edit / delete / accept memberships / etc
+        is_authorized = true
+        
+      when "Experiment", "Job", "TavernaEnactor", "Runner"
+        # user instance is absolutely required for this - so find it, if not yet available
+        unless user_instance
+          user_instance = get_user(user_id)
+        end
+        
+        # "thing_instance" was already found previously;
+        # neither of these "thing" types uses policy-based authorization, hence use
+        # the existing <thing>.authorized?() method
+        #
+        # "action_name" used to work with original action name, rather than classification made inside the module
+        is_authorized = thing_instance.authorized?(action_name, user)
+      
+      else
+        # don't recognise the kind of "thing" that is being authorized, so
+        # we don't specifically know that it needs to be blocked;
+        # therefore, allow any actions on it
+        is_authorized = true
+    end
+    
+    return is_authorized
+    
+  end
+
+
+  private
+
+  def Authorization.categorize_action(action_name)
+    case action_name
+      when 'show', 'index', 'view', 'search', 'favourite', 'favourite_delete', 'comment', 'comment_delete', 'comments', 'comments_timeline', 'rate', 'tag',  'items', 'statistics', 'tag_suggestions'
+        action = ''
+      when 'new', 'create', 'update', 'edit', 'new_version', 'create_version', 'destroy_version', 'edit_version', 'update_version', 'new_item', 'create_item', 'edit_item', 'update_item', 'quick_add', 'resolve_link'
+        action = ''
+      when 'download', 'named_download', 'launch', 'submit_job'
+        action = ''
+      when 'destroy', 'destroy_item'
+        action = ''
+      else
+        # unknown action
+        action = ""
+    end
+    
+    return action
+  end
+
+  # check if the DB holds entry for the "thing" to be authorized 
+  def Authorization.find_thing(thing_type, thing_id)
+    found_instance = nil
+    
+    begin
+      case thing_type
+        when "Workflow", "Blob", "Pack"
+          # "find_by_sql" works faster itself PLUS only a subset of all fields is selected;
+          # this is the most frequent query to be executed, hence needs to be optimised
+          found_instance = Contribution.find_by_sql "SELECT contributor_id, contributor_type, policy_id FROM contributions WHERE contributable_id=#{thing_id} AND contributable_type='#{thing_type}'"
+          found_instance = nil if found_instance.empty? # if nothing was found
+        when "Network"
+          found_instance = Network.find(thing_id)
+        when "Experiment"
+          found_instance = Experiment.find(thing_id)
+        when "Job"
+          found_instance = Job.find(thing_id)
+        when "TavernaEnactor"
+          found_instance = TavernaEnactor.find(thing_id)
+        when "Runner"
+          # the line below doesn't have a typo - "runners" should really be searched in "TavernaEnactor" model
+          found_instance = TavernaEnactor.find(thing_id)
+      end
+    rescue ActiveRecord::RecordNotFound
+      # do nothing; makes sure that app won't crash when the required object is not found;
+      # the method will return "nil" anyway, so no need to take any further actions here
+    end
+    
+    return found_instance
+  end
+
+
+  # checks if "user" is owner of the "thing"
+  def Authorization.is_owner?(user_id, thing_instance)
+    is_authorized = false
+
+    # if owner of the "thing" is the "user" then the "user" is authorized
+    if thing_instance.contributor_type == 'User' && thing_instance.contributor_id == user_id
+      is_authorized = true
+    elsif thing_instance.contributor_type == 'Network'
+      is_authorized = is_network_admin?(user_id, thing_instance.contributor_id)
+    end
+
+    return is_authorized
+  end
+  
+  # checks if "user" is admin of the policy associated with the "thing"
+  def Authorization.is_policy_admin(policy, user_id)
+    # if anonymous user or no policy provided - definitely not policy admin
+    return false unless (policy && user_id)
+    
+    return(policy.contributor_type == 'User' && policy.contributor_id == user_id)
+  end
+  
+  
+  def Authorization.is_network_admin?(user_id, network_id)
+    # checks if there is a network with ID(network_id) which has admin with ID(user_id) -
+    # if found, user with ID(user_id) is an admin of that network 
+    network = Network.find_by_sql "SELECT user_id FROM networks WHERE id=#{network_id} AND user_id=#{user_id}"
+    return(!network.blank?)
+  end
+  
+  
+  def Authorization.is_network_member?(user_id, network_id)
+    # checks if user with ID(user_id) is a member of the group ID(network_id)
+    membership = Membership.find_by_sql "SELECT id FROM memberships WHERE user_id=#{user_id} AND network_id=#{network_id} AND user_established_at IS NOT NULL AND network_established_at IS NOT NULL"
+    return(!membership.blank?)
+  end
+  
+  
+  # checks if two users are friends
+  def Authorization.is_friend?(contributor_id, user_id)
+    friendship = Friendship.find_by_sql "SELECT id FROM friendships WHERE (user_id=#{contributor_id} AND friend_id=#{user_id}) OR (user_id=#{user_id} AND friend_id=#{contributor_id}) AND accepted_at IS NOT NULL"
+    return(!friendship.blank?)
+  end
+  
+  
+  # gets the user object from the user_id;
+  # used by is_authorized when calling model.authorized? method for classes that don't use policy-based authorization
+  def Authorization.get_user(user_id)
+    return nil if user_id == 0
+    
+    begin
+      user = User.find(:first, :conditions => ["id = ?", user_id])
+      return user
+    rescue ActiveRecord::RecordNotFound
+      # user not found, "nil" for anonymous user will be returned
+      return nil
+    end
+  end
+  
+  
+  # query database for relevant fields in policies table
+  def Authorization.get_policy(policy_id)
+    select_string = 'id, contributor_id, contributor_type, share_mode, update_mode'
+    policy_array = Policy.find_by_sql "SELECT #{select_string} FROM policies WHERE policies.id=#{policy_id}"
+    
+    if policy_array.blank?
+      # an unlikely event that contribution doesn't have a policy - need to use
+      # default one; "owner" of the contribution will be treated as policy admin
+      #
+      # the following is slow, but given the very rare execution can be kept
+      begin
+        # thing_instance is Contribution, so thing_instance.contributor is the original uploader == owner of the item
+        contributor = eval("#{thing_instance.contributor_type}.find(#{thing_instance.contributor_id})")
+        policy = Policy._default(contributor) 
+      rescue ActiveRecord::RecordNotFound => e
+        # original contributor not found, but the Contribution entry still exists -
+        # this is an error in associations then, because all dependent items
+        # should have been deleted along with the contributor entry; log the error
+        logger.error("UNEXPECTED ERROR - Contributor object missing for an existing contribution: (#{thing_instance.class.name}, #{thing_instance.id})")
+        logger.error("EXCEPTION:" + e)
+        return nil
+      end
+    else
+      policy = policy_array[0]
+    end
+    
+    # if no policy is found (even no default one) --> nil will be returned
+    return policy
+  end
+  
+  
+  # get all user permissions related to policy for the "thing" for "user"
+  def Authorization.get_user_permissions(user_id, policy_id)
+    select_string = 'contributor_id, download, edit, view'
+    Permission.find_by_sql "SELECT #{select_string} FROM permissions WHERE policy_id=#{policy_id} AND contributor_type='User' AND contributor_id=#{user_id}"
+  end
+  
+  
+  # get all group permissions related to policy for the "thing"
+  def Authorization.get_group_permissions(policy_id)
+    select_string = 'contributor_id, download, edit, view'
+    Permission.find_by_sql "SELECT #{select_string} FROM permissions WHERE policy_id=#{policy_id} AND contributor_type='Network'"
+  end
+  
+
+  # checks whether "user" is authorized for "action" on "thing"
+  def Authorization.authorized_by_policy?(policy, thing_instance, action, user_id)
+    is_authorized = false
+    
+    # NB! currently myExperiment won't support objects owned by entities other than users
+    # (especially, policy checks are not agreed for these cases - however, owner tests and
+    #  permission tests are possible and will be carried out)
+    unless thing_instance.contributor_type == "User"
+      return false
+    end
+    
+    ####################################################################################
+    #
+    # For details on what each sharing / updating mode means, see the wiki:
+    # http://wiki.myexperiment.org/index.php/Developer:Ownership_Sharing_and_Permissions
+    #
+    ####################################################################################
+    share_mode = policy.share_mode
+    update_mode = policy.update_mode
+
+    case action
+      when 'view'
+        if (share_mode == 0 || share_mode == 1 || share_mode == 2)
+          # if share mode is 0,1,2, anyone can view
+          is_authorized = true
+        elsif !user_id.nil? && (share_mode == 3 || share_mode == 4 || update_mode == 1)
+          # if share mode is 3,4, friends can view; AND friends can also view if update mode is 1 -- due to cascading permissions
+          is_authorized = is_friend?(thing_instance.contributor_id, user_id)
+        end
+        
+      when 'download'
+        if (share_mode == 0)
+          # if share mode is 0, anyone can download
+          is_authorized = true
+        elsif !user_id.nil? && (share_mode == 1 || share_mode == 3 || update_mode == 1)
+          # if share mode is 1,3, friends can download; AND if update mode is 1, friends can download too -- due to cascading permissions
+          is_authorized = is_friend?(thing_instance.contributor_id, user_id)
+        end
+      when 'edit'
+        if (update_mode == 0 && share_mode == 0)
+          # if update mode is 0, anyone with view & download permissions can edit (sharing mode 0 for anonymous)
+          is_authorized = true
+        elsif !user_id.nil? && (update_mode == 1 || (update_mode == 0 && (share_mode == 1 || share_mode == 3)))
+          # if update mode is 1, friends can edit; AND if update mode is 0 and friends have view & download permissions, they can edit
+          is_authorized = is_friend?(thing_instance.contributor_id, user_id)
+        end
+    end
+
+    return is_authorized
+  end
+
+end

Property changes: branches/authorization_new/lib/authorization.rb


Added: svn:mergeinfo

Deleted: branches/authorization_new/lib/is_authorized.rb (2048 => 2049)


--- branches/authorization_new/lib/is_authorized.rb	2009-01-13 18:33:46 UTC (rev 2048)
+++ branches/authorization_new/lib/is_authorized.rb	2009-01-15 15:47:14 UTC (rev 2049)
@@ -1,405 +0,0 @@
-# myExperiment: lib/is_authorized.rb
-# 
-# Copyright (c) 2007 University of Manchester and the University of Southampton.
-# See license.txt for details.
-
-module IsAuthorized
-
-  # check the relevant permissions based on 'action' string
-  
-  # 1) action_name - name of the action that is about to happen with the "thing"
-  # 2) thing_type - class name of the thing that needs to be authorized
-  # 3) thing - this is supposed to be an instance of the thing to be authorized, but
-  #            can also accept an ID (since we have the type, too - "thing_type")
-  # 4) user - can be either user instance or the ID (NIL or 0 to indicate anonymous/not logged in user)
-  #
-  # Note: there is no method overloading in Ruby and it's a good idea to have a default "nil" value for "user";
-  #       this leaves no other choice as to have (sometimes) redundant "thing_type" parameter.
-  def is_authorized?(action_name, thing_type, thing, user=nil)
-    thing_instance = nil
-    user_instance = nil
-    user_id = nil # if this value will not get updated by input parameters - user will be treated as anonymous
-
-    # ***************************************
-    #      Pre-checks on the Parameters
-    # ***************************************
-
-    # check first if the action that is being executed is known - not authorized otherwise
-    action = ""
-    return false unless action
-    
-    # if thing_type or thing itself are unknown - don't authorise the action
-    return false if (thing_type.blank? || thing.blank?)
-    
-    
-    
-    # some value for "thing" supplied - assume that the object exists; check if it is an instance or the ID
-    if thing.kind_of?(Fixnum)
-      thing_id = thing
-    else
-      thing_instance = thing
-      thing_id = thing.id
-    end
-    
-    if user.kind_of?(User)
-      user_instance = user
-      user_id = user.id
-    elsif user == 0
-      # "Authenticated System" sets current_user to 0 if not logged in (i.e. anonymous user)
-      user_id = nil
-    elsif user.nil? || user.kind_of?(Fixnum)
-      # anonymous user OR only id of the user, not an instance was provided;
-      user_id = user
-    end
-    
-
-    # ***************************************
-    #      Actual Authorization Begins 
-    # ***************************************
-
-    # if (thing_type, ID) pair was supplied instead of a "thing" instance,
-    # need to find the object that needs to be authorized first;
-    # (only do this for object types that are known to require authorization)
-    #
-    # this is required to get "policy_id" for policy-based aurhorized objects (like workflows / blobs / packs)
-    # and to get objects themself for other object types (networks, experiments, jobs, tavernaenactors, runners)
-    if thing_instance.nil? && ["Workflow", "Blob", "Pack", "Network", "Experiment", "Job", "TavernaEnactor", "Runner"].include?(thing_type)
-      thing_instance = find_thing(thing_type, thing_id)
-      
-      unless thing_instance
-        # search didn't yield any results - the "thing" wasn't found; can't authorize unknown objects
-        logger.error("UNEXPECTED ERROR - Couldn't find object to be authorized:(#{thing_type}, #{thing_id}); action: #{action_name}; user: #{user_id}")
-        return false
-      end
-    end
-    
-
-    # initially not authorized, so if all tests fail -
-    # safe result of being not authorized will get returned 
-    is_authorized = false
-    
-    case thing_type
-      when "Workflow", "Blob", "Pack"
-        unless user_id.nil?
-          # access is authorized and no further checks required in two cases:
-          # ** user is the owner of the "thing"
-          return true if is_owner?(user_id, thing_instance)
-          
-          # ** user is admin of the policy associated with the "thing"
-          #    (this means that the user might not have uploaded the "thing", but
-          #     is the one managing the access permissions for it)
-          #
-          #    it's fine if policy will not be found at this step - default one will get
-          #    used further when required
-          policy_id = thing_instance.policy_id
-          policy = get_policy(policy_id)
-          return false unless policy # if policy wasn't found (and default one couldn't be applied) - error; not authorized
-          return true if is_policy_admin?(policy, user_id)
-          
-          
-          # only owners / policy admins are allowed to perform actions categorized as "destroy";
-          # hence "destroy" actions are not authorized below this point
-          return false if action == "destroy"
-          
-          
-          # user is not the owner/admin of the object; action is not of "destroy" class;
-          # next thing - obtain all the permissions that are relevant to the user
-          # (start with individual user permissions; group permissions will only
-          #  be considered if that is required further on)
-          user_permissions = get_user_permissions(user_id, policy_id)
-          
-          # individual user permissions override any other settings;
-          # if several of these are found (which shouldn't be the case),
-          # all are considered, but the one with "highest" access right is
-          # used to make final decision -- that is if at least one of the
-          # user permissions allows to make the action, it will be allowed;
-          # likewise, if none of the permissions allow the action it will
-          # not be allowed
-          unless user_permissions.empty?
-            authorized_by_user_permissions = false
-            user_permissions.each do |p|
-              authorized_by_user_permissions = true if p.attributes["#{action}"]
-            end
-            return authorized_by_user_permissions
-          end
-          
-          
-          # no user permissions found, need to check what is allowed by policy
-          # (if no policy was found, default policy is in use instead)
-          authorized_by_policy = false
-          authorized_by_policy = authorized_by_policy?(policy, thing_instance, action, user_id)
-          return true if authorized_by_policy
-          
-
-          # not authorized by policy, check the group permissions -- the ones
-          # attached to "thing's" policy and belonging to the groups, where
-          # "user" is a member or admin of;
-          #
-          # these cannot limit what is allowed by policy settings, only give more access rights 
-          authorized_by_group_permissions = false
-          group_permissions = get_group_permissions(policy_id)
-          
-          unless group_permissions.empty?
-            group_permissions.each do |p|
-              # check if this permission is applicable to the "user"
-              if p.attributes["#{action}"] && (is_network_member?(user_id, p.contributor_id) || is_network_admin?(user_id, p.contributor_id))
-                authorized_by_group_permissions = true
-                break
-              end
-            end
-            return authorized_by_group_permissions if authorized_by_group_permissions
-          end
-          
-          # user permissions, policy settings and group permissions didn't give the
-          # positive result - decline the action request
-          return false
-        
-        else
-          # this is for cases where trying to authorize anonymous users;
-          # the only possible check - on public policy settings:
-          policy_id = thing_instance.policy_id
-          policy = get_policy(policy_id)
-          return false unless policy # if policy wasn't found (and default one couldn't be applied) - error; not authorized
-          
-          return authorized_by_policy?(policy, thing_instance, action, nil)
-        end
-        
-      when "Network"
-        # TODO
-        # add checks to allow only admin to edit / delete / accept memberships / etc
-        is_authorized = true
-        
-      when "Experiment", "Job", "TavernaEnactor", "Runner"
-        # user instance is absolutely required for this - so find it, if not yet available
-        unless user_instance
-          user_instance = get_user(user_id)
-        end
-        
-        # "thing_instance" was already found previously;
-        # neither of these "thing" types uses policy-based authorization, hence use
-        # the existing <thing>.authorized?() method
-        #
-        # "action_name" used to work with original action name, rather than classification made inside the module
-        is_authorized = thing_instance.authorized?(action_name, user)
-      
-      else
-        # don't recognise the kind of "thing" that is being authorized, so
-        # we don't specifically know that it needs to be blocked;
-        # therefore, allow any actions on it
-        is_authorized = true
-    end
-    
-    return is_authorized
-    
-  end
-
-
-  private
-
-  def categorize_action(action_name)
-    case action_name
-      when 'show', 'index', 'view', 'search', 'favourite', 'favourite_delete', 'comment', 'comment_delete', 'comments', 'comments_timeline', 'rate', 'tag',  'items', 'statistics', 'tag_suggestions'
-        action = ''
-      when 'new', 'create', 'update', 'edit', 'new_version', 'create_version', 'destroy_version', 'edit_version', 'update_version', 'new_item', 'create_item', 'edit_item', 'update_item', 'quick_add', 'resolve_link'
-        action = ''
-      when 'download', 'named_download', 'launch', 'submit_job'
-        action = ''
-      when 'destroy', 'destroy_item'
-        action = ''
-      else
-        # unknown action
-        action = ""
-    end
-    
-    return action
-  end
-
-  # check if the DB holds entry for the "thing" to be authorized 
-  def find_thing(thing_type, thing_id)
-    found_instance = nil
-    
-    begin
-      case thing_type
-        when "Workflow", "Blob", "Pack"
-          # "find_by_sql" works faster itself PLUS only a subset of all fields is selected;
-          # this is the most frequent query to be executed, hence needs to be optimised
-          found_instance = Contribution.find_by_sql "SELECT contributor_id, contributor_type, policy_id FROM contributions WHERE contributable_id=#{thing_id} AND contributable_type='#{thing_type}'"
-          found_instance = nil if found_instance.empty? # if nothing was found
-        when "Network"
-          found_instance = Network.find(thing_id)
-        when "Experiment"
-          found_instance = Experiment.find(thing_id)
-        when "Job"
-          found_instance = Job.find(thing_id)
-        when "TavernaEnactor"
-          found_instance = TavernaEnactor.find(thing_id)
-        when "Runner"
-          # the line below doesn't have a typo - "runners" should really be searched in "TavernaEnactor" model
-          found_instance = TavernaEnactor.find(thing_id)
-      end
-    rescue ActiveRecord::RecordNotFound
-      # do nothing; makes sure that app won't crash when the required object is not found;
-      # the method will return "nil" anyway, so no need to take any further actions here
-    end
-    
-    return found_instance
-  end
-
-
-  # checks if "user" is owner of the "thing"
-  def is_owner?(user_id, thing_instance)
-    is_authorized = false
-
-    # if owner of the "thing" is the "user" then the "user" is authorized
-    if thing_instance.contributor_type == 'User' && thing_instance.contributor_id == user_id
-      is_authorized = true
-    elsif thing_instance.contributor_type == 'Network'
-      is_authorized = is_network_admin?(user_id, thing_instance.contributor_id)
-    end
-
-    return is_authorized
-  end
-  
-  # checks if "user" is admin of the policy associated with the "thing"
-  def is_policy_admin(policy, user_id)
-    # if anonymous user or no policy provided - definitely not policy admin
-    return false unless (policy && user_id)
-    
-    return(policy.contributor_type == 'User' && policy.contributor_id == user_id)
-  end
-  
-  
-  def is_network_admin?(user_id, network_id)
-    # checks if there is a network with ID(network_id) which has admin with ID(user_id) -
-    # if found, user with ID(user_id) is an admin of that network 
-    network = Network.find_by_sql "SELECT user_id FROM networks WHERE id=#{network_id} AND user_id=#{user_id}"
-    return(!network.blank?)
-  end
-  
-  
-  def is_network_member?(user_id, network_id)
-    # checks if user with ID(user_id) is a member of the group ID(network_id)
-    membership = Membership.find_by_sql "SELECT id FROM memberships WHERE user_id=#{user_id} AND network_id=#{network_id} AND user_established_at IS NOT NULL AND network_established_at IS NOT NULL"
-    return(!membership.blank?)
-  end
-  
-  
-  # checks if two users are friends
-  def is_friend?(contributor_id, user_id)
-    friendship = Friendship.find_by_sql "SELECT id FROM friendships WHERE (user_id=#{contributor_id} AND friend_id=#{user_id}) OR (user_id=#{user_id} AND friend_id=#{contributor_id}) AND accepted_at IS NOT NULL"
-    return(!friendship.blank?)
-  end
-  
-  
-  # gets the user object from the user_id;
-  # used by is_authorized when calling model.authorized? method for classes that don't use policy-based authorization
-  def get_user(user_id)
-    return nil if user_id == 0
-    
-    begin
-      user = User.find(:first, :conditions => ["id = ?", user_id])
-      return user
-    rescue ActiveRecord::RecordNotFound
-      # user not found, "nil" for anonymous user will be returned
-      return nil
-    end
-  end
-  
-  
-  # query database for relevant fields in policies table
-  def get_policy(policy_id)
-    select_string = 'id, contributor_id, contributor_type, share_mode, update_mode'
-    policy_array = Policy.find_by_sql "SELECT #{select_string} FROM policies WHERE policies.id=#{policy_id}"
-    
-    if policy_array.blank?
-      # an unlikely event that contribution doesn't have a policy - need to use
-      # default one; "owner" of the contribution will be treated as policy admin
-      #
-      # the following is slow, but given the very rare execution can be kept
-      begin
-        # thing_instance is Contribution, so thing_instance.contributor is the original uploader == owner of the item
-        contributor = eval("#{thing_instance.contributor_type}.find(#{thing_instance.contributor_id})")
-        policy = Policy._default(contributor) 
-      rescue ActiveRecord::RecordNotFound => e
-        # original contributor not found, but the Contribution entry still exists -
-        # this is an error in associations then, because all dependent items
-        # should have been deleted along with the contributor entry; log the error
-        logger.error("UNEXPECTED ERROR - Contributor object missing for an existing contribution: (#{thing_instance.class.name}, #{thing_instance.id})")
-        logger.error("EXCEPTION:" + e)
-        return nil
-      end
-    else
-      policy = policy_array[0]
-    end
-    
-    # if no policy is found (even no default one) --> nil will be returned
-    return policy
-  end
-  
-  
-  # get all user permissions related to policy for the "thing" for "user"
-  def get_user_permissions(user_id, policy_id)
-    select_string = 'contributor_id, download, edit, view'
-    Permission.find_by_sql "SELECT #{select_string} FROM permissions WHERE policy_id=#{policy_id} AND contributor_type='User' AND contributor_id=#{user_id}"
-  end
-  
-  
-  # get all group permissions related to policy for the "thing"
-  def get_group_permissions(policy_id)
-    select_string = 'contributor_id, download, edit, view'
-    Permission.find_by_sql "SELECT #{select_string} FROM permissions WHERE policy_id=#{policy_id} AND contributor_type='Network'"
-  end
-  
-
-  # checks whether "user" is authorized for "action" on "thing"
-  def authorized_by_policy?(policy, thing_instance, action, user_id)
-    is_authorized = false
-    
-    # NB! currently myExperiment won't support objects owned by entities other than users
-    # (especially, policy checks are not agreed for these cases - however, owner tests and
-    #  permission tests are possible and will be carried out)
-    unless thing_instance.contributor_type == "User"
-      return false
-    end
-    
-    ####################################################################################
-    #
-    # For details on what each sharing / updating mode means, see the wiki:
-    # http://wiki.myexperiment.org/index.php/Developer:Ownership_Sharing_and_Permissions
-    #
-    ####################################################################################
-    share_mode = policy.share_mode
-    update_mode = policy.update_mode
-
-    case action
-      when 'view'
-        if (share_mode == 0 || share_mode == 1 || share_mode == 2)
-          # if share mode is 0,1,2, anyone can view
-          is_authorized = true
-        elsif !user_id.nil? && (share_mode == 3 || share_mode == 4 || update_mode == 1)
-          # if share mode is 3,4, friends can view; AND friends can also view if update mode is 1 -- due to cascading permissions
-          is_authorized = is_friend?(thing_instance.contributor_id, user_id)
-        end
-        
-      when 'download'
-        if (share_mode == 0)
-          # if share mode is 0, anyone can download
-          is_authorized = true
-        elsif !user_id.nil? && (share_mode == 1 || share_mode == 3 || update_mode == 1)
-          # if share mode is 1,3, friends can download; AND if update mode is 1, friends can download too -- due to cascading permissions
-          is_authorized = is_friend?(thing_instance.contributor_id, user_id)
-        end
-      when 'edit'
-        if (update_mode == 0 && share_mode == 0)
-          # if update mode is 0, anyone with view & download permissions can edit (sharing mode 0 for anonymous)
-          is_authorized = true
-        elsif !user_id.nil? && (update_mode == 1 || (update_mode == 0 && (share_mode == 1 || share_mode == 3)))
-          # if update mode is 1, friends can edit; AND if update mode is 0 and friends have view & download permissions, they can edit
-          is_authorized = is_friend?(thing_instance.contributor_id, user_id)
-        end
-    end
-
-    return is_authorized
-  end
-
-end

reply via email to

[Prev in Thread] Current Thread [Next in Thread]