Exemplo n.º 1
0
    def _validate_dates(self):
        """
        Validate Date/X-Oss-Date headers for signature v2
        :raises: AccessDenied
        :raises: RequestTimeTooSkewed
        """
        if self._is_query_auth:
            self._validate_expire_param()
            # TODO: make sure the case if timestamp param in query
            return

        date_header = self.headers.get('Date')
        oss_date_header = self.headers.get('X-Oss-Date')
        if not date_header and not oss_date_header:
            raise AccessDenied('OSS authentication requires a valid Date '
                               'or x-oss-date header')

        # Anyways, request timestamp should be validated
        epoch = OssTimestamp(0)
        if self.timestamp < epoch:
            raise AccessDenied()

        # If the standard date is too far ahead or behind, it is an
        # error
        delta = 60 * 5
        if abs(int(self.timestamp) - int(OssTimestamp.now())) > delta:
            raise RequestTimeTooSkewed()
Exemplo n.º 2
0
    def _parse_query_authentication(self):
        try:
            access = self.params['OSSAccessKeyId']
            expires = self.params['Expires']
            sig = self.params['Signature']
        except KeyError:
            raise AccessDenied()

        if not all([access, sig, expires]):
            raise AccessDenied()

        return access, sig
Exemplo n.º 3
0
    def check_owner(self, user_id):
        """
        Check that the user is an owner.
        """
        if not CONF.oss_acl:
            # Ignore Oss2Swift ACL.
            return

        if not self.owner.id:
            if CONF.allow_no_owner:
                # No owner means public.
                return
            raise AccessDenied()

        if user_id != self.owner.id:
            raise AccessDenied()
Exemplo n.º 4
0
 def _parse_header_authentication(self):
     auth_str = self.headers['Authorization']
     if not auth_str.startswith('OSS ') or ':' not in auth_str:
         raise AccessDenied()
     # This means signature format V2
     access, sig = auth_str.split(' ', 1)[1].rsplit(':', 1)
     return access, sig
Exemplo n.º 5
0
    def _validate_expire_param(self):
        """
        Validate Expires in query parameters
        :raises: AccessDenied
        """
        # Expires header is a float since epoch
        try:
            ex = OssTimestamp(float(self.params['Expires']))
        except ValueError:
            raise AccessDenied()

        if OssTimestamp.now() > ex:
            raise AccessDenied('Request has expired')

        if ex >= 2**31:
            raise AccessDenied(
                'Invalid date (should be seconds since epoch): %s' %
                self.params['Expires'])
Exemplo n.º 6
0
    def timestamp(self):
        if not self._timestamp:
            try:
                if self._is_query_auth and 'Timestamp' in self.params:
                    timestamp = mktime(self.params['Timestamp'],
                                       X_OSS_DATE_FORMAT)
                else:
                    timestamp = mktime(
                        self.headers.get('X-Oss-Date',
                                         self.headers.get('Date')))
            except ValueError:
                raise AccessDenied('OSS authentication requires a valid Date '
                                   'or x-oss-date header')

            try:
                self._timestamp = OssTimestamp(timestamp)
            except ValueError:
                raise AccessDenied()

        return self._timestamp
Exemplo n.º 7
0
    def _string_to_sign(self):
        """
        Create 'StringToSign' value in Amazon terminology for v2.
        """
        oss_headers = {}

        buf = "%s\n%s\n%s\n" % (self.method, self.headers.get(
            'Content-MD5', ''), self.headers.get('Content-Type') or '')

        for oss_header in sorted((key.lower() for key in self.headers
                                  if key.lower().startswith('x-oss-'))):
            oss_headers[oss_header] = self.headers[oss_header]

        if self._is_header_auth:
            if 'x-oss-date' in oss_headers:
                buf += "\n"
            elif 'Date' in self.headers:
                buf += "%s\n" % self.headers['Date']
        elif self._is_query_auth:
            buf += "%s\n" % self.params['Expires']
        else:
            # Should have already raised NotOssRequest in _parse_auth_info,
            # but as a sanity check...
            raise AccessDenied()

        for k in sorted(key.lower() for key in oss_headers):
            buf += "%s:%s\n" % (k, oss_headers[k])

        path = self._canonical_uri()
        if self.query_string:
            path += '?' + self.query_string
        if '?' in path:
            path, args = path.split('?', 1)
            params = []
            for key, value in sorted(self.params.items()):
                if key in ALLOWED_SUB_RESOURCES:
                    params.append('%s=%s' % (key, value) if value else key)
            if params:
                return '%s%s?%s' % (buf, path, '&'.join(params))

        return buf + path
Exemplo n.º 8
0
    def check_permission(self, user_id, grant):
        """
        Check that the user has a permission.
        """
        if not CONF.oss_acl:
            # Ignore Oss2Swift ACL.
            return

        try:
            # owners have full control permission
            self.check_owner(user_id)
            return
        except AccessDenied:
            pass

        if grant.upper in PERMISSIONS:
            g = self.grant
            if g.allow(user_id, 'public-read-write') or \
                    g.allow(user_id, grant):
                return

        raise AccessDenied()
Exemplo n.º 9
0
    def GET(self, req):
        """
        Handle GET Bucket website request
        """
        resp = req.get_response(self.app)
        if resp.bucket_acl == 'private':
            raise AccessDenied()
        if 'x-oss-web-index' not in resp.headers:
            raise NoSuchWebsiteConfiguration()
        web_index = resp.headers['x-oss-web-index']
        web_error = resp.headers['x-oss-web-error']
        elem = Element('WebsiteConfiguration')
        index = SubElement(elem, 'IndexDocument')
        SubElement(index, 'Suffix').text = web_index
        if web_error is not None:
            error_doc = SubElement(elem, 'ErrorDocument')
            key = SubElement(error_doc, 'Key')
            if key is None and key == '':
                raise NoSuchKey()
            key.text = web_error
        body = tostring(elem)

        return HTTPOk(body=body, content_type='application/xml')
Exemplo n.º 10
0
    def _get_response(self,
                      app,
                      method,
                      container,
                      obj,
                      headers=None,
                      body=None,
                      query=None):
        """
        Calls the application with this request's environment.  Returns a
        Response object that wraps up the application's result.
        """

        method = method or self.environ['REQUEST_METHOD']
        if container is None:
            container = self.container_name
        if obj is None:
            obj = self.object_name
        if str(obj).startswith('/'):
            raise InvalidObjectName

        sw_req = self.to_swift_req(method,
                                   container,
                                   obj,
                                   headers=headers,
                                   body=body,
                                   query=query)
        if container and obj:
            # before obj request container
            req = self.to_swift_req(method,
                                    container,
                                    obj='',
                                    headers=headers,
                                    body=body,
                                    query=query)
            resp = req.get_response(app)
            if resp.status_int == HTTP_NOT_FOUND:
                raise NoSuchBucket(container)
            else:
                sw_resp = sw_req.get_response(app)
        else:
            sw_resp = sw_req.get_response(app)
        # reuse account and tokens
        _, self.account, _ = split_path(sw_resp.environ['PATH_INFO'], 2, 3,
                                        True)
        self.account = utf8encode(self.account)

        resp = Response.from_swift_resp(sw_resp)
        if 'X-Container-Read' in sw_resp.headers:
            resp.headers['X-Container-Read'] = sw_resp.headers[
                'X-Container-Read']
        if 'X-Container-Write' in sw_resp.headers:
            resp.headers['X-Container-Write'] = sw_resp.headers[
                'X-Container-Write']
        status = resp.status_int  # pylint: disable-msg=E1101

        if not self.user_id:
            if 'HTTP_X_USER_NAME' in sw_resp.environ:
                # keystone
                self.user_id = \
                    utf8encode("%s:%s" %
                               (sw_resp.environ['HTTP_X_TENANT_NAME'],
                                sw_resp.environ['HTTP_X_USER_NAME']))
            else:
                # tempauth
                self.user_id = self.access_key

        success_codes = self._swift_success_codes(method, container, obj)
        error_codes = self._swift_error_codes(method, container, obj)

        if status in success_codes:
            return resp

        for key in resp.environ.keys():
            if 'swift.container' in key:
                if 'meta'.decode("utf8") in resp.environ[key]:
                    if 'web-index'.decode("utf8") in resp.environ[key][
                            'meta'.decode("utf8")]:
                        resp.headers['x-oss-web-index'] = resp.environ[key][
                            'meta'.decode("utf8")]['web-index'.decode("utf8")]
                    if 'web-error'.decode("utf8") in resp.environ[key][
                            'meta'.decode("utf8")]:
                        resp.headers['x-oss-web-error'] = resp.environ[key][
                            'meta'.decode("utf8")]['web-error'.decode("utf8")]
                        if status == HTTP_NOT_FOUND:
                            if obj.endswith('/'):
                                resp.headers['x-oss-index'] = resp.environ[
                                    key]['meta'.decode("utf8")][
                                        'web-index'.decode("utf8")]
                            return resp
        err_msg = resp.body
        if status in error_codes:
            err_resp = \
                error_codes[sw_resp.status_int]  # pylint: disable-msg=E1101
            if isinstance(err_resp, tuple):
                raise err_resp[0](*err_resp[1:])
            else:
                raise err_resp()
        if status == HTTP_BAD_REQUEST:
            raise BadSwiftRequest(err_msg)
        if status == HTTP_UNAUTHORIZED:
            raise SignatureDoesNotMatch()
        if status == HTTP_FORBIDDEN:
            raise AccessDenied()
        if status == HTTP_MOVED_PERMANENTLY:
            resp.headers['x-oss-website-redirect'] = 'true'
            return resp

        raise InternalError('unexpected status code %d' % status)