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()
def GETorHEAD(self, req): # if req.headers is not None and 'Accept-Encoding' in req.headers and req.headers['Accept-Encoding'] == 'gzip': # resp = req.get_response(self.app, method='GET') # resp.headers['Content-Encoding'] = 'gzip' # resp.headers['Transfer-encoding'] = 'chunked' # del resp.headers['Content-Length'] # resp.body = self._gen_gzip(resp.body) # else: resp = req.get_response(self.app, method='GET') if 'x-oss-index' in resp.headers: index = resp.headers['x-oss-index'] resp = req.get_response(self.app, obj=index, method='GET') if 'x-oss-web-error' in resp.headers: obj = resp.headers['x-oss-web-error'] resp = req.get_response(self.app, obj=obj, method='GET') if req.method == 'HEAD': resp.app_iter = None if 'x-oss-meta-validdate' in resp.headers: validDate = resp.headers['x-oss-meta-validdate'] if str(validDate).isdigit() and validDate > float( OssTimestamp.now().internal): raise ObjectInvalid() else: pass for key in ('content-type', 'content-language', 'expires', 'cache-control', 'content-disposition', 'content-encoding'): if 'response-' + key in req.params: resp.headers[key] = req.params['response-' + key] return resp
def PUT(self, req): """ Handle PUT Object and PUT Object (Copy) request """ # set X-Timestamp by oss2swift to use at copy resp body req_timestamp = OssTimestamp.now() expireDay = '' createDate = '' data = req.body do_crc64 = crcmod.mkCrcFun(0x142F0E1EBA9EA3693L, initCrc=0L, xorOut=0xffffffffffffffffL, rev=True) crcValue = do_crc64(data) req.headers['X-Timestamp'] = req_timestamp.internal req.headers['x-object-meta-object-type'] = 'Normal' req.headers['x-object-meta-hash-crc64ecma'] = str(crcValue) if all(h in req.headers for h in ('x-oss-copy-source', 'x-oss-copy-source-range')): raise InvalidArgument('x-oss-copy-source-range', req.headers['x-oss-copy-source-range'], 'Illegal copy header') req.check_copy_source(self.app) bucket_headers = {} bucket_headers = req.get_container_info(self.app) expireDay, createDate = self._parse_lifecycle(bucket_headers, req.object_name) if expireDay != '': try: days = int(expireDay) expire_sc = str( int(days * 2 * 3600 + float(req_timestamp.internal))) req.headers['X-Delete-At'] = expire_sc except: raise InvalidArgument('X-Deltete-At', days) elif createDate != '': try: unix_time = to_unixtime(createDate, '%Y-%m-%dT%H:%M:%S.000Z') if unix_time <= int(req_timestamp): pass else: req.headers['X-Object-Meta-ValidDate'] = unix_time except: raise InvalidArgument('X-Object-Meta-ValidDate', createDate) resp = req.get_response(self.app) if 'x-oss-copy-source' in req.headers: resp.append_copy_resp_body(req.controller_name, req_timestamp.ossxmlformat) # delete object metadata from response for key in list(resp.headers.keys()): if key.startswith('x-oss-meta-'): del resp.headers[key] resp.status = HTTP_OK resp.headers['x-oss-hash-crc64ecma'] = crcValue return resp
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'])
def test_object_PUT_copy_self_metadata_replace(self): date_header = self.get_date_header() timestamp = mktime(date_header) last_modified = OssTimestamp(timestamp).ossxmlformat header = {'x-oss-metadata-directive': 'REPLACE', 'Date': date_header} status, headers, body = self._test_object_PUT_copy_self( swob.HTTPOk, header, timestamp=timestamp) self.assertEqual(status.split()[0], '200') self.assertEqual(headers['Content-Type'], 'application/xml') self.assertTrue(headers.get('etag') is None) elem = fromstring(body, 'CopyObjectResult') self.assertEqual(elem.find('LastModified').text, last_modified) self.assertEqual(elem.find('ETag').text, '"%s"' % self.etag) _, _, headers = self.swift.calls_with_headers[-1] self.assertEqual(headers['X-Copy-From'], '/bucket/object') self.assertEqual(headers['Content-Length'], '0')
def test_object_PUT_copy_no_slash(self): date_header = self.get_date_header() timestamp = mktime(date_header) last_modified = OssTimestamp(timestamp).ossxmlformat # Some clients (like Boto) don't include the leading slash; # OSS seems to tolerate this so we should, too status, headers, body = self._test_object_PUT_copy( swob.HTTPOk, src_path='some/source', put_header={'Date': date_header}, timestamp=timestamp) self.assertEqual(status.split()[0], '200') self.assertEqual(headers['Content-Type'], 'application/xml') self.assertTrue(headers.get('etag') is None) self.assertTrue(headers.get('x-oss-meta-something') is None) elem = fromstring(body, 'CopyObjectResult') self.assertEqual(elem.find('LastModified').text, last_modified) self.assertEqual(elem.find('ETag').text, '"%s"' % self.etag) _, _, headers = self.swift.calls_with_headers[-1] self.assertEqual(headers['X-Copy-From'], '/some/source') self.assertEqual(headers['Content-Length'], '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
def PUT(self, req): """ Handles Upload Part and Upload Part Copy. """ if 'uploadId' not in req.params: raise InvalidArgument('ResourceType', 'partNumber', 'Unexpected query string parameter') try: part_number = int(req.params['partNumber']) if part_number < 1 or CONF.max_upload_part_num < part_number: raise Exception() except Exception: err_msg = 'Part number must be an integer between 1 and %d,' \ ' inclusive' % CONF.max_upload_part_num raise InvalidArgument('partNumber', req.params['partNumber'], err_msg) data = req.body upload_id = req.params['uploadId'] _check_upload_info(req, self.app, upload_id) req.container_name += MULTIUPLOAD_SUFFIX req.object_name = '%s/%s/%d' % (req.object_name, upload_id, part_number) req_timestamp = OssTimestamp.now() req.headers['X-Timestamp'] = req_timestamp.internal source_resp = req.check_copy_source(self.app) if 'x-oss-copy-source' in req.headers and \ 'x-oss-copy-source-range' in req.headers: rng = req.headers['x-oss-copy-source-range'] header_valid = True try: rng_obj = Range(rng) if len(rng_obj.ranges) != 1: header_valid = False except ValueError: header_valid = False if not header_valid: err_msg = ('The x-oss-copy-source-range value must be of the ' 'form bytes=first-last where first and last are ' 'the zero-based offsets of the first and last ' 'bytes to copy') raise InvalidArgument('x-oss-source-range', rng, err_msg) source_size = int(source_resp.headers['Content-Length']) if not rng_obj.ranges_for_length(source_size): err_msg = ('Range specified is not valid for source object ' 'of size: %s' % source_size) raise InvalidArgument('x-oss-source-range', rng, err_msg) req.headers['range'] = rng del req.headers['x-oss-copy-source-range'] resp = req.get_response(self.app) do_crc64 = crcmod.mkCrcFun(0x142F0E1EBA9EA3693L, initCrc=0L, xorOut=0xffffffffffffffffL, rev=True) if 'x-oss-copy-source' in req.headers: resp.append_copy_resp_body(req.controller_name, req_timestamp.ossxmlformat) resp.status = 200 resp.headers['x-oss-hash-crc64ecma']=do_crc64(data) return resp