Esempio n. 1
0
 def delete(self):
     """For now, it is not possible to delete a workspace through the webdav client."""
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 2
0
def parseXmlBody(environ, allowEmpty=False):
    """Read request body XML into an etree.Element.
    
    Return None, if no request body was sent.
    Raise HTTP_BAD_REQUEST, if something else went wrong.
    
    TODO: this is a very relaxed interpretation: should we raise HTTP_BAD_REQUEST 
    instead, if CONTENT_LENGTH is missing, invalid, or 0?
    
    RFC: For compatibility with HTTP/1.0 applications, HTTP/1.1 requests containing 
    a message-body MUST include a valid Content-Length header field unless the 
    server is known to be HTTP/1.1 compliant. 
    If a request contains a message-body and a Content-Length is not given, the 
    server SHOULD respond with 400 (bad request) if it cannot determine the 
    length of the message, or with 411 (length required) if it wishes to insist 
    on receiving a valid Content-Length."
    
    So I'd say, we should accept a missing CONTENT_LENGTH, and try to read the 
    content anyway.
    But WSGI doesn't guarantee to support input.read() without length(?).
    At least it locked, when I tried it with a request that had a missing 
    content-type and no body.
    
    Current approach: if CONTENT_LENGTH is
    
    - valid and >0:
      read body (exactly <CONTENT_LENGTH> bytes) and parse the result.  
    - 0: 
      Assume empty body and return None or raise exception.
    - invalid (negative or not a number:
      raise HTTP_BAD_REQUEST  
    - missing: 
      NOT: Try to read body until end and parse the result.
      BUT: assume '0'  
    - empty string: 
      WSGI allows it to be empty or absent: treated like 'missing'.  
    """
    # 
    clHeader = environ.get("CONTENT_LENGTH", "").strip() 
#    contentLength = -1 # read all of stream
    if clHeader == "":
        # No Content-Length given: read to end of stream 
        # TODO: etree.parse() locks, if input is invalid?
#        pfroot = etree.parse(environ["wsgi.input"]).getroot()
#        requestbody = environ["wsgi.input"].read()  # TODO: read() should be called in a loop?
        requestbody = ""
    else:
        try:
            contentLength = long(clHeader)
            if contentLength < 0:   
                raise DAVError(HTTP_BAD_REQUEST, "Negative content-length.")
        except ValueError:
            raise DAVError(HTTP_BAD_REQUEST, "content-length is not numeric.")
        
        if contentLength == 0:
            requestbody = ""
        else:
            requestbody = environ["wsgi.input"].read(contentLength)
            environ["wsgidav.all_input_read"] = 1

    if requestbody == "":
        if allowEmpty:
            return None
        else:
            raise DAVError(HTTP_BAD_REQUEST, "Body must not be empty.")
    
    try:
        rootEL = etree.fromstring(requestbody)
    except Exception, e:
        raise DAVError(HTTP_BAD_REQUEST, "Invalid XML format.", srcexception=e)   
Esempio n. 3
0
 def copy_move_single(self, dest_path, is_move):
     """See DAVResource.copy_move_single() """
     if self.provider.readonly:
         raise DAVError(HTTP_FORBIDDEN)
     raise DAVError(HTTP_FORBIDDEN)
 def handleMove(self, destPath):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 5
0
 def _checkWriteAccess(self):
     """Raise HTTP_FORBIDDEN, if resource is unwritable."""
     if self.rev is not None:
         # Only working directory may be edited
         raise DAVError(HTTP_FORBIDDEN)
class SeafDirResource(DAVCollection):
    def __init__(self, path, repo, rel_path, obj, environ):
        super(SeafDirResource, self).__init__(path, environ)
        self.repo = repo
        self.rel_path = rel_path
        self.obj = obj
        self.username = environ.get("http_authenticator.username", "")

    # Getter methods for standard live properties
    def getCreationDate(self):
        #        return int(time.time())
        return None

    def getDisplayName(self):
        return self.name

    def getDirectoryInfo(self):
        return None

    def getEtag(self):
        return utf8_wrap(self.obj.obj_id)

    def getLastModified(self):
        #        return int(time.time())
        return None

    def getMemberNames(self):
        namelist = []
        for e in self.obj.dirs:
            namelist.append(e[0])
        for e in self.obj.files:
            namelist.append(e[0])
        return namelist

    def getMember(self, name):
        member_rel_path = "/".join([self.rel_path, name])
        member_path = "/".join([self.path, name])
        member = self.obj.lookup(name)

        if not member:
            raise DAVError(HTTP_NOT_FOUND)

        if isinstance(member, SeafFile):
            return SeafileResource(member_path, self.repo, member_rel_path,
                                   member, self.environ)
        else:
            return SeafDirResource(member_path, self.repo, member_rel_path,
                                   member, self.environ)

    def getMemberList(self):
        member_list = []
        d = self.obj

        if d.version == 0:
            file_mtimes = []
            try:
                file_mtimes = seafile_api.get_files_last_modified(
                    self.repo.id, self.rel_path, -1)
            except:
                raise DAVError(HTTP_INTERNAL_ERROR)

            mtimes = UTF8Dict()
            for entry in file_mtimes:
                mtimes[entry.file_name] = entry.last_modified
        for name, dent in d.dirents.iteritems():
            member_path = utf8_path_join(self.path, name)
            member_rel_path = utf8_path_join(self.rel_path, name)

            if dent.is_dir():
                obj = fs_mgr.load_seafdir(d.store_id, d.version, dent.id)
                res = SeafDirResource(member_path, self.repo, member_rel_path,
                                      obj, self.environ)
            elif dent.is_file():
                obj = fs_mgr.load_seafile(d.store_id, d.version, dent.id)
                res = SeafileResource(member_path, self.repo, member_rel_path,
                                      obj, self.environ)
            else:
                continue

            if d.version == 1:
                obj.last_modified = dent.mtime
            else:
                obj.last_modified = mtimes[name]

            member_list.append(res)

        return member_list

    # --- Read / write ---------------------------------------------------------

    def createEmptyResource(self, name):
        """Create an empty (length-0) resource.

        See DAVResource.createEmptyResource()
        """
        assert not "/" in name
        if self.provider.readonly:
            raise DAVError(HTTP_FORBIDDEN)

        if seafile_api.check_permission(self.repo.id, self.username) != "rw":
            raise DAVError(HTTP_FORBIDDEN)

        try:
            seafile_api.post_empty_file(self.repo.id, self.rel_path, name,
                                        self.username)
        except SearpcError, e:
            if e.msg == 'Invalid file name':
                raise DAVError(HTTP_BAD_REQUEST)
            raise

        # Repo was updated, can't use self.repo
        repo = seafile_api.get_repo(self.repo.id)
        if not repo:
            raise DAVError(HTTP_INTERNAL_ERROR)

        member_rel_path = "/".join([self.rel_path, name])
        member_path = "/".join([self.path, name])
        obj = resolveRepoPath(repo, member_rel_path)
        if not obj or not isinstance(obj, SeafFile):
            raise DAVError(HTTP_INTERNAL_ERROR)

        return SeafileResource(member_path, repo, member_rel_path, obj,
                               self.environ)
 def createCollection(self, name):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 8
0
 def move_recursive(self, dest_path):
     """ It is forbidden to delete a course description file"""
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 9
0
	def copyMoveSingle(self, destPath, isMove):
		raise DAVError(HTTP_FORBIDDEN)
		
Esempio n. 10
0
 def delete(self):
     """ It is forbidden to delete a course description file"""
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 11
0
 def copy_move_single(self, dest_path, is_move):
     """ It is forbidden to delete a course description file"""
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 12
0
    def check_write_permission(self, url, depth, token_list, principal):
        """Check, if <principal> can modify <url>, otherwise raise HTTP_LOCKED.

        If modifying <url> is prevented by a lock, DAVError(HTTP_LOCKED) is
        raised. An embedded DAVErrorCondition contains the conflicting locks.

        <url> may be modified by <principal>, if it is not currently locked
        directly or indirectly (i.e. by a locked parent).
        For depth-infinity operations, <url> also must not have locked children.

        It is not enough to check whether a lock is owned by <principal>, but
        also the token must be passed with the request. Because <principal> may
        run two different applications.

        See http://www.webdav.org/specs/rfc4918.html#lock-model
            http://www.webdav.org/specs/rfc4918.html#rfc.section.7.4

        TODO: verify assumptions:
        - Parent locks WILL NOT be conflicting, if they are depth-0.
        - Exclusive child locks WILL be conflicting, even if they are owned by <principal>.

        @param url: URL that shall be modified, created, moved, or deleted
        @param depth: "0"|"infinity"
        @param token_list: list of lock tokens, that the principal submitted in If: header
        @param principal: name of the principal requesting a lock

        @return: None or raise error
        """
        assert compat.is_native(url)
        assert depth in ("0", "infinity")
        _logger.debug("check_write_permission({}, {}, {}, {})".format(
            url, depth, token_list, principal))

        # Error precondition to collect conflicting URLs
        errcond = DAVErrorCondition(PRECONDITION_CODE_LockConflict)

        self._lock.acquire_read()
        try:
            # Check url and all parents for conflicting locks
            u = url
            while u:
                ll = self.get_url_lock_list(u)
                _logger.debug("  checking {}".format(u))
                for l in ll:
                    _logger.debug("     l={}".format(lock_string(l)))
                    if u != url and l["depth"] != "infinity":
                        # We only consider parents with Depth: inifinity
                        continue
                    elif principal == l["principal"] and l[
                            "token"] in token_list:
                        # User owns this lock
                        continue
                    else:
                        # Token is owned by principal, but not passed with lock list
                        _logger.debug(
                            " -> DENIED due to locked parent {}".format(
                                lock_string(l)))
                        errcond.add_href(l["root"])
                u = util.get_uri_parent(u)

            if depth == "infinity":
                # Check child URLs for conflicting locks
                childLocks = self.storage.get_lock_list(url,
                                                        include_root=False,
                                                        include_children=True,
                                                        token_only=False)

                for l in childLocks:
                    assert util.is_child_uri(url, l["root"])
                    #                    if util.is_child_uri(url, l["root"]):
                    _logger.debug(" -> DENIED due to locked child {}".format(
                        lock_string(l)))
                    errcond.add_href(l["root"])
        finally:
            self._lock.release()

        # If there were conflicts, raise HTTP_LOCKED for <url>, and pass
        # conflicting resource with 'no-conflicting-lock' precondition
        if len(errcond.hrefs) > 0:
            raise DAVError(HTTP_LOCKED, err_condition=errcond)
        return
Esempio n. 13
0
    def _check_lock_permission(self, url, lock_type, lock_scope, lock_depth,
                               token_list, principal):
        """Check, if <principal> can lock <url>, otherwise raise an error.

        If locking <url> would create a conflict, DAVError(HTTP_LOCKED) is
        raised. An embedded DAVErrorCondition contains the conflicting resource.

        @see http://www.webdav.org/specs/rfc4918.html#lock-model

        - Parent locks WILL NOT be conflicting, if they are depth-0.
        - Exclusive depth-infinity parent locks WILL be conflicting, even if
          they are owned by <principal>.
        - Child locks WILL NOT be conflicting, if we request a depth-0 lock.
        - Exclusive child locks WILL be conflicting, even if they are owned by
          <principal>. (7.7)
        - It is not enough to check whether a lock is owned by <principal>, but
          also the token must be passed with the request. (Because <principal>
          may run two different applications on his client.)
        - <principal> cannot lock-exclusive, if he holds a parent shared-lock.
          (This would only make sense, if he was the only shared-lock holder.)
        - TODO: litmus tries to acquire a shared lock on one resource twice
          (locks: 27 'double_sharedlock') and fails, when we return HTTP_LOCKED.
          So we allow multi shared locks on a resource even for the same
          principal.

        @param url: URL that shall be locked
        @param lock_type: "write"
        @param lock_scope: "shared"|"exclusive"
        @param lock_depth: "0"|"infinity"
        @param token_list: list of lock tokens, that the user submitted in If: header
        @param principal: name of the principal requesting a lock

        @return: None (or raise)
        """
        assert lock_type == "write"
        assert lock_scope in ("shared", "exclusive")
        assert lock_depth in ("0", "infinity")

        _logger.debug("checkLockPermission({}, {}, {}, {})".format(
            url, lock_scope, lock_depth, principal))

        # Error precondition to collect conflicting URLs
        errcond = DAVErrorCondition(PRECONDITION_CODE_LockConflict)

        self._lock.acquire_read()
        try:
            # Check url and all parents for conflicting locks
            u = url
            while u:
                ll = self.get_url_lock_list(u)
                for l in ll:
                    _logger.debug("    check parent {}, {}".format(
                        u, lock_string(l)))
                    if u != url and l["depth"] != "infinity":
                        # We only consider parents with Depth: infinity
                        continue
                    elif l["scope"] == "shared" and lock_scope == "shared":
                        # Only compatible with shared locks (even by same
                        # principal)
                        continue
                    # Lock conflict
                    _logger.debug(" -> DENIED due to locked parent {}".format(
                        lock_string(l)))
                    errcond.add_href(l["root"])
                u = util.get_uri_parent(u)

            if lock_depth == "infinity":
                # Check child URLs for conflicting locks
                childLocks = self.storage.get_lock_list(url,
                                                        include_root=False,
                                                        include_children=True,
                                                        token_only=False)

                for l in childLocks:
                    assert util.is_child_uri(url, l["root"])
                    #                    if util.is_child_uri(url, l["root"]):
                    _logger.debug(" -> DENIED due to locked child {}".format(
                        lock_string(l)))
                    errcond.add_href(l["root"])
        finally:
            self._lock.release()

        # If there were conflicts, raise HTTP_LOCKED for <url>, and pass
        # conflicting resource with 'no-conflicting-lock' precondition
        if len(errcond.hrefs) > 0:
            raise DAVError(HTTP_LOCKED, err_condition=errcond)
        return
Esempio n. 14
0
 def _check_read_access(self):
     """Raise HTTP_FORBIDDEN, if resource is unreadable."""
     for role in ("admin", "editor", "reader"):
         if role in self._roles:
             return
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 15
0
 def copy_move_single(self, dest_path, is_move):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 16
0
	def beginWrite(self, contentType=None):
		if isinstance(self.file, Writable):
			return self.file.writestream()
		raise DAVError(HTTP_FORBIDDEN)	
Esempio n. 17
0
 def move_recursive(self, dest_path):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 18
0
	def delete(self):
		if isinstance(self.file, Writable):
			return self.file.deleteMe()
		raise DAVError(HTTP_FORBIDDEN)
Esempio n. 19
0
 def createEmptyResource(self, name):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 20
0
	def createEmptyResource(self, name):
		if isinstance(self.dir, WriteableDirectory):
			return self.provider.getResourceInst(j(self.path, self.dir.new_file(name).name))
		raise DAVError(HTTP_FORBIDDEN)
Esempio n. 21
0
 def handleDelete(self):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 22
0
 def get_member(self, name):
     token: Token = self.environ["manabi.token"]
     path = token.path
     if Path(name) != path:
         raise DAVError(HTTP_FORBIDDEN)
     return super().get_member(name)
Esempio n. 23
0
 def handleCopy(self, destPath, depthInfinity):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 24
0
 def create_empty_resource(self, name):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 25
0
    def setPropertyValue(self, propname, value, dryRun=False):
        """Set or remove property value.

        See DAVResource.setPropertyValue()
        """
        raise DAVError(HTTP_FORBIDDEN)
Esempio n. 26
0
 def set_last_modified(self, dest_path, time_stamp, dry_run):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 27
0
def evaluateHTTPConditionals(davres, lastmodified, entitytag, environ):
    """Handle 'If-...:' headers (but not 'If:' header).
    
    If-Match     
        @see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.24
        Only perform the action if the client supplied entity matches the 
        same entity on the server. This is mainly for methods like 
        PUT to only update a resource if it has not been modified since the 
        user last updated it.     
        If-Match: "737060cd8c284d8af7ad3082f209582d"
    If-Modified-Since     
        @see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25
        Allows a 304 Not Modified to be returned if content is unchanged     
        If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
    If-None-Match     
        @see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26
        Allows a 304 Not Modified to be returned if content is unchanged, 
        see HTTP ETag     
        If-None-Match: "737060cd8c284d8af7ad3082f209582d"
    If-Unmodified-Since     
        @see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.28
        Only send the response if the entity has not been modified since a 
        specific time.
    """
    if not davres:
        return
    ## Conditions

    # An HTTP/1.1 origin server, upon receiving a conditional request that includes both a Last-Modified date
    # (e.g., in an If-Modified-Since or If-Unmodified-Since header field) and one or more entity tags (e.g., 
    # in an If-Match, If-None-Match, or If-Range header field) as cache validators, MUST NOT return a response 
    # status of 304 (Not Modified) unless doing so is consistent with all of the conditional header fields in 
    # the request.
    
    if "HTTP_IF_MATCH" in environ and davres.supportEtag(): 
        ifmatchlist = environ["HTTP_IF_MATCH"].split(",")
        for ifmatchtag in ifmatchlist:
            ifmatchtag = ifmatchtag.strip(" \"\t")
            if ifmatchtag == entitytag or ifmatchtag == "*":
                break   
            raise DAVError(HTTP_PRECONDITION_FAILED,
                           "If-Match header condition failed")

    # TODO: after the refactoring
    ifModifiedSinceFailed = False
    if "HTTP_IF_MODIFIED_SINCE" in environ and davres.supportModified(): 
        ifmodtime = parseTimeString(environ["HTTP_IF_MODIFIED_SINCE"])
        if ifmodtime and ifmodtime > lastmodified:
            ifModifiedSinceFailed = True

    # If-None-Match 
    # If none of the entity tags match, then the server MAY perform the requested method as if the 
    # If-None-Match header field did not exist, but MUST also ignore any If-Modified-Since header field
    # (s) in the request. That is, if no entity tags match, then the server MUST NOT return a 304 (Not Modified) 
    # response.
    ignoreIfModifiedSince = False         
    if "HTTP_IF_NONE_MATCH" in environ and davres.supportEtag():          
        ifmatchlist = environ["HTTP_IF_NONE_MATCH"].split(",")
        for ifmatchtag in ifmatchlist:
            ifmatchtag = ifmatchtag.strip(" \"\t")
            if ifmatchtag == entitytag or ifmatchtag == "*":
                # ETag matched. If it's a GET request and we don't have an 
                # conflicting If-Modified header, we return NOT_MODIFIED
                if environ["REQUEST_METHOD"] in ("GET", "HEAD") and not ifModifiedSinceFailed:
                    raise DAVError(HTTP_NOT_MODIFIED,
                                   "If-None-Match header failed")
                raise DAVError(HTTP_PRECONDITION_FAILED,
                               "If-None-Match header condition failed")
        ignoreIfModifiedSince = True

    if "HTTP_IF_UNMODIFIED_SINCE" in environ and davres.supportModified(): 
        ifunmodtime = parseTimeString(environ["HTTP_IF_UNMODIFIED_SINCE"])
        if ifunmodtime and ifunmodtime <= lastmodified:
            raise DAVError(HTTP_PRECONDITION_FAILED,
                           "If-Unmodified-Since header condition failed")

    if ifModifiedSinceFailed and not ignoreIfModifiedSince:
        raise DAVError(HTTP_NOT_MODIFIED,
                       "If-Modified-Since header condition failed")

    return
Esempio n. 28
0
 def delete(self):
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 29
0
 def move_recursive(self, dest_path):
     """See DAVResource.move_recursive() """
     if self.provider.readonly:
         raise DAVError(HTTP_FORBIDDEN)
     raise DAVError(HTTP_FORBIDDEN)
Esempio n. 30
0
 def beginWrite(self, contentType=None):
     raise DAVError(HTTP_FORBIDDEN)