def _dispatch(self, request, project_id=None, *args, **kwargs): request.user = AnonymousUser() try: project = self._get_project_from_id(project_id) except APIError as e: raise InvalidRequest(str(e)) if project: Raven.tags_context({'project': project.id}) origin = self.get_request_origin(request) if origin is not None: # This check is specific for clients who need CORS support if not project: raise InvalidRequest( 'Your client must be upgraded for CORS support.') if not is_valid_origin(origin, project): raise InvalidOrigin(origin) # XXX: It seems that the OPTIONS call does not always include custom headers if request.method == 'OPTIONS': response = self.options(request, project) else: try: auth_vars = self._parse_header(request, project) except APIError as e: raise InvalidRequest(str(e)) try: project_, user = project_from_auth_vars(auth_vars) except APIError as error: return HttpResponse(six.text_type(error.msg), status=error.http_status) else: if user: request.user = user # Legacy API was /api/store/ and the project ID was only available elsewhere if not project: if not project_: raise InvalidRequest('Unable to identify project') project = project_ elif project_ != project: raise InvalidRequest('Project ID mismatch') else: Raven.tags_context({'project': project.id}) auth = Auth(auth_vars, is_public=bool(origin)) if auth.version >= 3: if request.method == 'GET': # GET only requires an Origin/Referer check # If an Origin isn't passed, it's possible that the project allows no origin, # so we need to explicitly check for that here. If Origin is not None, # it can be safely assumed that it was checked previously and it's ok. if origin is None and not is_valid_origin(origin, project): # Special case an error message for a None origin when None wasn't allowed raise InvalidRequest( 'Missing required Origin or Referer header') else: # Version 3 enforces secret key for server side requests if not auth.secret_key: raise InvalidRequest( 'Missing required attribute in authentication header: sentry_secret' ) try: response = super(APIView, self).dispatch(request, project=project, auth=auth, **kwargs) except APIError as error: response = HttpResponse(six.text_type(error.msg), content_type='text/plain', status=error.http_status) if isinstance( error, APIRateLimited) and error.retry_after is not None: response['Retry-After'] = str(error.retry_after) if origin: response['Access-Control-Allow-Origin'] = origin return response
def process(self, request, project, auth, data, **kwargs): event_received.send_robust(ip=request.META['REMOTE_ADDR'], sender=type(self)) # TODO: improve this API (e.g. make RateLimit act on __ne__) rate_limit = safe_execute(app.quotas.is_rate_limited, project=project, _with_transaction=False) if isinstance(rate_limit, bool): rate_limit = RateLimit(is_limited=rate_limit, retry_after=None) if rate_limit is not None and rate_limit.is_limited: app.tsdb.incr_multi([ (app.tsdb.models.project_total_received, project.id), (app.tsdb.models.project_total_rejected, project.id), (app.tsdb.models.organization_total_received, project.organization_id), (app.tsdb.models.organization_total_rejected, project.organization_id), ]) raise APIRateLimited(rate_limit.retry_after) else: app.tsdb.incr_multi([ (app.tsdb.models.project_total_received, project.id), (app.tsdb.models.organization_total_received, project.organization_id), ]) result = plugins.first('has_perm', request.user, 'create_event', project, version=1) if result is False: raise APIForbidden('Creation of this event was blocked') content_encoding = request.META.get('HTTP_CONTENT_ENCODING', '') if content_encoding == 'gzip': data = decompress_gzip(data) elif content_encoding == 'deflate': data = decompress_deflate(data) elif not data.startswith('{'): data = decode_and_decompress_data(data) data = safely_load_json_string(data) try: # mutates data validate_data(project, data, auth.client) except InvalidData as e: raise APIError(u'Invalid data: %s (%s)' % (six.text_type(e), type(e))) # mutates data manager = EventManager(data, version=auth.version) data = manager.normalize() scrub_ip_address = project.get_option('sentry:scrub_ip_address', False) # insert IP address if not available if auth.is_public and not scrub_ip_address: ensure_has_ip(data, request.META['REMOTE_ADDR']) event_id = data['event_id'] # TODO(dcramer): ideally we'd only validate this if the event_id was # supplied by the user cache_key = 'ev:%s:%s' % ( project.id, event_id, ) if cache.get(cache_key) is not None: logger.warning( 'Discarded recent duplicate event from project %s/%s (id=%s)', project.organization.slug, project.slug, event_id) raise InvalidRequest('An event with the same ID already exists.') if project.get_option('sentry:scrub_data', True): # We filter data immediately before it ever gets into the queue inst = SensitiveDataFilter() inst.apply(data) if scrub_ip_address: # We filter data immediately before it ever gets into the queue ensure_does_not_have_ip(data) # mutates data (strips a lot of context if not queued) insert_data_to_database(data) cache.set(cache_key, '', 60 * 5) logger.debug('New event from project %s/%s (id=%s)', project.organization.slug, project.slug, event_id) return event_id