def get_filter_settings(project): 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} return filter_settings
def get(self, request, project): """ List a project's filters Retrieve a list of filters for a given project. {method} {path} """ results = [] for flt in message_filters.get_all_filters(): filter_spec = flt.spec results.append({ "id": filter_spec.id, # 'active' will be either a boolean or list for the legacy browser filters # all other filters will be boolean "active": message_filters.get_filter_state(filter_spec.id, project), "description": filter_spec.description, "name": filter_spec.name, "hello": filter_spec.id + " - " + filter_spec.name, }) results.sort(key=lambda x: x["name"]) return Response(results)
def put(self, request, project, filter_id): """ Update a filter Update a project's filter. {method} {path} """ current_filter = None for flt in message_filters.get_all_filters(): if flt.spec.id == filter_id: current_filter = flt break else: raise ResourceDoesNotExist # could not find filter with the requested id serializer = current_filter.spec.serializer_cls(data=request.DATA, partial=True) if not serializer.is_valid(): return Response(serializer.errors, status=400) current_state = message_filters.get_filter_state(filter_id, project) new_state = message_filters.set_filter_state(filter_id, project, serializer.object) audit_log_state = AuditLogEntryEvent.PROJECT_ENABLE if filter_id == 'legacy-browsers': if isinstance(current_state, bool) or new_state == 0 or isinstance( new_state, six.binary_type): returned_state = new_state if isinstance(new_state, six.binary_type): audit_log_state = AuditLogEntryEvent.PROJECT_DISABLE returned_state = current_state elif current_state - new_state: returned_state = current_state - new_state audit_log_state = AuditLogEntryEvent.PROJECT_DISABLE elif new_state - current_state: returned_state = new_state - current_state elif new_state == current_state: returned_state = new_state if filter_id in ('browser-extensions', 'localhost', 'web-crawlers'): returned_state = filter_id removed = current_state - new_state if removed == 1: audit_log_state = AuditLogEntryEvent.PROJECT_DISABLE self.create_audit_entry( request=request, organization=project.organization, target_object=project.id, event=audit_log_state, data={"state": returned_state}, ) return Response(status=201)
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)
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), "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 filter settings for this project filter_settings = {} project_cfg['filter_settings'] = filter_settings for flt in get_all_filters(): filter_id = flt.spec.id settings = _load_filter_settings(flt, project) filter_settings[filter_id] = settings 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, 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)