MDL-36426 repository: Prevent login_as() users to access private repositories
authorFrederic Massart <fred@moodle.com>
Mon, 4 Feb 2013 06:58:46 +0000 (14:58 +0800)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 4 Mar 2013 01:38:35 +0000 (02:38 +0100)
17 files changed:
lang/en/repository.php
repository/coursefiles/lib.php
repository/equella/lib.php
repository/filesystem/lib.php
repository/flickr_public/lib.php
repository/lib.php
repository/local/lib.php
repository/merlot/lib.php
repository/recent/lib.php
repository/s3/lib.php
repository/upgrade.txt
repository/upload/lib.php
repository/url/lib.php
repository/user/lib.php
repository/webdav/lib.php
repository/wikimedia/lib.php
repository/youtube/lib.php

index 25713be..5ee707f 100644 (file)
@@ -158,7 +158,7 @@ $string['nofilesattached'] = 'No files attached';
 $string['nofilesavailable'] = 'No files available';
 $string['nomorefiles'] = 'No more attachments allowed';
 $string['nopathselected'] = 'No destination path select yet (double click tree node to select)';
-$string['nopermissiontoaccess'] = 'No permission to access this repository';
+$string['nopermissiontoaccess'] = 'No permission to access this repository.';
 $string['noresult'] = 'No search result';
 $string['norepositoriesavailable'] = 'Sorry, none of your current repositories can return files in the required format.';
 $string['norepositoriesexternalavailable'] = 'Sorry, none of your current repositories can return external files.';
index 959f34e..cee6d85 100644 (file)
@@ -216,4 +216,13 @@ class repository_coursefiles extends repository {
         // this should be realtime
         return 0;
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index d29378e..b54806b 100644 (file)
@@ -437,4 +437,13 @@ class repository_equella extends repository {
             return get_string('lostsource', 'repository', '');
         }
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 432e79d..c856371 100644 (file)
@@ -333,4 +333,13 @@ class repository_filesystem extends repository {
             send_file_not_found();
         }
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 6b11cfc..04d1f07 100644 (file)
@@ -550,4 +550,13 @@ class repository_flickr_public extends repository {
     public function get_file_source_info($photoid) {
         return $this->build_photo_url($photoid);
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index f28a2d7..0a2bf47 100644 (file)
@@ -487,6 +487,9 @@ abstract class repository {
     public $returntypes;
     /** @var stdClass repository instance database record */
     public $instance;
+    /** @var string Type of repository (webdav, google_docs, dropbox, ...). Read from $this->get_typename(). */
+    protected $typename;
+
     /**
      * Constructor
      *
@@ -558,6 +561,24 @@ abstract class repository {
         }
     }
 
+    /**
+     * Returns the type name of the repository.
+     *
+     * @return string type name of the repository.
+     * @since  2.5
+     */
+    public function get_typename() {
+        if (empty($this->typename)) {
+            $matches = array();
+            if (!preg_match("/^repository_(.*)$/", get_class($this), $matches)) {
+                throw new coding_exception('The class name of a repository should be repository_<typeofrepository>, '.
+                        'e.g. repository_dropbox');
+            }
+            $this->typename = $matches[1];
+        }
+        return $this->typename;
+    }
+
     /**
      * Get a repository type object by a given type name.
      *
@@ -620,19 +641,43 @@ abstract class repository {
     }
 
     /**
-     * Checks if user has a capability to view the current repository in current context
+     * Checks if user has a capability to view the current repository.
      *
-     * @return bool
+     * @return bool true when the user can, otherwise throws an exception.
+     * @throws repository_exception when the user does not meet the requirements.
      */
     public final function check_capability() {
-        $capability = false;
-        if (preg_match("/^repository_(.*)$/", get_class($this), $matches)) {
-            $type = $matches[1];
-            $capability = has_capability('repository/'.$type.':view', $this->context);
+        global $USER;
+
+        // Ensure that the user can view the repository in the current context.
+        $can = has_capability('repository/'.$this->get_typename().':view', $this->context);
+
+        // Context in which the repository has been created.
+        $repocontext = context::instance_by_id($this->instance->contextid);
+
+        // Prevent access to private repositories when logged in as.
+        if ($can && session_is_loggedinas()) {
+            if ($this->contains_private_data() || $repocontext->contextlevel == CONTEXT_USER) {
+                $can = false;
+            }
         }
-        if (!$capability) {
-            throw new repository_exception('nopermissiontoaccess', 'repository');
+
+        // Ensure that the user can view the repository in the context of the repository.
+        // We need to perform the check when already disallowed.
+        if ($can) {
+            if ($repocontext->contextlevel == CONTEXT_USER && $repocontext->instanceid != $USER->id) {
+                // Prevent URL hijack to access someone else's repository.
+                $can = false;
+            } else {
+                $can = has_capability('repository/'.$this->get_typename().':view', $repocontext);
+            }
+        }
+
+        if ($can) {
+            return true;
         }
+
+        throw new repository_exception('nopermissiontoaccess', 'repository');
     }
 
     /**
@@ -1767,13 +1812,36 @@ abstract class repository {
      */
     public function get_name() {
         global $DB;
-        if ( $name = $this->instance->name ) {
+        if ($name = $this->instance->name) {
             return $name;
         } else {
-            return get_string('pluginname', 'repository_' . $this->options['type']);
+            return get_string('pluginname', 'repository_' . $this->get_typename());
         }
     }
 
+    /**
+     * Is this repository accessing private data?
+     *
+     * This function should return true for the repositories which access external private
+     * data from a user. This is the case for repositories such as Dropbox, Google Docs or Box.net
+     * which authenticate the user and then store the auth token.
+     *
+     * Of course, many repositories store 'private data', but we only want to set
+     * contains_private_data() to repositories which are external to Moodle and shouldn't be accessed
+     * to by the users having the capability to 'login as' someone else. For instance, the repository
+     * 'Private files' is not considered as private because it's part of Moodle.
+     *
+     * You should not set contains_private_data() to true on repositories which allow different types
+     * of instances as the levels other than 'user' are, by definition, not private. Also
+     * the user instances will be protected when they need to.
+     *
+     * @return boolean True when the repository accesses private external data.
+     * @since  2.5
+     */
+    public function contains_private_data() {
+        return true;
+    }
+
     /**
      * What kind of files will be in this repository?
      *
@@ -1806,7 +1874,7 @@ abstract class repository {
         $meta = new stdClass();
         $meta->id   = $this->id;
         $meta->name = format_string($this->get_name());
-        $meta->type = $this->options['type'];
+        $meta->type = $this->get_typename();
         $meta->icon = $OUTPUT->pix_url('icon', 'repository_'.$meta->type)->out(false);
         $meta->supported_types = file_get_typegroup('extension', $this->supported_filetypes());
         $meta->return_types = $this->supported_returntypes();
index 5798ad6..93bdd20 100644 (file)
@@ -262,4 +262,13 @@ class repository_local extends repository {
             'name' => $fileinfo->get_visible_name()
         );
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 0ad1418..187eef5 100644 (file)
@@ -161,5 +161,14 @@ class repository_merlot extends repository {
     public function supported_filetypes() {
         return array('link');
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
 
index 76cdb69..a370a33 100644 (file)
@@ -203,4 +203,13 @@ class repository_recent extends repository {
     public function has_moodle_files() {
         return true;
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 596fc09..346f771 100644 (file)
@@ -249,4 +249,13 @@ class repository_s3 extends repository {
     public function supported_returntypes() {
         return FILE_INTERNAL;
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 4f420d6..4728b1f 100644 (file)
@@ -8,6 +8,11 @@ http://docs.moodle.org/dev/Repository_API
 * repository::append_suffix() has been deprecated, use repository::get_unused_filename() if you need
   to get a file name which has not yet been used in the draft area.
 
+* contains_private_data() is a new method to determine if a user 'logged in as' another user
+  can access the content of the repository. The default is to return True (no access).
+
+* get_typename() returns the type of repository: dropbox, googledocs, etc...
+
 === 2.4 ===
 
 * copy_to_area() can receive a new parameter called $areamaxbytes which controls the maximum
index eed039e..1cfcf28 100644 (file)
@@ -288,4 +288,13 @@ class repository_upload extends repository {
     public function supported_returntypes() {
         return FILE_INTERNAL;
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 1255570..45a5b3f 100644 (file)
@@ -237,4 +237,13 @@ EOD;
     public function supported_filetypes() {
         return array('web_image');
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 2b8412f..4e536d5 100644 (file)
@@ -169,4 +169,13 @@ class repository_user extends repository {
         // this should be realtime
         return 0;
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index e2d39bf..5a19cb3 100644 (file)
@@ -185,4 +185,14 @@ class repository_webdav extends repository {
     public function supported_returntypes() {
         return (FILE_INTERNAL | FILE_EXTERNAL);
     }
+
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index 09a00fb..490c6d5 100644 (file)
@@ -189,4 +189,13 @@ EOD;
     public function get_file_source_info($url) {
         return $url;
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }
index bc2f304..e3f39ab 100644 (file)
@@ -202,4 +202,13 @@ class repository_youtube extends repository {
     public function supported_returntypes() {
         return FILE_EXTERNAL;
     }
+
+    /**
+     * Is this repository accessing private data?
+     *
+     * @return bool
+     */
+    public function contains_private_data() {
+        return false;
+    }
 }