示例#1
0
    def __upload(self, file, remote: str = None) -> StackFile:
        """
        Core logic of the upload, previous method simply converts the name
        to a file IO pointer and passes it to this method
        :param file: File pointer
        :param path: Remote path
        :param name: Remote name
        :return: StackFile instance
        """
        if not remote and not getattr(file, 'name', None):
            raise StackException(
                "Unable to determine remote file name, either set "
                "it via file.name or by passing the 'remote' parameter")

        name = remote or file.name
        path = join(self.__cwd, name)
        name = basename(name)

        try:
            resp = self.http.webdav('PUT', path, data=file, stream=True)
            assert resp.status_code == 201, 'Expected 201 (created) status code'
            return self.file(path)

        except (AssertionError, RequestException) as e:
            raise StackException(e)
示例#2
0
    def download_into(self,
                      file: str,
                      buffer: Union[BufferedIOBase, BytesIO, BinaryIO] = None,
                      remote_path: str = None):
        """
        Download a file from your STACK account
        :param file: File name to download
        :param remote_path: Path to find the file in
        :param buffer: Buffer to download into (BytesIO, StringIO, file pointer)
        :return: BytesIO buffer
        """
        if not remote_path:
            remote_path = self.__cwd

        file = join(remote_path, file.lstrip("/"))

        if not buffer:
            buffer = BytesIO()

        if not hasattr(buffer, 'write'):
            raise StackException(
                "Download buffer must be a binary IO type, please "
                "use BytesIO or open your file in 'wb' mode.")

        try:
            resp = self.http.webdav('GET', file, stream=True)
            shutil.copyfileobj(resp.raw, buffer)
            buffer.seek(0)
            return buffer

        except RequestException as e:
            raise StackException(e)
示例#3
0
    def users(self) -> Iterable[StackUser]:
        """
        Get an iterator of all users registered to this account
        :return: StackUser iterator
        """
        limit = 50
        offset = 0
        amount = None

        while amount is None or (amount is not None and offset < int(amount)):
            params = {
                "public": False,
                "offset": offset,
                "limit": limit,
                "query": ""
            }
            resp = self.http.get("/api/users", params=params)

            if resp.status_code == 403:
                raise StackException(
                    'Unable to list users, access denied. '
                    'Please log in with the administrator account.',
                    resp=resp)

            users = resp.json()

            yield from (StackUser(stack=self, props=u)
                        for u in users.get("users") or [])

            amount = int(users.get("amountUsers"))
            offset += limit
示例#4
0
    def ls(self,
           search: str = "",
           order: str = "asc",
           path: str = None) -> Iterable[Union[StackFile, StackDirectory]]:
        """
        List the current (or other) directory
        :param path: Path to list, optional (default: CWD)
        :param search: Search files with a given name
        :param order: What way should the files be ordered? 
        :return: StackNode iterator
        """
        if order not in ("asc", "desc"):
            raise StackException("Invalid order parameter, got '{}', allowed "
                                 "values: 'asc', 'desc'".format(order))

        if not path:
            path = self.__cwd

        offset = 0
        amount = None

        while amount is None or (amount is not None and offset < int(amount)):
            files = self.__files(path, offset, query=search, order=order)
            offset += len(files.get("nodes") or [])
            amount = int(files.get("amount"))
            yield from self.__nodes_to_objects(files.get("nodes") or [])
示例#5
0
    def move(self, path: str):
        """
        Move the file / directory to another location
        :param path: New path to send to, accepts absolute and relative paths
        :return: None
        """
        if path.endswith("/"):
            path = join(path, self.name)

        if path.startswith("../"):
            path = realpath(join(self.directory, path))
        elif not path.startswith("/") or path.startswith("./"):
            path = join(self.directory, path.lstrip('./'))

        try:
            dest = join(self._http.webdav_base_url, path.lstrip('/'))
            resp = self._http.webdav('MOVE',
                                     self.path,
                                     headers={'Destination': dest})
            assert resp.status_code in (
                201, 403), 'Expected 201 (created) status code'
            self._props["path"] = path
            self.refresh()

        except (RequestException, AssertionError) as e:
            raise StackException(e)
示例#6
0
    def create_user(self,
                    name: str,
                    username: str,
                    password: str,
                    disk_quota: int = None) -> StackUser:
        """
        Create a STACK user
        :param name: User to create
        :param username: Username
        :param password: Password
        :param disk_quota: Disk quota in bytes, if set to None user is allowed to use infinite space
        :return: The newly created stack user
        """
        if self.enforce_password_policy:
            if len(password) < 8:
                raise StackException(
                    "Password must be at least 8 characters long!")

        disk_quota = int(disk_quota) if disk_quota else -1
        payload = {
            "action": "create",
            "user": {
                "username": username,
                "newUser": True,
                "isPublic": False,
                "password": password,
                "displayName": name,
                "quota": disk_quota
            }
        }

        resp = self.http.post("/api/users/update", json=[payload])

        if resp.status_code == 409:
            raise StackException(
                "Unable to create user '{}', either you don't have permission "
                "to do so or the user already exists!".format(username),
                resp=resp)

        data = resp.json()

        if data.get("status") != "ok":
            raise StackException(
                "Expected 'ok' status, got response: {}".format(data),
                resp=resp)

        return self.user(username)
示例#7
0
    def __node(self, name: str):
        """
        Get a node by name
        :param name: Node name to find
        :return: Node object
        """
        if name.startswith("/"):
            path = name
        else:
            path = join(self.__cwd, name)

        resp = self.http.get("/api/pathinfo", params={"path": path})

        if resp.status_code == 404:
            raise StackException('No such file or directory.', resp=resp)

        if resp.status_code != 200:
            raise StackException(resp.text.strip(), resp=resp)

        return self.__node_to_object(resp.json())
示例#8
0
    def directory(self, name: str) -> StackDirectory:
        """
        Get a directory by name
        :param name: Name to find
        :return: Directory object
        """
        node = self.__node(name)

        if isinstance(node, StackFile):
            raise StackException("Directory '{}' is a file!".format(name))

        return node
示例#9
0
    def file(self, name: str) -> StackFile:
        """
        Get a file by name
        :param name: Name to find
        :return: File object
        """
        node = self.__node(name)

        if isinstance(node, StackDirectory):
            raise StackException("File '{}' is a directory!".format(name))

        return node
示例#10
0
    def user(self, name: str) -> StackUser:
        """
        Get a user by name
        :param name: Name to find
        :return: Stack user
        """
        params = {"public": False, "offset": 0, "limit": 1, "query": name}
        users = self.http.get("/api/users", params=params)

        if users.status_code == 403:
            raise StackException(
                'Unable to list users, access denied. '
                'Please log in with the administrator account.',
                resp=users)

        data = users.json()

        if not any(data.get("users") or []):
            raise StackException("Unable to find user '{}'".format(name),
                                 resp=users)

        return StackUser(stack=self, props=data.get("users", [])[0])
示例#11
0
    def delete(self):
        """
        Delete the current user
        :return: None
        """
        data = {"action": "delete", "user": self._props}
        resp = self._http.post("/api/users/update", json=[data], csrf=True)
        resp_data = resp.json()

        if not resp_data.get("status") == "ok":
            raise StackException(
                "Unable to delete user '{}', expected status 'ok' "
                "and got response: {}".format(self.username, resp_data),
                resp=resp)
示例#12
0
    def mkdir(self, name: str, path: str = None) -> StackDirectory:
        """
        Make a new directory
        :param name: Directory name to create
        :param path: Directory to create it in
        :return: StackDirectory
        """
        if not path:
            path = self.__cwd

        path = join(path, name)

        try:
            self.http.webdav('MKCOL', path)
            return self.directory(path)

        except RequestException as e:
            raise StackException(e)
示例#13
0
    def save(self):
        """
        Save the current user state
        :return: None
        """
        resp = self._http.post("/api/users/update",
                               json=[{
                                   "action": "update",
                                   "user": self._props
                               }],
                               csrf=True)

        resp_data = resp.json()

        if resp_data.get("status") != "ok":
            raise StackException(
                "Unable to set properties. Expected status 'ok' "
                "and got response: {}".format(resp_data),
                resp=resp)
示例#14
0
    def login(self) -> None:
        """
        Log into STACK
        :return: None
        :raises: StackException
        """
        data = {"username": self.__username, "password": self.__password}
        resp = self.http.post("/login",
                              data=data,
                              allow_redirects=False,
                              csrf=False)
        self.__logged_in = resp.status_code in (301, 302)

        if self.__logged_in:
            logging.debug("Logged in successfully")
        else:
            logging.debug(
                "No redirect detected in login request, invalid password.")
            raise StackException("Invalid username or password.", resp=resp)
示例#15
0
    def upload(self, file, *, remote: str = None) -> StackFile:
        """
        Upload a file to Stack
        :param file: IO pointer or string containing a path to a file
        :param remote:
            Custom name which will be used on
            the remote server, defaults to file.name
            in the current working directory
        :return: Instance of a stack file
        """
        if hasattr(file, 'read'):
            return self.__upload(file=file, remote=remote)

        if isinstance(file, str):
            with open(file, "rb") as fd:
                return self.__upload(file=fd, remote=remote)

        raise StackException("File should either be a path to a file on "
                             "disk or an IO type, got: {}".format(type(file)))
示例#16
0
    def walk(self,
             path: str = None,
             order: str = "asc") -> Iterable[StackFile]:
        """
        Recursively walk through the entire directory tree, yielding
        each file in all directories

        :param path: Starting path to walk
        :param order: Walk order (asc, desc)
        :return: Generator of StackFile's
        """
        if order not in ('asc', 'desc'):
            raise StackException(
                'Invalid order: {!r}, accepted values: asc, desc'.format(
                    order))

        if not path:
            path = self.__cwd

        for node in self.ls(path=path, order=order):
            if isinstance(node, StackDirectory):
                yield from self.walk(node.path, order=order)
            else:
                yield node