def test_project(self): project = Project.objects.get() ProjectOption.objects.create(project=project, key='sentry:origins', value=['http://foo.example']) with self.Settings(SENTRY_ALLOW_ORIGIN=None): result = get_origins(project) self.assertEquals(result, frozenset(['http://foo.example']))
def test_empty_origin_values(self): project = Project.objects.get() project.update_option('sentry:origins', [u'*', None, '']) with self.settings(SENTRY_ALLOW_ORIGIN=None): result = get_origins(project) self.assertEquals(result, frozenset([u'*']))
def test_project_and_setting(self): project = Project.objects.get() project.update_option('sentry:origins', ['http://foo.example']) with self.settings(SENTRY_ALLOW_ORIGIN='http://example.com'): result = get_origins(project) self.assertEquals(result, frozenset(['http://foo.example', 'http://example.com']))
def test_project_and_setting(self): from sentry.models import Project, ProjectOption project = Project.objects.get() ProjectOption.objects.create(project=project, key='sentry:origins', value=['http://foo.example']) with self.Settings(SENTRY_ALLOW_ORIGIN='http://example.com'): result = get_origins(project) self.assertEquals(result, frozenset(['http://foo.example', 'http://example.com']))
def crossdomain_xml(request, project): origin_list = get_origins(project) if origin_list == '*': origin_list = [origin_list] response = render_to_response('sentry/crossdomain.xml', { 'origin_list': origin_list }, request) response['Content-Type'] = 'application/xml' return response
def crossdomain_xml(request, project_id): if not project_id.isdigit(): return HttpResponse(status=404) try: project = Project.objects.get_from_cache(id=project_id) except Project.DoesNotExist: return HttpResponse(status=404) origin_list = get_origins(project) response = render_to_response("sentry/crossdomain.xml", {"origin_list": origin_list}) response["Content-Type"] = "application/xml" return response
def crossdomain_xml(request, project_id): if not project_id.isdigit(): return HttpResponse(status=404) try: project = Project.objects.get_from_cache(id=project_id) except Project.DoesNotExist: return HttpResponse(status=404) origin_list = get_origins(project) response = render_to_response('sentry/crossdomain.xml', {'origin_list': origin_list}) response['Content-Type'] = 'application/xml' return response
def crossdomain_xml(request, project_id): if project_id.isdigit(): lookup = {"id": project_id} else: lookup = {"slug": project_id} try: project = Project.objects.get_from_cache(**lookup) except Project.DoesNotExist: return HttpResponse(status=404) origin_list = get_origins(project) if origin_list == "*": origin_list = [origin_list] response = render_to_response("sentry/crossdomain.xml", {"origin_list": origin_list}) response["Content-Type"] = "application/xml" return response
def crossdomain_xml(request, project_id): if project_id.isdigit(): lookup = {'id': project_id} else: lookup = {'slug': project_id} try: project = Project.objects.get_from_cache(**lookup) except Project.DoesNotExist: return HttpResponse(status=404) origin_list = get_origins(project) if origin_list == '*': origin_list = [origin_list] response = render_to_response('sentry/crossdomain.xml', {'origin_list': origin_list}) response['Content-Type'] = 'application/xml' return response
def crossdomain_xml(request, project_id): if not project_id.isdigit(): return HttpResponse(status=404) try: project = Project.objects.get_from_cache(id=project_id) except Project.DoesNotExist: return HttpResponse(status=404) origin_list = get_origins(project) if origin_list == ['*']: origin_list = [origin_list] response = render_to_response('sentry/crossdomain.xml', { 'origin_list': origin_list }) response['Content-Type'] = 'application/xml' return response
def crossdomain_xml(request, project_id): if project_id.isdigit(): lookup = {'id': project_id} else: lookup = {'slug': project_id} try: project = Project.objects.get_from_cache(**lookup) except Project.DoesNotExist: return HttpResponse(status=404) origin_list = get_origins(project) if origin_list == '*': origin_list = [origin_list] response = render_to_response('sentry/crossdomain.xml', { 'origin_list': origin_list }) response['Content-Type'] = 'application/xml' return response
def get_allowed_origins(self): from sentry.utils.http import get_origins return get_origins(self.project)
def test_setting_uri(self): with self.settings(SENTRY_ALLOW_ORIGIN='http://example.com'): result = get_origins(None) self.assertEquals(result, frozenset(['http://example.com']))
def test_setting_all(self): with self.settings(SENTRY_ALLOW_ORIGIN='*'): result = get_origins(None) self.assertEquals(result, frozenset(['*']))
def test_setting_empty(self): with self.settings(SENTRY_ALLOW_ORIGIN=None): result = get_origins(None) self.assertEquals(result, frozenset([]))
def get_project_config(project, org_options=None, full_config=True, project_keys=None): """ Constructs the ProjectConfig information. :param project: The project to load configuration for. Ensure that organization is bound on this object; otherwise it will be loaded from the database. :param org_options: Inject preloaded organization options for faster loading. If ``None``, options are lazy-loaded from the database. :param full_config: True if only the full config is required, False if only the restricted (for external relays) is required (default True, i.e. full configuration) :param project_keys: Pre-fetched project keys for performance, similar to org_options. However, if no project keys are provided it is assumed that the config does not need to contain auth information (this is the case when used in python's StoreView) :return: a ProjectConfig object for the given project """ with configure_scope() as scope: scope.set_tag("project", project.id) public_keys = [] for project_key in project_keys or (): key = { "publicKey": project_key.public_key, "isEnabled": project_key.status == 0 } if full_config: key["numericId"] = project_key.id key["quotas"] = [ quota.to_json() for quota in quotas.get_quotas(project, key=project_key) ] public_keys.append(key) now = datetime.utcnow().replace(tzinfo=utc) if org_options is None: org_options = OrganizationOption.objects.get_all_values( project.organization_id) with Hub.current.start_span(op="get_public_config"): cfg = { "disabled": project.status > 0, "slug": project.slug, "lastFetch": now, "lastChange": project.get_option("sentry:relay-rev-lastchange", now), "rev": project.get_option("sentry:relay-rev", uuid.uuid4().hex), "publicKeys": public_keys, "config": { "allowedDomains": list(get_origins(project)), "trustedRelays": org_options.get("sentry:trusted-relays", []), "piiConfig": _get_pii_config(project), "datascrubbingSettings": _get_datascrubbing_settings(project, org_options), }, "projectId": project.id, } if not full_config: # This is all we need for external Relay processors return ProjectConfig(project, **cfg) # The organization id is only required for reporting when processing events # internally. Do not expose it to external Relays. cfg["organizationId"] = project.organization_id project_cfg = cfg["config"] with Hub.current.start_span(op="get_filter_settings"): # get the filter settings for this project filter_settings = {} project_cfg["filterSettings"] = filter_settings for flt in get_all_filters(): filter_id = get_filter_key(flt) settings = _load_filter_settings(flt, project) filter_settings[filter_id] = settings invalid_releases = project.get_option(u"sentry:{}".format( FilterTypes.RELEASES)) if invalid_releases: filter_settings["releases"] = {"releases": invalid_releases} blacklisted_ips = project.get_option("sentry:blacklisted_ips") if blacklisted_ips: filter_settings["clientIps"] = {"blacklistedIps": blacklisted_ips} error_messages = project.get_option(u"sentry:{}".format( FilterTypes.ERROR_MESSAGES)) if error_messages: filter_settings["errorMessages"] = {"patterns": error_messages} csp_disallowed_sources = [] if bool(project.get_option("sentry:csp_ignored_sources_defaults", True)): csp_disallowed_sources += DEFAULT_DISALLOWED_SOURCES csp_disallowed_sources += project.get_option( "sentry:csp_ignored_sources", []) if csp_disallowed_sources: filter_settings["csp"] = { "disallowedSources": csp_disallowed_sources } with Hub.current.start_span(op="get_grouping_config_dict_for_project"): project_cfg["groupingConfig"] = get_grouping_config_dict_for_project( project) return ProjectConfig(project, **cfg)
def test_project_default(self): project = Project.objects.get() with self.settings(SENTRY_ALLOW_ORIGIN=None): result = get_origins(project) self.assertEquals(result, frozenset(['*']))
def get_project_config(project, full_config=True, project_keys=None): """ Constructs the ProjectConfig information. :param project: The project to load configuration for. Ensure that organization is bound on this object; otherwise it will be loaded from the database. :param full_config: True if only the full config is required, False if only the restricted (for external relays) is required (default True, i.e. full configuration) :param project_keys: Pre-fetched project keys for performance. However, if no project keys are provided it is assumed that the config does not need to contain auth information (this is the case when used in python's StoreView) :return: a ProjectConfig object for the given project """ with configure_scope() as scope: scope.set_tag("project", project.id) if project.status != ObjectStatus.VISIBLE: return ProjectConfig(project, disabled=True) public_keys = get_public_key_configs(project, full_config, project_keys=project_keys) with Hub.current.start_span(op="get_public_config"): now = datetime.utcnow().replace(tzinfo=utc) cfg = { "disabled": False, "slug": project.slug, "lastFetch": now, "lastChange": project.get_option("sentry:relay-rev-lastchange", now), "rev": project.get_option("sentry:relay-rev", uuid.uuid4().hex), "publicKeys": public_keys, "config": { "allowedDomains": list(get_origins(project)), "trustedRelays": [ r["public_key"] for r in project.organization.get_option( "sentry:trusted-relays", []) if r ], "piiConfig": get_pii_config(project), "datascrubbingSettings": get_datascrubbing_settings(project), }, "organizationId": project.organization_id, "projectId": project.id, # XXX: Unused by Relay, required by Python store } if not full_config: # This is all we need for external Relay processors return ProjectConfig(project, **cfg) with Hub.current.start_span(op="get_filter_settings"): cfg["config"]["filterSettings"] = get_filter_settings(project) with Hub.current.start_span(op="get_grouping_config_dict_for_project"): cfg["config"]["groupingConfig"] = get_grouping_config_dict_for_project( project) with Hub.current.start_span(op="get_event_retention"): cfg["config"]["eventRetention"] = quotas.get_event_retention( project.organization) with Hub.current.start_span(op="get_all_quotas"): cfg["config"]["quotas"] = get_quotas(project, keys=project_keys) return ProjectConfig(project, **cfg)
def get_project_config(project_id, full_config=True, for_store=False): """ Constructs the ProjectConfig information. :param project_id: the project id as int or string :param full_config: True if only the full config is required, False if only the restricted (for external relays) is required (default True, i.e. full configuration) :param for_store: If set to true, this omits all parameters that are not needed for store normalization. This is a temporary flag that should be removed once store has been moved to Relay. Most importantly, this avoids database accesses. :return: a ProjectConfig object for the given project """ project = _get_project_from_id(six.text_type(project_id)) if project is None: raise APIError("Invalid project id:{}".format(project_id)) with configure_scope() as scope: scope.set_tag("project", project.id) if for_store: project_keys = [] else: project_keys = ProjectKey.objects.filter(project=project).all() public_keys = {} for project_key in project_keys: public_keys[project_key.public_key] = project_key.status == 0 now = datetime.utcnow().replace(tzinfo=utc) org_options = OrganizationOption.objects.get_all_values( project.organization_id) cfg = { "disabled": project.status > 0, "slug": project.slug, "lastFetch": now, "lastChange": project.get_option("sentry:relay-rev-lastchange", now), "rev": project.get_option("sentry:relay-rev", uuid.uuid4().hex), "publicKeys": public_keys, "config": { "allowedDomains": project.get_option("sentry:origins", ["*"]), "trustedRelays": org_options.get("sentry:trusted-relays", []), "piiConfig": _get_pii_config(project), "datascrubbingSettings": _get_datascrubbing_settings(project, org_options), }, "project_id": project.id, } if not full_config: # This is all we need for external Relay processors return ProjectConfig(project, **cfg) # The organization id is only required for reporting when processing events # internally. Do not expose it to external Relays. cfg["organization_id"] = project.organization_id # Explicitly bind Organization so we don't implicitly query it later # this just allows us to comfortably assure that `project.organization` is safe. # This also allows us to pull the object from cache, instead of being # implicitly fetched from database. project.organization = Organization.objects.get_from_cache( id=project.organization_id) if project.organization is not None: org_options = OrganizationOption.objects.get_all_values( project.organization_id) else: org_options = {} project_cfg = cfg["config"] # get the filter settings for this project filter_settings = {} project_cfg["filter_settings"] = filter_settings for flt in get_all_filters(): filter_id = get_filter_key(flt) settings = _load_filter_settings(flt, project) filter_settings[filter_id] = settings invalid_releases = project.get_option(u"sentry:{}".format( FilterTypes.RELEASES)) if invalid_releases: filter_settings[FilterTypes.RELEASES] = {"releases": invalid_releases} blacklisted_ips = project.get_option("sentry:blacklisted_ips") if blacklisted_ips: filter_settings["client_ips"] = {"blacklisted_ips": blacklisted_ips} error_messages = project.get_option(u"sentry:{}".format( FilterTypes.ERROR_MESSAGES)) if error_messages: filter_settings[FilterTypes.ERROR_MESSAGES] = { "patterns": error_messages } csp_disallowed_sources = [] if bool(project.get_option("sentry:csp_ignored_sources_defaults", True)): csp_disallowed_sources += DEFAULT_DISALLOWED_SOURCES csp_disallowed_sources += project.get_option("sentry:csp_ignored_sources", []) if csp_disallowed_sources: filter_settings["csp"] = {"disallowed_sources": csp_disallowed_sources} scrub_ip_address = org_options.get("sentry:require_scrub_ip_address", False) or project.get_option( "sentry:scrub_ip_address", False) project_cfg["scrub_ip_addresses"] = scrub_ip_address project_cfg["grouping_config"] = get_grouping_config_dict_for_project( project) project_cfg["allowed_domains"] = list(get_origins(project)) return ProjectConfig(project, **cfg)
def get_full_relay_config(project_id): """ Constructs the internal (big) RelayConfig :param project_id: the project id as int or string :return: FullRelayConfig the relay configuration """ cfg = {} project = _get_project_from_id(six.text_type(project_id)) if project is None: raise APIError("Invalid project id:{}".format(project_id)) cfg['project_id'] = project.id cfg['organization_id'] = project.organization_id # Explicitly bind Organization so we don't implicitly query it later # this just allows us to comfortably assure that `project.organization` is safe. # This also allows us to pull the object from cache, instead of being # implicitly fetched from database. project.organization = Organization.objects.get_from_cache( id=project.organization_id) if project.organization is not None: org_options = OrganizationOption.objects.get_all_values( project.organization_id) else: org_options = {} # get the project options project_cfg = {} cfg['config'] = project_cfg # getting kafka info try: project_cfg['kafka_max_event_size'] = options.get( 'kafka-publisher.max-event-size') project_cfg['kafka_raw_event_sample_rate'] = options.get( 'kafka-publisher.raw-event-sample-rate') except Exception: pass # should we log ? invalid_releases = project.get_option(u'sentry:{}'.format( FilterTypes.RELEASES)) if invalid_releases is not None: project_cfg['invalid_releases'] = invalid_releases # get the filters enabled for the current project enabled_filters = [ filter_class.id for filter_class in filters.all() if filter_class(project).is_enabled() ] project_cfg['enabled_filters'] = enabled_filters scrub_ip_address = ( org_options.get('sentry:require_scrub_ip_address', False) or project.get_option('sentry:scrub_ip_address', False)) project_cfg['scrub_ip_addresses'] = scrub_ip_address scrub_data = (org_options.get('sentry:require_scrub_data', False) or project.get_option('sentry:scrub_data', True)) project_cfg['scrub_data'] = scrub_data project_cfg['grouping_config'] = get_grouping_config_dict_for_project( project) project_cfg['allowed_domains'] = list(get_origins(project)) if scrub_data: # We filter data immediately before it ever gets into the queue sensitive_fields_key = 'sentry:sensitive_fields' sensitive_fields = (org_options.get(sensitive_fields_key, []) + project.get_option(sensitive_fields_key, [])) project_cfg['sensitive_fields'] = sensitive_fields exclude_fields_key = 'sentry:safe_fields' exclude_fields = (org_options.get(exclude_fields_key, []) + project.get_option(exclude_fields_key, [])) project_cfg['exclude_fields'] = exclude_fields scrub_defaults = (org_options.get('sentry:require_scrub_defaults', False) or project.get_option('sentry:scrub_defaults', True)) project_cfg['scrub_defaults'] = scrub_defaults return FullRelayConfig(project, **cfg)
def get_project_config(project, org_options=None, full_config=True, for_store=False): """ Constructs the ProjectConfig information. :param project: The project to load configuration for. Ensure that organization is bound on this object; otherwise it will be loaded from the database. :param org_options: Inject preloaded organization options for faster loading. If ``None``, options are lazy-loaded from the database. :param full_config: True if only the full config is required, False if only the restricted (for external relays) is required (default True, i.e. full configuration) :param for_store: If set to true, this omits all parameters that are not needed for Relay. This is a temporary flag that should be removed once store has been moved to Relay. Most importantly, this avoids database accesses. :return: a ProjectConfig object for the given project """ with configure_scope() as scope: scope.set_tag("project", project.id) if for_store: project_keys = [] else: project_keys = ProjectKey.objects.filter(project=project).all() public_keys = [] for project_key in project_keys: key = { "publicKey": project_key.public_key, "isEnabled": project_key.status == 0 } if full_config: key["numericId"] = project_key.id key["quotas"] = [ quota.to_json() for quota in quotas.get_quotas(project, key=project_key) ] public_keys.append(key) now = datetime.utcnow().replace(tzinfo=utc) if org_options is None: org_options = OrganizationOption.objects.get_all_values( project.organization_id) cfg = { "disabled": project.status > 0, "slug": project.slug, "lastFetch": now, "lastChange": project.get_option("sentry:relay-rev-lastchange", now), "rev": project.get_option("sentry:relay-rev", uuid.uuid4().hex), "publicKeys": public_keys, "config": { "allowedDomains": project.get_option("sentry:origins", ["*"]), "trustedRelays": org_options.get("sentry:trusted-relays", []), "piiConfig": _get_pii_config(project), "datascrubbingSettings": _get_datascrubbing_settings(project, org_options), }, "project_id": project.id, } if not full_config: # This is all we need for external Relay processors return ProjectConfig(project, **cfg) # The organization id is only required for reporting when processing events # internally. Do not expose it to external Relays. cfg["organization_id"] = project.organization_id project_cfg = cfg["config"] # get the filter settings for this project filter_settings = {} project_cfg["filter_settings"] = filter_settings for flt in get_all_filters(): filter_id = get_filter_key(flt) settings = _load_filter_settings(flt, project) filter_settings[filter_id] = settings invalid_releases = project.get_option(u"sentry:{}".format( FilterTypes.RELEASES)) if invalid_releases: filter_settings[FilterTypes.RELEASES] = {"releases": invalid_releases} blacklisted_ips = project.get_option("sentry:blacklisted_ips") if blacklisted_ips: filter_settings["client_ips"] = {"blacklisted_ips": blacklisted_ips} error_messages = project.get_option(u"sentry:{}".format( FilterTypes.ERROR_MESSAGES)) if error_messages: filter_settings[FilterTypes.ERROR_MESSAGES] = { "patterns": error_messages } csp_disallowed_sources = [] if bool(project.get_option("sentry:csp_ignored_sources_defaults", True)): csp_disallowed_sources += DEFAULT_DISALLOWED_SOURCES csp_disallowed_sources += project.get_option("sentry:csp_ignored_sources", []) if csp_disallowed_sources: filter_settings["csp"] = {"disallowed_sources": csp_disallowed_sources} scrub_ip_address = org_options.get("sentry:require_scrub_ip_address", False) or project.get_option( "sentry:scrub_ip_address", False) project_cfg["scrub_ip_addresses"] = scrub_ip_address project_cfg["grouping_config"] = get_grouping_config_dict_for_project( project) project_cfg["allowed_domains"] = list(get_origins(project)) return ProjectConfig(project, **cfg)
def get_project_config(project_id, full_config=True, for_store=False): """ Constructs the ProjectConfig information. :param project_id: the project id as int or string :param full_config: True if only the full config is required, False if only the restricted (for external relays) is required (default True, i.e. full configuration) :param for_store: If set to true, this omits all parameters that are not needed for store normalization. This is a temporary flag that should be removed once store has been moved to Relay. Most importantly, this avoids database accesses. :return: a ProjectConfig object for the given project """ project = _get_project_from_id(six.text_type(project_id)) if project is None: raise APIError("Invalid project id:{}".format(project_id)) with configure_scope() as scope: scope.set_tag("project", project.id) if for_store: project_keys = [] else: project_keys = ProjectKey.objects \ .filter(project=project) \ .all() public_keys = {} for project_key in project_keys: public_keys[project_key.public_key] = project_key.status == 0 now = datetime.utcnow().replace(tzinfo=utc) org_options = OrganizationOption.objects.get_all_values( project.organization_id) cfg = { 'disabled': project.status > 0, 'slug': project.slug, 'lastFetch': now, 'lastChange': project.get_option('sentry:relay-rev-lastchange', now), 'rev': project.get_option('sentry:relay-rev', uuid.uuid4().hex), 'publicKeys': public_keys, 'config': { 'allowedDomains': project.get_option('sentry:origins', ['*']), 'trustedRelays': org_options.get('sentry:trusted-relays', []), 'piiConfig': _get_pii_config(project, org_options), }, 'project_id': project.id, } if not full_config: # This is all we need for external Relay processors return ProjectConfig(project, **cfg) # The organization id is only required for reporting when processing events # internally. Do not expose it to external Relays. cfg['organization_id'] = project.organization_id # Explicitly bind Organization so we don't implicitly query it later # this just allows us to comfortably assure that `project.organization` is safe. # This also allows us to pull the object from cache, instead of being # implicitly fetched from database. project.organization = Organization.objects.get_from_cache( id=project.organization_id) if project.organization is not None: org_options = OrganizationOption.objects.get_all_values( project.organization_id) else: org_options = {} project_cfg = cfg['config'] # get the filter settings for this project filter_settings = {} project_cfg['filter_settings'] = filter_settings for flt in get_all_filters(): filter_id = get_filter_key(flt) settings = _load_filter_settings(flt, project) filter_settings[filter_id] = settings invalid_releases = project.get_option(u'sentry:{}'.format(FilterTypes.RELEASES)) if invalid_releases: filter_settings[FilterTypes.RELEASES] = {'releases': invalid_releases} blacklisted_ips = project.get_option('sentry:blacklisted_ips') if blacklisted_ips: filter_settings['client_ips'] = {'blacklisted_ips': blacklisted_ips} error_messages = project.get_option(u'sentry:{}'.format(FilterTypes.ERROR_MESSAGES)) if error_messages: filter_settings[FilterTypes.ERROR_MESSAGES] = {'patterns': error_messages} csp_disallowed_sources = [] if bool(project.get_option('sentry:csp_ignored_sources_defaults', True)): csp_disallowed_sources += DEFAULT_DISALLOWED_SOURCES csp_disallowed_sources += project.get_option('sentry:csp_ignored_sources', []) if csp_disallowed_sources: filter_settings['csp'] = {'disallowed_sources': csp_disallowed_sources} scrub_ip_address = (org_options.get('sentry:require_scrub_ip_address', False) or project.get_option('sentry:scrub_ip_address', False)) project_cfg['scrub_ip_addresses'] = scrub_ip_address scrub_data = (org_options.get('sentry:require_scrub_data', False) or project.get_option('sentry:scrub_data', True)) project_cfg['scrub_data'] = scrub_data project_cfg['grouping_config'] = get_grouping_config_dict_for_project(project) project_cfg['allowed_domains'] = list(get_origins(project)) if scrub_data: # We filter data immediately before it ever gets into the queue sensitive_fields_key = 'sentry:sensitive_fields' sensitive_fields = ( org_options.get(sensitive_fields_key, []) + project.get_option(sensitive_fields_key, []) ) project_cfg['sensitive_fields'] = sensitive_fields exclude_fields_key = 'sentry:safe_fields' exclude_fields = ( org_options.get(exclude_fields_key, []) + project.get_option(exclude_fields_key, []) ) project_cfg['exclude_fields'] = exclude_fields scrub_defaults = (org_options.get('sentry:require_scrub_defaults', False) or project.get_option('sentry:scrub_defaults', True)) project_cfg['scrub_defaults'] = scrub_defaults return ProjectConfig(project, **cfg)