Exemple #1
0
def setHomeFolderMapping(event: events.Event):
    user = event.info
    homeDirsRoot = Setting().get(PluginSettings.HOME_DIRS_ROOT)
    homeFolder = Folder().createFolder(
        user, "Home", parentType="user", public=False, creator=user
    )
    Folder().setUserAccess(homeFolder, user, AccessType.ADMIN, save=False)

    absDir = "%s/%s" % (homeDirsRoot, HomePathMapper().davToPhysical("/" + user["login"]))
    absDir = pathlib.Path(absDir)
    absDir.mkdir(parents=True, exist_ok=True)
    homeFolder.update({"fsPath": absDir.as_posix(), "isMapping": True})
    # We don't want to trigger events here, amirite?
    Folder().save(homeFolder, validate=True, triggerEvents=False)
Exemple #2
0
    def _createSubdir(self,
                      rootDir: Path,
                      rootFolder: dict,
                      name: str,
                      user=None) -> dict:
        """Create both Girder folder and corresponding directory. The name is stored in the Girder
        folder, whereas the name of the directory is taken from the folder ID. This is a
        deliberate step to discourage renaming of directories directly on disk, which would mess
        up the mapping between Girder folders and directories
        """
        folder = Folder().createFolder(rootFolder, name, creator=user)
        dirname = str(folder['_id'])
        dir = rootDir / dirname
        dir.mkdir(parents=True)
        folder.update({'fsPath': dir.absolute().as_posix(), 'isMapping': True})
        folder = Folder().save(folder, validate=False, triggerEvents=False)

        # update the time
        Folder().updateFolder(rootFolder)
        return folder
Exemple #3
0
class FileOperationsTestCase(base.TestCase):
    def setUp(self):
        super(FileOperationsTestCase, self).setUp()
        users = (
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Jane",
                "lastName": "Austin",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Sally",
                "lastName": "User",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Joel",
                "lastName": "CanTDoMuch",
                "password": "******",
            },
        )

        self.users = {user["login"]: User().createUser(**user) for user in users}

        self.public_root = tempfile.mkdtemp()
        self.shared_root = tempfile.mkdtemp()
        self.private_root = tempfile.mkdtemp()

        self.base_collection = Collection().createCollection(
            random_string(),
            creator=self.users["admin"],
            public=True,
            reuseExisting=True,
        )

        self.public_folder = Folder().createFolder(
            self.base_collection,
            "public",
            creator=self.users["sally"],
            parentType="collection",
            public=True,
            reuseExisting=True,
        )
        self.public_folder.update(dict(fsPath=self.public_root, isMapping=True))
        self.public_folder = Folder().save(self.public_folder)

        self.private_folder = Folder().createFolder(
            self.base_collection,
            "private",
            creator=self.users["sally"],
            parentType="collection",
            public=False,
            reuseExisting=True,
        )
        self.private_folder.update(dict(fsPath=self.private_root, isMapping=True))
        self.private_folder = Folder().save(self.private_folder)

    def test_basic_file_ops(self):
        from girder.plugins.virtual_resources.rest import VirtualObject as vo

        root_path = pathlib.Path(self.public_folder["fsPath"])
        dir1 = root_path / "some_dir"
        dir1.mkdir()
        file1 = dir1 / "some_file"
        file_contents = b"hello world\n"
        with file1.open(mode="wb") as fp:
            fp.write(file_contents)

        resp = self.request(
            path="/file/{}".format(
                vo.generate_id(file1.as_posix(), self.public_folder["_id"])
            ),
            method="GET",
            user=self.users["sally"],
        )
        self.assertStatusOk(resp)
        fobj = resp.json
        self.assertEqual(fobj["name"], "some_file")
        self.assertEqual(int(fobj["size"]), file1.stat().st_size)

        resp = self.request(
            path="/file/{_id}".format(**fobj),
            method="PUT",
            user=self.users["joel"],
            params={"name": "new_name"},
        )
        self.assertStatus(resp, 403)

        resp = self.request(
            path="/file/{_id}".format(**fobj),
            method="PUT",
            user=self.users["sally"],
            params={"name": "new_name"},
        )
        self.assertStatusOk(resp)
        fobj = resp.json

        self.assertFalse(file1.exists())
        file1 = file1.with_name(fobj["name"])
        self.assertTrue(file1.exists() and file1.is_file())

        resp = self.request(
            path="/file/{_id}/download".format(**fobj),
            method="GET",
            user=self.users["joel"],
            isJson=False,
        )
        self.assertStatusOk(resp)

        contentDisposition = 'filename="%s"' % fobj["name"]
        self.assertEqual(resp.headers["Content-Type"], "application/octet-stream")
        self.assertEqual(
            resp.headers["Content-Disposition"], "attachment; %s" % contentDisposition
        )
        self.assertEqual(file_contents.decode(), self.getBody(resp))

        # Test downloading the file with contentDisposition=inline.
        params = {"contentDisposition": "inline"}
        resp = self.request(
            path="/file/{_id}/download".format(**fobj),
            method="GET",
            user=self.users["joel"],
            isJson=False,
            params=params,
        )
        self.assertStatusOk(resp)
        self.assertEqual(resp.headers["Content-Type"], "application/octet-stream")
        self.assertEqual(
            resp.headers["Content-Disposition"], "inline; %s" % contentDisposition
        )
        self.assertEqual(file_contents.decode(), self.getBody(resp))

        # Test downloading with an offset
        resp = self.request(
            path="/file/{_id}/download".format(**fobj),
            method="GET",
            user=self.users["joel"],
            isJson=False,
            params={"offset": 1},
        )
        self.assertStatus(resp, 206)
        self.assertEqual(file_contents[1:].decode(), self.getBody(resp))

        # Test downloading with a range header and query range params
        respHeader = self.request(
            path="/file/{_id}/download".format(**fobj),
            method="GET",
            user=self.users["joel"],
            isJson=False,
            additionalHeaders=[("Range", "bytes=2-7")],
        )
        respQuery = self.request(
            path="/file/{_id}/download".format(**fobj),
            method="GET",
            user=self.users["joel"],
            isJson=False,
            params={"offset": 2, "endByte": 8},
        )
        for resp in [respHeader, respQuery]:
            self.assertEqual(file_contents[2:8].decode(), self.getBody(resp))
            self.assertEqual(resp.headers["Accept-Ranges"], "bytes")
            length = len(file_contents)
            begin, end = min(length, 2), min(length, 8)
            self.assertEqual(resp.headers["Content-Length"], end - begin)
            if length:
                self.assertStatus(resp, 206)
                self.assertEqual(
                    resp.headers["Content-Range"],
                    "bytes %d-%d/%d" % (begin, end - 1, length),
                )
            else:
                self.assertStatusOk(resp)

        resp = self.request(
            path="/file/{_id}".format(**fobj),
            method="DELETE",
            user=self.users["sally"],
            isJson=False,
        )
        self.assertStatusOk(resp)
        self.assertFalse(file1.exists())

    def _testUploadFile(self, name):
        """
        Uploads a non-empty file to the server.
        """
        # Initialize the upload
        resp = self.request(
            path="/file",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": self.private_folder["_id"],
                "name": name,
                "size": len(chunk1) + len(chunk2),
                "mimeType": "text/plain",
            },
        )
        self.assertStatusOk(resp)

        uploadId = resp.json["_id"]

        # Uploading with no user should fail
        fields = [("offset", 0), ("uploadId", uploadId)]
        files = [("chunk", "helloWorld.txt", chunk1)]
        resp = self.multipartRequest(path="/file/chunk", fields=fields, files=files)
        self.assertStatus(resp, 401)

        # Uploading with the wrong user should fail
        fields = [("offset", 0), ("uploadId", uploadId)]
        files = [("chunk", "helloWorld.txt", chunk1)]
        resp = self.multipartRequest(
            path="/file/chunk", user=self.users["joel"], fields=fields, files=files
        )
        self.assertStatus(resp, 403)

        # Sending the first chunk should fail because the default minimum chunk
        # size is larger than our chunk.
        Setting().unset(SettingKey.UPLOAD_MINIMUM_CHUNK_SIZE)
        fields = [("offset", 0), ("uploadId", uploadId)]
        files = [("chunk", "helloWorld.txt", chunk1)]
        resp = self.multipartRequest(
            path="/file/chunk", user=self.users["sally"], fields=fields, files=files
        )
        self.assertStatus(resp, 400)
        self.assertEqual(
            resp.json,
            {
                "type": "validation",
                "message": "Chunk is smaller than the minimum size.",
            },
        )

        # Send the first chunk (use multipart)
        Setting().set(SettingKey.UPLOAD_MINIMUM_CHUNK_SIZE, 0)
        resp = self.multipartRequest(
            path="/file/chunk", user=self.users["sally"], fields=fields, files=files
        )
        self.assertStatusOk(resp)

        # Attempting to send second chunk with incorrect offset should fail
        fields = [("offset", 0), ("uploadId", uploadId)]
        files = [("chunk", name, chunk2)]
        resp = self.multipartRequest(
            path="/file/chunk", user=self.users["sally"], fields=fields, files=files
        )

        self.assertStatus(resp, 400)

        # Ask for completion before sending second chunk should fail
        resp = self.request(
            path="/file/completion",
            method="POST",
            user=self.users["sally"],
            params={"uploadId": uploadId},
        )
        self.assertStatus(resp, 400)

        # Request offset from server (simulate a resume event)
        resp = self.request(
            path="/file/offset", user=self.users["sally"], params={"uploadId": uploadId}
        )
        self.assertStatusOk(resp)

        # Trying to send too many bytes should fail
        currentOffset = resp.json["offset"]
        fields = [("offset", resp.json["offset"]), ("uploadId", uploadId)]
        files = [("chunk", name, "extra_" + chunk2 + "_bytes")]
        resp = self.multipartRequest(
            path="/file/chunk", user=self.users["sally"], fields=fields, files=files
        )
        self.assertStatus(resp, 400)
        self.assertEqual(
            resp.json, {"type": "validation", "message": "Received too many bytes."}
        )

        # The offset should not have changed
        resp = self.request(
            path="/file/offset", user=self.users["sally"], params={"uploadId": uploadId}
        )
        self.assertStatusOk(resp)
        self.assertEqual(resp.json["offset"], currentOffset)

        # Now upload the second chunk (using query params + body)
        resp = self.request(
            path="/file/chunk",
            method="POST",
            user=self.users["sally"],
            body=chunk2,
            params={"offset": resp.json["offset"], "uploadId": uploadId},
            type="text/plain",
        )
        self.assertStatusOk(resp)

        file = resp.json

        self.assertHasKeys(file, ["itemId"])
        self.assertEqual(file["name"], name)
        self.assertEqual(file["size"], len(chunk1 + chunk2))

        return file

    def test_upload_odd_cases(self):
        from girder.plugins.virtual_resources.rest import VirtualObject as vo

        # Change dest perms to ro
        dest_dir = pathlib.Path(self.private_folder["fsPath"])
        dest_dir.chmod(0o551)
        resp = self.request(
            path="/file",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": self.private_folder["_id"],
                "name": "empty_file.txt",
                "size": "0",
                "mimeType": "text/plain",
            },
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(
            resp.json["message"],
            "Insufficient perms to write on {}".format(self.private_folder["fsPath"]),
        )

        dest_dir.chmod(0o751)
        (dest_dir / "empty_file.txt").mkdir()
        resp = self.request(
            path="/file",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": self.private_folder["_id"],
                "name": "empty_file.txt",
                "size": "0",
                "mimeType": "text/plain",
            },
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(
            resp.json["message"],
            "IsADirectoryError: IsADirectoryError(21, 'Is a directory')",
        )
        (dest_dir / "empty_file.txt").rmdir()

        resp = self.request(
            path="/file",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": self.private_folder["_id"],
                "name": "empty_file.txt",
                "size": 0,
                "mimeType": "text/plain",
            },
        )
        self.assertStatusOk(resp)

        resp = self.request(
            path="/file",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": self.private_folder["_id"],
                "name": "blah_file.txt",
                "size": len(chunk1),
                "mimeType": "text/plain",
            },
        )
        self.assertStatusOk(resp)
        upload = resp.json

        (dest_dir / "blah_file.txt").unlink()
        dest_dir.chmod(0o551)
        resp = self.request(
            path="/file/chunk",
            method="POST",
            user=self.users["sally"],
            params={"uploadId": upload["_id"], "offset": 0},
            type="text/plain",
            body=chunk1,
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(
            resp.json["message"], "Exception: Exception('Failed to store upload.')",
        )
        dest_dir.chmod(0o775)

        resp = self.request(
            path="/file",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": self.private_folder["_id"],
                "name": "full_body.txt",
                "size": len(chunk1),
                "mimeType": "text/plain",
            },
            type="text/plain",
            body=chunk1,
        )
        self.assertStatusOk(resp)
        fobj = resp.json
        self.assertEqual(fobj["size"], len(chunk1))
        self.assertEqual(fobj["_modelType"], "file")

        dest_dir = pathlib.Path(self.private_folder["fsPath"]) / "some_dir"
        dest_dir.mkdir()
        resp = self.request(
            path="/file",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": vo.generate_id(
                    dest_dir.as_posix(), self.private_folder["_id"]
                ),
                "name": "full_body.txt",
                "size": len(chunk1),
                "mimeType": "text/plain",
            },
            type="text/plain",
            body=chunk1,
        )
        self.assertStatusOk(resp)
        fobj = resp.json
        self.assertEqual(fobj["size"], len(chunk1))
        self.assertTrue((dest_dir / fobj["name"]).is_file())

    def testFilesystemAssetstore(self):
        """
        Test usage of the Filesystem assetstore type.
        """
        self.assetstore = Assetstore().getCurrent()
        root = self.assetstore["root"]

        # Clean out the test assetstore on disk
        shutil.rmtree(root)

        # First clean out the temp directory
        tmpdir = os.path.join(root, "temp")
        if os.path.isdir(tmpdir):
            for tempname in os.listdir(tmpdir):
                os.remove(os.path.join(tmpdir, tempname))

        # Upload the two-chunk file
        file = self._testUploadFile("helloWorld1.txt")
        self.assertEqual(file["_modelType"], "file")

        # We want to make sure the file got uploaded correctly into
        # the assetstore and stored at the right location
        # self.assertEqual(os.stat(abspath).st_size, file["size"])
        # self.assertEqual(os.stat(abspath).st_mode & 0o777, DEFAULT_PERMS)

        # Make sure access control is enforced on download
        resp = self.request(path="/file/%s/download" % file["_id"], method="GET")
        self.assertStatus(resp, 401)

        # Make sure access control is enforced on get info
        resp = self.request(path="/file/" + str(file["_id"]), method="GET")
        self.assertStatus(resp, 401)

        # Make sure we can get the file info and that it's filtered
        resp = self.request(
            path="/file/" + str(file["_id"]), method="GET", user=self.users["sally"]
        )
        self.assertStatusOk(resp)
        self.assertEqual(resp.json["mimeType"], "application/octet-stream")  # FIXME
        self.assertEqual(resp.json["exts"], ["txt"])
        self.assertEqual(resp.json["_modelType"], "file")
        self.assertEqual(resp.json["creatorId"], str(self.users["sally"]["_id"]))
        self.assertEqual(resp.json["size"], file["size"])
        self.assertTrue("itemId" in resp.json)
        # self.assertTrue("assetstoreId" in resp.json)  # FIXME!
        # self.assertFalse("sha512" in resp.json)  # FIXME ?

    def tearDown(self):
        # Folder().remove(self.public_folder)
        # Folder().remove(self.private_folder)
        # Collection().remove(self.base_collection)
        for user in self.users.values():
            User().remove(user)
        for root in (self.public_root, self.shared_root, self.private_root):
            shutil.rmtree(root)
        super(FileOperationsTestCase, self).tearDown()
Exemple #4
0
def copyVersionsAndRuns(event: events.Event) -> None:
    def get_dir_path(root_id_key, tale):
        if root_id_key == "versionsRootId":
            return util.getTaleVersionsDirPath(tale)
        elif root_id_key == "runsRootId":
            return util.getTaleRunsDirPath(tale)

    old_tale, new_tale = event.info
    creator = User().load(new_tale["creatorId"], force=True)
    versions_map = {}
    for root_id_key in ("versionsRootId", "runsRootId"):
        old_root = Folder().load(old_tale[root_id_key],
                                 user=creator,
                                 level=AccessType.READ)
        new_root = Folder().load(new_tale[root_id_key],
                                 user=creator,
                                 level=AccessType.WRITE)
        old_root_path = get_dir_path(root_id_key, old_tale)
        new_root_path = get_dir_path(root_id_key, new_tale)
        for src in Folder().childFolders(old_root, "folder", user=creator):
            dst = Folder().createFolder(new_root, src["name"], creator=creator)
            if root_id_key == "versionsRootId":
                versions_map[str(src["_id"])] = str(dst["_id"])
            filtered_folder = Folder().filter(dst, creator)
            for key in src:
                if key not in filtered_folder and key not in dst:
                    dst[key] = copy.deepcopy(src[key])

            src_path = old_root_path / str(src["_id"])
            dst_path = new_root_path / str(dst["_id"])
            dst_path.mkdir(parents=True)
            shutil.copytree(src_path,
                            dst_path,
                            dirs_exist_ok=True,
                            symlinks=True)
            dst.update({
                "fsPath": dst_path.absolute().as_posix(),
                "isMapping": True,
                "created": src["created"],  # preserve timestamps
                "updated": src["updated"],
            })
            if root_id_key == "runsRootId":
                current_version = dst_path / "version"
                new_version_id = versions_map[current_version.resolve().name]
                new_version_path = (
                    "../../../../versions/"
                    f"{str(new_tale['_id'])[:2]}/{new_tale['_id']}/{new_version_id}"
                )
                current_version.unlink()
                current_version.symlink_to(new_version_path, True)
                dst["runVersionId"] = ObjectId(new_version_id)
            dst = Folder().save(dst, validate=False, triggerEvents=False)
        # update the time on root
        Folder().updateFolder(new_root)

    versions_root = Folder().load(new_tale["versionsRootId"],
                                  user=creator,
                                  level=AccessType.WRITE)
    for version in Folder().childFolders(versions_root, "folder",
                                         user=creator):
        manifest = Manifest(new_tale,
                            creator,
                            versionId=version["_id"],
                            expand_folders=False)
        dst_path = pathlib.Path(version["fsPath"])
        with open(dst_path / "manifest.json", "w") as fp:
            fp.write(manifest.dump_manifest())

    Folder().updateFolder(versions_root)
class ResourceOperationsTestCase(base.TestCase):
    def setUp(self):
        super(ResourceOperationsTestCase, self).setUp()
        users = (
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Jane",
                "lastName": "Austin",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Sally",
                "lastName": "User",
                "password": "******",
            },
        )

        self.users = {
            user["login"]: User().createUser(**user)
            for user in users
        }

        self.public_root = tempfile.mkdtemp()
        self.private_root = tempfile.mkdtemp()

        self.base_collection = Collection().createCollection(
            "Virtual Resources",
            creator=self.users["admin"],
            public=True,
            reuseExisting=True,
        )

        self.public_folder = Folder().createFolder(
            self.base_collection,
            "public",
            parentType="collection",
            public=True,
            reuseExisting=True,
        )
        self.public_folder.update(dict(fsPath=self.public_root,
                                       isMapping=True))
        self.public_folder = Folder().save(self.public_folder)

        self.regular_folder = Folder().createFolder(
            self.base_collection,
            "public_no_map",
            parentType="collection",
            public=True,
            reuseExisting=True,
        )

        self.private_folder = Folder().createFolder(
            self.base_collection,
            "private",
            creator=self.users["sally"],
            parentType="collection",
            public=False,
            reuseExisting=True,
        )
        self.private_folder.update(
            dict(fsPath=self.private_root, isMapping=True))
        self.private_folder = Folder().save(self.private_folder)

    def test_basic_ops(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        nested_dir = root_path / "level0"
        nested_dir.mkdir(parents=True)

        file1 = root_path / "some_file.txt"
        file1_contents = b"Blah Blah Blah"
        with file1.open(mode="wb") as fp:
            fp.write(file1_contents)

        file2 = nested_dir / "some_other_file.txt"
        file2_contents = b"Lorem ipsum..."
        with file2.open(mode="wb") as fp:
            fp.write(file2_contents)

        # COPY
        copy_target_dir = root_path / "copy_dest"
        copy_target_dir.mkdir()

        resources = {
            "item": [
                VirtualObject.generate_id(file1.as_posix(),
                                          self.public_folder["_id"])
            ],
            "folder": [
                VirtualObject.generate_id(nested_dir.as_posix(),
                                          self.public_folder["_id"])
            ],
        }

        parentId = VirtualObject.generate_id(copy_target_dir.as_posix(),
                                             self.public_folder["_id"])

        resp = self.request(
            path="/resource/copy",
            method="POST",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": parentId,
                "resources": json.dumps(resources),
            },
        )
        self.assertStatusOk(resp)

        copied_dir = copy_target_dir / nested_dir.name
        self.assertTrue(copied_dir.is_dir())
        copied_file1 = copy_target_dir / file1.name
        self.assertTrue(copied_file1.is_file())
        with copied_file1.open(mode="rb") as fp:
            self.assertEqual(fp.read(), file1_contents)
        copied_file2 = copied_dir / file2.name
        self.assertTrue(copied_file2.is_file())
        with copied_file2.open(mode="rb") as fp:
            self.assertEqual(fp.read(), file2_contents)

        # MOVE
        move_target_dir = root_path / "moved"
        move_target_dir.mkdir()
        resources = {
            "item": [
                VirtualObject.generate_id(file1.as_posix(),
                                          self.public_folder["_id"]),
                VirtualObject.generate_id(copied_file2.as_posix(),
                                          self.public_folder["_id"]),
            ],
            "folder": [
                VirtualObject.generate_id(copy_target_dir.as_posix(),
                                          self.public_folder["_id"])
            ],
        }
        parentId = VirtualObject.generate_id(move_target_dir.as_posix(),
                                             self.public_folder["_id"])
        resp = self.request(
            path="/resource/move",
            method="PUT",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": parentId,
                "resources": json.dumps(resources),
            },
        )
        self.assertStatusOk(resp)
        for resource in (copy_target_dir, file1, copied_file2):
            self.assertFalse(resource.exists())

        moved_dir = move_target_dir / copy_target_dir.name
        moved_file1 = move_target_dir / file1.name
        moved_file2 = move_target_dir / file2.name
        self.assertTrue(moved_dir.is_dir())
        self.assertTrue(moved_file1.is_file())
        self.assertTrue(moved_file2.is_file())

        # DELETE
        regular_folder = Folder().createFolder(
            self.base_collection,
            "public",
            parentType="collection",
            public=True,
            reuseExisting=True,
        )
        with file1.open(mode="wb") as fp:  # Recreate
            fp.write(file1_contents)

        resources = {"item": [], "folder": [str(regular_folder["_id"])]}
        for path in root_path.iterdir():
            if path.is_dir():
                key = "folder"
            else:
                key = "item"
            resources[key].append(
                VirtualObject.generate_id(path.as_posix(),
                                          self.public_folder["_id"]))

        resp = self.request(
            path="/resource",
            method="DELETE",
            user=self.users["admin"],
            params={"resources": json.dumps(resources)},
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(list(root_path.iterdir())), 0)
        regular_folder = Folder().load(regular_folder["_id"], force=True)
        self.assertTrue(regular_folder is None)

    def test_resource_op_acls(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        nested_dir = root_path / "level0"
        nested_dir.mkdir(parents=True)

        file1 = root_path / "some_file.txt"
        file1_contents = b"Blah Blah Blah"
        with file1.open(mode="wb") as fp:
            fp.write(file1_contents)

        sallys_root_path = pathlib.Path(self.private_folder["fsPath"])
        # MOVE
        move_target_dir = sallys_root_path / "moved"
        move_target_dir.mkdir()
        resources = {
            "item": [
                VirtualObject.generate_id(file1.as_posix(),
                                          self.public_folder["_id"])
            ],
            "folder": [
                VirtualObject.generate_id(nested_dir.as_posix(),
                                          self.public_folder["_id"])
            ],
        }
        parentId = VirtualObject.generate_id(move_target_dir.as_posix(),
                                             self.private_folder["_id"])
        resp = self.request(
            path="/resource/move",
            method="PUT",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": parentId,
                "resources": json.dumps(resources),
            },
        )
        self.assertStatusOk(resp)
        self.assertTrue(nested_dir.exists())
        self.assertTrue(file1.exists())
        self.assertEqual(len(list(move_target_dir.iterdir())), 0)

        # COPY cors
        resp = self.request(
            path="/resource/copy",
            method="POST",
            user=self.users["sally"],
            params={
                "parentType": "folder",
                "parentId": parentId,
                "resources": json.dumps(resources),
            },
        )
        self.assertStatusOk(resp)
        self.assertTrue((move_target_dir / nested_dir.name).is_dir())
        self.assertTrue((move_target_dir / file1.name).is_file())

    def test_lookup(self):
        root_path = pathlib.Path(self.private_folder["fsPath"])
        nested_dir = root_path / "level0"
        nested_dir.mkdir(parents=True)
        file1 = root_path / "some_file.txt"
        file1_contents = b"Blah Blah Blah"
        with file1.open(mode="wb") as fp:
            fp.write(file1_contents)

        for model, lookup_path in (
            ("folder", "/collection/Virtual Resources/private/level0"),
            ("item", "/collection/Virtual Resources/private/some_file.txt"),
        ):
            resp = self.request(
                path="/resource/lookup",
                method="GET",
                user=self.users["admin"],
                params={"path": lookup_path},
            )
            self.assertStatusOk(resp)
            self.assertEqual(resp.json["_modelType"], model)
            self.assertEqual(resp.json["name"], pathlib.Path(lookup_path).name)

        # test should return empty document for nonexisting file
        for path in (
                "/user/nonexisting/blah",
                "/collection/Virtual Resources/private/level0/blah",
        ):
            resp = self.request(
                path="/resource/lookup",
                method="GET",
                user=self.users["admin"],
                params={
                    "path": path,
                    "test": True
                },
                exception=True,
            )
            self.assertStatus(resp, 200)
            self.assertEqual(resp.json, None)

        for path, msg in (
            ("/user/nonexisting/blah", "User not found: nonexisting"),
            ("/collection/nonexisting/blah",
             "Collection not found: nonexisting"),
            ("/blah/nonexisting/blah", "Invalid path format"),
            (
                "/collection/Virtual Resources/private/level0/blah",
                "Path not found: collection/Virtual Resources/private/level0/blah",
            ),
        ):

            resp = self.request(
                path="/resource/lookup",
                method="GET",
                user=self.users["admin"],
                params={"path": path},
                exception=True,
            )
            self.assertStatus(resp, 400)
            self.assertEqual(resp.json["message"], msg)

        file1.unlink()
        nested_dir.rmdir()

    def test_copy_existing_name(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.private_folder["fsPath"])
        file1 = root_path / "existing.txt"
        file1_contents = b"Blah Blah Blah"
        with file1.open(mode="wb") as fp:
            fp.write(file1_contents)

        resources = {
            "item": [
                VirtualObject.generate_id(file1.as_posix(),
                                          self.private_folder["_id"])
            ]
        }
        resp = self.request(
            path="/resource/copy",
            method="POST",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": self.private_folder["_id"],
                "resources": json.dumps(resources),
            },
        )
        self.assertStatusOk(resp)
        self.assertTrue((root_path / "existing.txt (1)").is_file())
        file1.unlink()
        (root_path / "existing.txt (1)").unlink()

    def test_path(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.private_folder["fsPath"])
        nested_dir = root_path / "directory"
        nested_dir.mkdir(parents=True)
        file1 = nested_dir / "file.txt"
        file1_contents = b"Blah Blah Blah"
        with file1.open(mode="wb") as fp:
            fp.write(file1_contents)

        item_id = VirtualObject.generate_id(file1.as_posix(),
                                            self.private_folder["_id"])
        folder_id = VirtualObject.generate_id(nested_dir.as_posix(),
                                              self.private_folder["_id"])

        resp = self.request(
            path="/resource/{}/path".format(item_id),
            method="GET",
            user=self.users["admin"],
            params={"type": "item"},
        )
        self.assertStatusOk(resp)
        self.assertEqual(
            resp.json,
            "/collection/Virtual Resources/private/directory/file.txt")

        resp = self.request(
            path="/resource/{}/path".format(folder_id),
            method="GET",
            user=self.users["admin"],
            params={"type": "folder"},
        )
        self.assertStatusOk(resp)
        self.assertEqual(resp.json,
                         "/collection/Virtual Resources/private/directory")

        for res_id, res_type in (
            (item_id, "folder"),
            (folder_id, "item"),
            (item_id, "collection"),
        ):
            resp = self.request(
                path="/resource/{}/path".format(res_id),
                method="GET",
                user=self.users["admin"],
                params={"type": res_type},
                exception=True,
            )
            self.assertStatus(resp, 400)
            self.assertEqual(resp.json["message"], "Invalid resource id.")

        file1.unlink()
        nested_dir.rmdir()

    def tearDown(self):
        for folder in (self.public_folder, self.private_folder,
                       self.regular_folder):
            Folder().remove(folder)
        Collection().remove(self.base_collection)
        for user in self.users.values():
            User().remove(user)
        for root in (self.public_root, self.private_root):
            shutil.rmtree(root)
        super(ResourceOperationsTestCase, self).tearDown()
class FolderOperationsTestCase(base.TestCase):
    def setUp(self):
        super(FolderOperationsTestCase, self).setUp()
        users = (
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Jane",
                "lastName": "Austin",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Sally",
                "lastName": "User",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Joel",
                "lastName": "CanTDoMuch",
                "password": "******",
            },
        )

        self.users = {
            user["login"]: User().createUser(**user)
            for user in users
        }

        self.public_root = tempfile.mkdtemp()
        self.shared_root = tempfile.mkdtemp()
        self.private_root = tempfile.mkdtemp()
        self.private2_root = tempfile.mkdtemp()

        self.base_collection = Collection().createCollection(
            "Virtual Resources",
            creator=self.users["admin"],
            public=True,
            reuseExisting=True,
        )

        self.public_folder = Folder().createFolder(
            self.base_collection,
            "public",
            parentType="collection",
            public=True,
            reuseExisting=True,
        )
        self.public_folder.update(dict(fsPath=self.public_root,
                                       isMapping=True))
        self.public_folder = Folder().save(self.public_folder)

        self.private_folder = Folder().createFolder(
            self.base_collection,
            "private",
            parentType="collection",
            public=False,
            creator=self.users["sally"],
            reuseExisting=True,
        )
        self.private_folder.update(
            dict(fsPath=self.private_root, isMapping=True))
        Folder().setUserAccess(self.private_folder, self.users["sally"],
                               AccessType.WRITE)
        Folder().setUserAccess(self.private_folder, self.users["joel"],
                               AccessType.READ)
        self.private_folder = Folder().save(self.private_folder)

        self.private_folder2 = Folder().createFolder(
            self.base_collection,
            "private2",
            parentType="collection",
            public=False,
            creator=self.users["sally"],
            reuseExisting=True,
        )
        self.private_folder2.update(
            dict(fsPath=self.private2_root, isMapping=True))
        Folder().setUserAccess(self.private_folder2, self.users["sally"],
                               AccessType.WRITE)
        Folder().setUserAccess(self.private_folder2, self.users["joel"],
                               AccessType.WRITE)
        self.private_folder2 = Folder().save(self.private_folder2)

        self.regular_folder = Folder().createFolder(
            self.base_collection,
            "regular",
            creator=self.users["sally"],
            parentType="collection",
            public=True,
            reuseExisting=True,
        )

    def test_basic_folder_ops(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        resp = self.request(
            path="/folder",
            method="POST",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": self.public_folder["_id"],
                "name": "test_folder",
            },
        )
        self.assertStatusOk(resp)
        folder = resp.json

        actual_folder_path = pathlib.Path(self.public_root) / folder["name"]
        self.assertTrue(actual_folder_path.is_dir())

        decoded_path, decoded_root_id = VirtualObject.path_from_id(
            folder["_id"])
        self.assertEqual(decoded_path, actual_folder_path)
        self.assertEqual(decoded_root_id, str(self.public_folder["_id"]))

        resp = self.request(
            path="/folder",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": str(self.public_folder["_id"])
            },
        )
        self.assertStatusOk(resp)
        get_folders = resp.json
        self.assertEqual(len(get_folders), 1)
        self.assertEqual(get_folders[0], folder)

        resp = self.request(
            path="/folder/{_id}".format(**folder),
            method="GET",
            user=self.users["admin"],
        )
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, get_folders[0])

        resp = self.request(
            path="/folder/{_id}".format(**folder),
            method="PUT",
            user=self.users["admin"],
            params={"name": "renamed"},
        )
        self.assertStatusOk(resp)
        folder = resp.json
        self.assertFalse(actual_folder_path.exists())
        actual_folder_path = pathlib.Path(self.public_root) / folder["name"]
        self.assertTrue(actual_folder_path.is_dir())

        resp = self.request(
            path="/folder/{_id}".format(**folder),
            method="DELETE",
            user=self.users["admin"],
        )
        self.assertStatusOk(resp)
        self.assertFalse(actual_folder_path.exists())

    def test_folder_move(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        dir1 = root_path / "some_dir"
        dir1.mkdir()
        file1 = dir1 / "some_file"
        with file1.open(mode="wb") as fp:
            fp.write(b"\n")

        folder_id = VirtualObject.generate_id(dir1, self.public_folder["_id"])

        resp = self.request(
            path="/folder/{}".format(folder_id),
            method="PUT",
            user=self.users["admin"],
            params={"name": dir1.name},
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(
            resp.json["message"],
            "Folder '{}' already exists in {}".format(
                dir1.name, self.public_folder["_id"]),
        )

        new_root_path = pathlib.Path(self.private_folder["fsPath"])
        dir2 = new_root_path / "level1"
        dir2.mkdir()
        new_folder_id = VirtualObject.generate_id(dir2,
                                                  self.private_folder["_id"])

        resp = self.request(
            path="/folder/{}".format(folder_id),
            method="PUT",
            user=self.users["admin"],
            params={
                "parentId": new_folder_id,
                "parentType": "folder"
            },
        )
        self.assertStatusOk(resp)
        self.assertFalse(dir1.exists())
        self.assertTrue((dir2 / dir1.name).exists())
        new_file = dir2 / dir1.name / file1.name
        self.assertTrue(new_file.exists())
        new_file.unlink()
        (dir2 / dir1.name).rmdir()
        dir2.rmdir()

    def test_move_to_root(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        dir1 = root_path / "some_dir"
        dir1.mkdir()
        file1 = dir1 / "some_file"
        with file1.open(mode="wb") as fp:
            fp.write(b"\n")
        folder_id = VirtualObject.generate_id(dir1, self.public_folder["_id"])

        resp = self.request(
            path="/folder/{}".format(folder_id),
            method="PUT",
            user=self.users["admin"],
            params={
                "parentId": self.private_folder["_id"],
                "parentType": "folder"
            },
        )
        self.assertStatusOk(resp)
        self.assertFalse(dir1.exists())

        root_path = pathlib.Path(self.private_folder["fsPath"])
        dir1 = root_path / "some_dir"
        file1 = dir1 / "some_file"
        self.assertTrue(dir1.exists())
        self.assertTrue(file1.exists())
        file1.unlink()
        dir1.rmdir()

    def test_move_acls(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.private_folder["fsPath"])
        dir1 = root_path / "some_dir"
        dir1.mkdir()

        folder_id = VirtualObject.generate_id(dir1, self.private_folder["_id"])

        resp = self.request(
            path="/folder/{}".format(folder_id),
            method="PUT",
            user=self.users["joel"],
            params={
                "parentId": self.private_folder2["_id"],
                "parentType": "folder"
            },
        )
        self.assertStatus(resp, 403)

        resp = self.request(
            path="/folder/{}".format(folder_id),
            method="PUT",
            user=self.users["sally"],
            params={
                "parentId": self.private_folder2["_id"],
                "parentType": "folder"
            },
        )
        self.assertStatusOk(resp)

        self.assertFalse(dir1.exists())
        root_path = pathlib.Path(self.private_folder2["fsPath"])
        dir1 = root_path / "some_dir"
        self.assertTrue(dir1.exists())
        folder_id = VirtualObject.generate_id(dir1,
                                              self.private_folder2["_id"])

        resp = self.request(
            path="/folder/{}".format(folder_id),
            method="PUT",
            user=self.users["joel"],
            params={
                "parentId": self.private_folder["_id"],
                "parentType": "folder"
            },
        )
        self.assertStatus(resp, 403)

        dir1.rmdir()

    def test_folder_details(self):
        root_path = pathlib.Path(self.public_folder["fsPath"])
        dir1 = root_path / "some_dir"
        dir1.mkdir()
        file1 = root_path / "some_file"
        with file1.open(mode="wb") as fp:
            fp.write(b"\n")

        resp = self.request(
            path="/folder/{_id}/details".format(**self.public_folder),
            method="GET",
            user=self.users["admin"],
        )
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, {"nFolders": 1, "nItems": 1})

        file1.unlink()
        dir1.rmdir()

    def test_folder_rootpath(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        nested_dir = root_path / "level0" / "level1" / "level2"
        nested_dir.mkdir(parents=True)

        folder_id = VirtualObject.generate_id(nested_dir,
                                              self.public_folder["_id"])
        resp = self.request(
            path="/folder/{}/rootpath".format(folder_id),
            method="GET",
            user=self.users["admin"],
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json), 4)
        rootpath = resp.json
        self.assertEqual(rootpath[0]["type"], "collection")
        self.assertEqual(rootpath[0]["object"]["_id"],
                         str(self.base_collection["_id"]))
        self.assertEqual(rootpath[1]["type"], "folder")
        self.assertEqual(rootpath[1]["object"]["_id"],
                         str(self.public_folder["_id"]))
        self.assertEqual(rootpath[2]["type"], "folder")
        self.assertEqual(rootpath[2]["object"]["name"], "level0")
        self.assertEqual(rootpath[3]["type"], "folder")
        self.assertEqual(rootpath[3]["object"]["name"], "level1")

        shutil.rmtree((root_path / "level0").as_posix())

    def test_folder_delete_contents(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        nested_dir = root_path / "lone_survivor"
        nested_dir.mkdir(parents=True)
        folder_id = VirtualObject.generate_id(nested_dir,
                                              self.public_folder["_id"])

        dir1 = nested_dir / "subfolder"
        dir1.mkdir()
        file1 = dir1 / "some_file.txt"
        with file1.open(mode="wb") as fp:
            fp.write(b"file1\n")
        file2 = nested_dir / "other_file.txt"
        with file2.open(mode="wb") as fp:
            fp.write(b"file2\n")
        self.assertEqual(len(list(nested_dir.iterdir())), 2)

        resp = self.request(
            path="/folder/{}/contents".format(folder_id),
            method="DELETE",
            user=self.users["admin"],
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(list(nested_dir.iterdir())), 0)
        nested_dir.rmdir()

    def test_folder_download(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        nested_dir = root_path / "lone_survivor"
        nested_dir.mkdir(parents=True)
        folder_id = VirtualObject.generate_id(nested_dir,
                                              self.public_folder["_id"])

        dir1 = nested_dir / "subfolder"
        dir1.mkdir()
        file1 = dir1 / "some_file.txt"
        with file1.open(mode="wb") as fp:
            fp.write(b"file1\n")
        file2 = nested_dir / "other_file.txt"
        with file2.open(mode="wb") as fp:
            fp.write(b"file2\n")
        self.assertEqual(len(list(nested_dir.iterdir())), 2)

        resp = self.request(
            path="/folder/{}/download".format(folder_id),
            method="GET",
            user=self.users["admin"],
            isJson=False,
        )
        self.assertStatusOk(resp)
        with zipfile.ZipFile(io.BytesIO(self.getBody(resp, text=False)),
                             "r") as fp:
            self.assertEqual(sorted(fp.namelist()),
                             ["other_file.txt", "subfolder/some_file.txt"])
            # TODO should probably check the content too...

    def test_folder_copy(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        dir1 = root_path / "source_folder"
        dir1.mkdir(parents=True)
        folder_id = VirtualObject.generate_id(dir1, self.public_folder["_id"])
        file1 = dir1 / "file.dat"
        with file1.open(mode="wb") as fp:
            fp.write(b"file1\n")

        resp = self.request(
            path="/folder/{}/copy".format(self.public_folder["_id"]),
            method="POST",
            user=self.users["sally"],
            params={"name": "new_copy"},
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(resp.json["message"],
                         "Copying mappings is not allowed.")

        resp = self.request(
            path="/folder/{}/copy".format(folder_id),
            method="POST",
            user=self.users["joel"],
            params={
                "name": "new_copy",
                "parentId": str(self.regular_folder["_id"]),
                "parentType": "folder",
            },
        )
        self.assertStatus(resp, 403)

        resp = self.request(
            path="/folder/{}/copy".format(folder_id),
            method="POST",
            user=self.users["sally"],
            params={
                "name": "new_copy",
                "parentId": str(self.regular_folder["_id"]),
                "parentType": "folder",
            },
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(
            resp.json["message"],
            "Folder {} is not a mapping.".format(self.regular_folder["_id"]),
        )

        resp = self.request(
            path="/folder/{}/copy".format(folder_id),
            method="POST",
            user=self.users["sally"],
            params={"name": "new_copy"},
        )
        self.assertStatus(resp, 403)
        self.assertFalse((dir1.with_name("new_copy") / file1.name).is_file())

        resp = self.request(
            path="/folder/{}/copy".format(folder_id),
            method="POST",
            user=self.users["admin"],
            params={},
            exception=True,
        )
        self.assertStatus(resp, 200)
        new_folder = resp.json
        self.assertEqual(new_folder["name"], "source_folder (1)")
        self.assertTrue(
            (dir1.with_name("source_folder (1)") / file1.name).is_file())

        resp = self.request(
            path="/folder/{}/copy".format(folder_id),
            method="POST",
            user=self.users["admin"],
            params={"name": "new_copy"},
        )
        self.assertStatusOk(resp)
        new_folder = resp.json
        self.assertEqual(new_folder["name"], "new_copy")
        self.assertTrue((dir1.with_name("new_copy") / file1.name).is_file())

        resp = self.request(
            path="/folder/{}/copy".format(folder_id),
            method="POST",
            user=self.users["admin"],
            params={
                "name": "copy_within_copy",
                "parentId": new_folder["_id"],
                "parentType": "folder",
            },
        )
        self.assertStatusOk(resp)
        self.assertTrue((dir1.with_name("new_copy") / "copy_within_copy" /
                         file1.name).is_file())

    def test_exists_already(self):
        root_path = pathlib.Path(self.public_folder["fsPath"])
        some_dir = root_path / "some_folder"
        some_dir.mkdir(parents=True)

        resp = self.request(
            path="/folder",
            method="POST",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": self.public_folder["_id"],
                "name": "some_folder",
            },
        )
        self.assertStatus(resp, 400)
        folder = resp.json
        self.assertEqual(
            folder,
            {
                "type": "validation",
                "message": "A folder with that name already exists here.",
                "field": "name",
            },
        )

        some_dir.rmdir()

    def test_folder_listing(self):
        root_path = pathlib.Path(self.public_folder["fsPath"])
        some_dir = root_path / "some_folder_with_subfolders"
        some_dir.mkdir(parents=True)

        (some_dir / "subfolder1").mkdir()
        (some_dir / "subfolder2").mkdir()

        resp = self.request(
            path="/folder",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": str(self.public_folder["_id"])
            },
        )
        self.assertStatusOk(resp)
        some_dir_obj = resp.json[0]
        self.assertEqual(some_dir_obj["name"], "some_folder_with_subfolders")

        resp = self.request(
            path="/folder",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": some_dir_obj["_id"]
            },
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json), 2)

        resp = self.request(
            path="/folder",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": some_dir_obj["_id"],
                "name": "subfolder2",
            },
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json), 1)
        self.assertEqual(resp.json[0]["name"], "subfolder2")

        resp = self.request(
            path="/folder",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": some_dir_obj["_id"],
                "name": "nope",
            },
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json), 0)
        shutil.rmtree(some_dir.as_posix())

    def tearDown(self):
        Folder().remove(self.public_folder)
        Folder().remove(self.private_folder)
        Folder().remove(self.private_folder2)
        Folder().remove(self.regular_folder)
        Collection().remove(self.base_collection)
        for user in self.users.values():
            User().remove(user)
        for root in (
                self.public_root,
                self.shared_root,
                self.private_root,
                self.private2_root,
        ):
            shutil.rmtree(root)
        super(FolderOperationsTestCase, self).tearDown()
class ItemOperationsTestCase(base.TestCase):
    def setUp(self):
        super(ItemOperationsTestCase, self).setUp()
        users = (
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Jane",
                "lastName": "Austin",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Sally",
                "lastName": "User",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Joel",
                "lastName": "CanTDoMuch",
                "password": "******",
            },
        )

        self.users = {
            user["login"]: User().createUser(**user)
            for user in users
        }

        self.public_root = tempfile.mkdtemp()
        self.private_root = tempfile.mkdtemp()

        self.base_collection = Collection().createCollection(
            "Virtual Resources",
            creator=self.users["admin"],
            public=True,
            reuseExisting=True,
        )

        self.public_folder = Folder().createFolder(
            self.base_collection,
            "public",
            parentType="collection",
            public=True,
            reuseExisting=True,
        )
        self.public_folder.update(dict(fsPath=self.public_root,
                                       isMapping=True))
        self.public_folder = Folder().save(self.public_folder)

        self.regular_folder = Folder().createFolder(
            self.base_collection,
            "public_no_map",
            parentType="collection",
            public=True,
            reuseExisting=True,
        )

        self.private_folder = Folder().createFolder(
            self.base_collection,
            "private",
            creator=self.users["sally"],
            parentType="collection",
            public=False,
            reuseExisting=True,
        )
        self.private_folder.update(
            dict(fsPath=self.private_root, isMapping=True))
        self.private_folder = Folder().save(self.private_folder)

    def test_basic_item_ops(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        nested_dir = root_path / "level0"
        nested_dir.mkdir(parents=True)
        parentId = VirtualObject.generate_id(nested_dir.as_posix(),
                                             self.public_folder["_id"])

        resp = self.request(
            path="/item",
            method="POST",
            user=self.users["admin"],
            params={
                "folderId": parentId,
                "name": "test_item"
            },
        )
        self.assertStatusOk(resp)
        item = resp.json

        actual_item_path = nested_dir / item["name"]
        self.assertTrue(actual_item_path.is_file())

        decoded_path, decoded_root_id = VirtualObject.path_from_id(item["_id"])
        self.assertEqual(decoded_path, actual_item_path)
        self.assertEqual(decoded_root_id, str(self.public_folder["_id"]))

        resp = self.request(
            path="/item",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": str(parentId),
                "name": "nope"
            },
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json), 0)

        resp = self.request(
            path="/item",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": str(parentId),
                "name": item["name"]
            },
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json), 1)

        resp = self.request(
            path="/item",
            method="GET",
            user=self.users["admin"],
            params={
                "parentType": "folder",
                "parentId": str(parentId)
            },
        )
        self.assertStatusOk(resp)
        get_items = resp.json
        self.assertEqual(len(get_items), 1)
        self.assertEqual(get_items[0], item)

        resp = self.request(path="/item/{_id}".format(**item),
                            method="GET",
                            user=self.users["admin"])
        self.assertStatusOk(resp)
        self.assertEqual(resp.json, get_items[0])

        resp = self.request(
            path="/item/{_id}".format(**item),
            method="PUT",
            user=self.users["admin"],
            params={"name": "renamed"},
        )
        self.assertStatusOk(resp)
        item = resp.json
        self.assertFalse(actual_item_path.exists())
        actual_item_path = actual_item_path.with_name(item["name"])
        self.assertTrue(actual_item_path.is_file())

        resp = self.request(
            path="/item/{_id}/files".format(**item),
            method="GET",
            user=self.users["admin"],
        )
        self.assertStatusOk(resp)
        files = resp.json
        self.assertEqual(len(files), 1)
        self.assertEqual(files[0]["_id"], item["_id"])

        resp = self.request(path="/item/{_id}".format(**item),
                            method="DELETE",
                            user=self.users["admin"])
        self.assertStatusOk(resp)
        self.assertFalse(actual_item_path.exists())
        shutil.rmtree(nested_dir.as_posix())

    def test_item_rootpath(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        nested_dir = root_path / "level0" / "level1"
        nested_dir.mkdir(parents=True)

        file1 = nested_dir / "some_file"
        file_contents = b"hello world\n"
        with file1.open(mode="wb") as fp:
            fp.write(file_contents)

        item_id = VirtualObject.generate_id(file1, self.public_folder["_id"])
        resp = self.request(
            path="/item/{}/rootpath".format(item_id),
            method="GET",
            user=self.users["admin"],
        )
        self.assertStatusOk(resp)
        self.assertEqual(len(resp.json), 4)
        rootpath = resp.json
        self.assertEqual(rootpath[0]["type"], "collection")
        self.assertEqual(rootpath[0]["object"]["_id"],
                         str(self.base_collection["_id"]))
        self.assertEqual(rootpath[1]["type"], "folder")
        self.assertEqual(rootpath[1]["object"]["_id"],
                         str(self.public_folder["_id"]))
        self.assertEqual(rootpath[2]["type"], "folder")
        self.assertEqual(rootpath[2]["object"]["name"], "level0")
        self.assertEqual(rootpath[3]["type"], "folder")
        self.assertEqual(rootpath[3]["object"]["name"], "level1")

        shutil.rmtree((root_path / "level0").as_posix())

    def test_copy_item(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        file1 = root_path / "some_file"
        file_contents = b"hello world\n"
        with file1.open(mode="wb") as fp:
            fp.write(file_contents)
        item_id = VirtualObject.generate_id(file1, self.public_folder["_id"])

        # Copy in the same folder
        resp = self.request(
            path="/item/{}/copy".format(item_id),
            method="POST",
            user=self.users["admin"],
            params={"name": "copy"},
        )
        self.assertStatusOk(resp)

        self.assertTrue(file1.with_name("copy").is_file())
        with open(file1.with_name("copy").as_posix(), "rb") as fp:
            self.assertEqual(fp.read(), file_contents)

        # Copy in the same mapping but different folder
        new_dir = root_path / "some_dir"
        new_dir.mkdir()
        folder_id = VirtualObject.generate_id(new_dir,
                                              self.public_folder["_id"])
        resp = self.request(
            path="/item/{}/copy".format(item_id),
            method="POST",
            user=self.users["admin"],
            params={"folderId": str(folder_id)},
        )
        self.assertStatusOk(resp)
        new_file = new_dir / "some_file"
        self.assertTrue(new_file.is_file())
        with open(new_file.as_posix(), "rb") as fp:
            self.assertEqual(fp.read(), file_contents)

        # Copy between two mappings
        resp = self.request(
            path="/item/{}/copy".format(item_id),
            method="POST",
            user=self.users["admin"],
            params={"folderId": str(self.private_folder["_id"])},
        )
        self.assertStatusOk(resp)
        cors_file = pathlib.Path(self.private_folder["fsPath"]) / "some_file"
        self.assertTrue(cors_file.is_file())
        with open(cors_file.as_posix(), "rb") as fp:
            self.assertEqual(fp.read(), file_contents)

        # Try to copy to non mapping
        resp = self.request(
            path="/item/{}/copy".format(item_id),
            method="POST",
            user=self.users["admin"],
            params={"folderId": str(self.regular_folder["_id"])},
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(
            resp.json["message"],
            "Folder {} is not a mapping.".format(self.regular_folder["_id"]),
        )

        shutil.rmtree(new_dir.as_posix())
        file1.unlink()
        file1.with_name("copy").unlink()
        cors_file.unlink()

    def test_move_item(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.public_folder["fsPath"])
        subdir = root_path / "subdir"
        subdir.mkdir()
        file1 = subdir / "to_be_moved"
        file_contents = b"hello world asdfadsf\n"
        with file1.open(mode="wb") as fp:
            fp.write(file_contents)
        folder_id = VirtualObject.generate_id(subdir,
                                              self.public_folder["_id"])
        item_id = VirtualObject.generate_id(file1, self.public_folder["_id"])

        # Move with the same name (noop)
        resp = self.request(
            path="/item/{}".format(item_id),
            method="PUT",
            user=self.users["admin"],
            params={
                "name": file1.name,
                "folderId": folder_id
            },
        )
        self.assertStatusOk(resp)
        self.assertTrue(file1.exists())

        # Move within the same folder
        resp = self.request(
            path="/item/{}".format(item_id),
            method="PUT",
            user=self.users["admin"],
            params={
                "name": "after_move",
                "folderId": folder_id
            },
        )
        self.assertStatusOk(resp)
        self.assertTrue((subdir / "after_move").exists())
        self.assertFalse(file1.exists())

        # Move to a different folder
        file1 = subdir / "after_move"
        item_id = VirtualObject.generate_id(file1, self.public_folder["_id"])
        resp = self.request(
            path="/item/{}".format(item_id),
            method="PUT",
            user=self.users["admin"],
            params={
                "name": "after_move",
                "folderId": self.private_folder["_id"]
            },
        )
        self.assertStatusOk(resp)
        self.assertFalse(file1.exists())
        root_path = pathlib.Path(self.private_folder["fsPath"])
        file_new = root_path / "after_move"
        item_id = VirtualObject.generate_id(file_new,
                                            self.private_folder["_id"])

        self.assertTrue(file_new.is_file())
        with open(file_new.as_posix(), "rb") as fp:
            self.assertEqual(fp.read(), file_contents)

        # Try to move not into mapping and fail
        resp = self.request(
            path="/item/{}".format(item_id),
            method="PUT",
            user=self.users["sally"],
            params={
                "name": "after_move",
                "folderId": str(self.regular_folder["_id"]),
                "parentType": "folder",
            },
            exception=True,
        )
        self.assertStatus(resp, 500)
        self.assertEqual(
            resp.json["message"],
            "Folder {} is not a mapping.".format(self.regular_folder["_id"]),
        )

        # move it back to subdir in a mapping
        resp = self.request(
            path="/item/{}".format(item_id),
            method="PUT",
            user=self.users["admin"],
            params={
                "name": "final_move",
                "folderId": folder_id
            },
        )
        self.assertStatusOk(resp)
        self.assertFalse(file_new.exists())
        file_new = subdir / "final_move"
        self.assertTrue(file_new.exists())
        file_new.unlink()

    def test_copy_existing_name(self):
        from girder.plugins.virtual_resources.rest import VirtualObject

        root_path = pathlib.Path(self.private_folder["fsPath"])
        file1 = root_path / "existing.txt"
        file1_contents = b"Blah Blah Blah"
        with file1.open(mode="wb") as fp:
            fp.write(file1_contents)
        item_id = VirtualObject.generate_id(file1, self.private_folder["_id"])

        resp = self.request(
            path="/item/{}/copy".format(item_id),
            method="POST",
            user=self.users["admin"],
            params={},
        )
        self.assertStatusOk(resp)
        self.assertEqual(resp.json["name"], "existing.txt (1)")

        item = resp.json
        resp = self.request(
            path="/item",
            method="POST",
            user=self.users["admin"],
            params={
                "folderId": item["folderId"],
                "name": item["name"]
            },
        )
        self.assertStatus(resp, 400)
        folder = resp.json
        self.assertEqual(
            folder,
            {
                "type": "validation",
                "message": "An item with that name already exists here.",
                "field": "name",
            },
        )

        self.assertTrue((root_path / "existing.txt (1)").is_file())
        file1.unlink()
        (root_path / "existing.txt (1)").unlink()

    def tearDown(self):
        for folder in (self.public_folder, self.private_folder,
                       self.regular_folder):
            Folder().remove(folder)
        Collection().remove(self.base_collection)
        for user in self.users.values():
            User().remove(user)
        for root in (self.public_root, self.private_root):
            shutil.rmtree(root)
        super(ItemOperationsTestCase, self).tearDown()
Exemple #8
0
class BasicOperationsTestCase(base.TestCase):
    def setUp(self):
        super(BasicOperationsTestCase, self).setUp()
        users = (
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Jane",
                "lastName": "Austin",
                "password": "******",
            },
            {
                "email": "*****@*****.**",
                "login": "******",
                "firstName": "Sally",
                "lastName": "User",
                "password": "******",
            },
        )

        self.users = {
            user["login"]: User().createUser(**user)
            for user in users
        }

        self.public_root = tempfile.mkdtemp()

        self.base_collection = Collection().createCollection(
            random_string(),
            creator=self.users["admin"],
            public=True,
            reuseExisting=True,
        )

        self.public_folder = Folder().createFolder(
            self.base_collection,
            "public",
            creator=self.users["sally"],
            parentType="collection",
            public=True,
            reuseExisting=True,
        )
        self.public_folder.update(dict(fsPath=self.public_root,
                                       isMapping=True))
        self.public_folder = Folder().save(self.public_folder)

    def test_vo_methods(self):
        from girder.plugins.virtual_resources.rest import VirtualObject as vo

        root_path = pathlib.Path(self.public_folder["fsPath"])
        non_existing = root_path / "something"

        with pytest.raises(ValidationException):
            vo().is_file(non_existing, self.public_folder["_id"])

        with pytest.raises(ValidationException):
            vo().is_dir(non_existing, self.public_folder["_id"])

    def test_mapping_creation(self):
        new_folder = Folder().createFolder(
            self.base_collection,
            random_string(),
            creator=self.users["sally"],
            parentType="collection",
            public=True,
            reuseExisting=True,
        )

        resp = self.request(
            path="/folder/{_id}".format(**new_folder),
            method="PUT",
            user=self.users["sally"],
            params={
                "fsPath": "/etc",
                "isMapping": True
            },  # H4ck3r detected!
        )
        self.assertStatus(resp, 403)
        self.assertEqual(
            resp.json,
            {
                "message": "Must be admin to setup virtual folders.",
                "type": "access"
            },
        )

        resp = self.request(
            path="/folder/{_id}".format(**new_folder),
            method="PUT",
            user=self.users["admin"],
            params={
                "fsPath": "/etc",
                "isMapping": True
            },  # G0d
        )
        self.assertStatusOk(resp)
        self.assertHasKeys(resp.json, ["fsPath", "isMapping"])
        self.assertEqual(resp.json["fsPath"], "/etc")
        self.assertEqual(resp.json["isMapping"], True)

        Folder().remove(new_folder)

    def tearDown(self):
        Folder().remove(self.public_folder)
        Collection().remove(self.base_collection)
        for user in self.users.values():
            User().remove(user)
        for root in (self.public_root, ):
            shutil.rmtree(root)
        super(BasicOperationsTestCase, self).tearDown()