def disable_2fa_required(self): require_2fa = self.organization.flags.require_2fa if not require_2fa or not require_2fa.is_set: return self.organization.update( flags=F('flags').bitand(~Organization.flags.require_2fa) ) logger.info( 'Require 2fa disabled during sso setup', extra={ 'organization_id': self.organization.id, } ) create_audit_entry( request=self.request, organization=self.organization, target_object=self.organization.id, event=AuditLogEntryEvent.ORG_EDIT, data={ 'require_2fa': u'to False when enabling SSO' }, )
def accept_invite(self, user=None): om = self.om if user is None: user = self.request.user if self.member_already_exists: self.handle_member_already_exists() om.delete() else: om.set_user(user) om.save() create_audit_entry( self.request, actor=user, organization=om.organization, target_object=om.id, target_user=user, event=AuditLogEntryEvent.MEMBER_ACCEPT, data=om.get_audit_log_data(), ) self.handle_success() metrics.incr("organization.invite-accepted", sample_rate=1.0)
def create_audit_log_entry(self, integration, organization, request, action, extra=None): super(SlackIntegrationProvider, self).create_audit_log_entry( integration, organization, request, action ) if action == "upgrade": create_audit_entry( request=request, organization=organization, target_object=integration.id, event=AuditLogEntryEvent.INTEGRATION_UPGRADE, data={"provider": integration.provider, "name": integration.name}, ) if extra and extra.get("extra_orgs"): for org in Organization.objects.filter(slug__in=extra["extra_orgs"]): create_audit_entry( request=request, organization=org, target_object=integration.id, event=AuditLogEntryEvent.INTEGRATION_UPGRADE, data={"provider": integration.provider, "name": integration.name}, ) return
def delete(self, request, organization, integration_id): # Removing the integration removes the organization # integrations and all linked issues. org_integration = self.get_organization_integration( organization, integration_id) updated = OrganizationIntegration.objects.filter( id=org_integration.id, status=ObjectStatus.VISIBLE).update( status=ObjectStatus.PENDING_DELETION) if updated: delete_organization_integration.apply_async( kwargs={ "object_id": org_integration.id, "transaction_id": uuid4().hex, "actor_id": request.user.id, }, countdown=0, ) integration = org_integration.integration create_audit_entry( request=request, organization=organization, target_object=integration.id, event=AuditLogEntryEvent.INTEGRATION_REMOVE, data={ "provider": integration.provider, "name": integration.name }, ) return self.respond(status=204)
def delete(self, request, organization, integration_id): # Removing the integration removes the organization # integrations and all linked issues. org_integration = self.get_organization_integration( organization, integration_id) integration = org_integration.integration # do any integration specific deleting steps integration.get_installation(organization.id).uninstall() with transaction.atomic(): updated = OrganizationIntegration.objects.filter( id=org_integration.id, status=ObjectStatus.VISIBLE).update( status=ObjectStatus.PENDING_DELETION) if updated: ScheduledDeletion.schedule(org_integration, days=0, actor=request.user) create_audit_entry( request=request, organization=organization, target_object=integration.id, event=AuditLogEntryEvent.INTEGRATION_REMOVE, data={ "provider": integration.provider, "name": integration.name }, ) return self.respond(status=204)
def audit(self): if self.request: create_audit_entry( request=self.request, organization=self.sentry_app.owner, target_object=self.sentry_app.owner.id, event=AuditLogEntryEvent.SENTRY_APP_REMOVE, data={"sentry_app": self.sentry_app.name}, )
def audit(self): from sentry.utils.audit import create_audit_entry if self.request: create_audit_entry( request=self.request, organization=self.organization, target_object=self.organization.id, event=AuditLogEntryEvent.INTERNAL_INTEGRATION_ADD, )
def audit(self): if self.request: create_audit_entry( request=self.request, organization=self.install.organization, target_object=self.install.organization.id, event=AuditLogEntryEvent.SENTRY_APP_UNINSTALL, data={"sentry_app": self.install.sentry_app.name}, )
def delete_groups(request, project, group_list, delete_type): if not group_list: return # deterministic sort for sanity, and for very large deletions we'll # delete the "smaller" groups first group_list.sort(key=lambda g: (g.times_seen, g.id)) group_ids = [g.id for g in group_list] Group.objects.filter( id__in=group_ids, ).exclude(status__in=[ GroupStatus.PENDING_DELETION, GroupStatus.DELETION_IN_PROGRESS, ]).update(status=GroupStatus.PENDING_DELETION) eventstream_state = eventstream.start_delete_groups(project.id, group_ids) transaction_id = uuid4().hex GroupHash.objects.filter( project_id=project.id, group__id__in=group_ids, ).delete() delete_groups_task.apply_async( kwargs={ 'object_ids': group_ids, 'transaction_id': transaction_id, 'eventstream_state': eventstream_state, }, countdown=3600, ) for group in group_list: create_audit_entry( request=request, transaction_id=transaction_id, logger=audit_logger, organization_id=project.organization_id, target_object=group.id, ) delete_logger.info( 'object.delete.queued', extra={ 'object_id': group.id, 'transaction_id': transaction_id, 'model': type(group).__name__, } ) issue_deleted.send_robust( group=group, user=request.user, delete_type=delete_type, sender=delete_groups)
def _delete_groups(request, project, group_list, delete_type): if not group_list: return # deterministic sort for sanity, and for very large deletions we'll # delete the "smaller" groups first group_list.sort(key=lambda g: (g.times_seen, g.id)) group_ids = [g.id for g in group_list] Group.objects.filter( id__in=group_ids, ).exclude(status__in=[ GroupStatus.PENDING_DELETION, GroupStatus.DELETION_IN_PROGRESS, ]).update(status=GroupStatus.PENDING_DELETION) eventstream_state = eventstream.start_delete_groups(project.id, group_ids) transaction_id = uuid4().hex GroupHash.objects.filter( project_id=project.id, group__id__in=group_ids, ).delete() delete_groups_task.apply_async( kwargs={ 'object_ids': group_ids, 'transaction_id': transaction_id, 'eventstream_state': eventstream_state, }, countdown=3600, ) for group in group_list: create_audit_entry( request=request, transaction_id=transaction_id, logger=audit_logger, organization_id=project.organization_id, target_object=group.id, ) delete_logger.info( 'object.delete.queued', extra={ 'object_id': group.id, 'transaction_id': transaction_id, 'model': type(group).__name__, } ) issue_deleted.send_robust( group=group, user=request.user, delete_type=delete_type, sender=_delete_groups)
def audit(self): from sentry.utils.audit import create_audit_entry if self.request and self.generate_audit: create_audit_entry( request=self.request, organization=self.organization, target_object=self.api_token.id, event=AuditLogEntryEvent.INTERNAL_INTEGRATION_ADD_TOKEN, data={'sentry_app': self.sentry_app.name}, )
def _delete_groups(request, project, group_list, delete_type): if not group_list: return # deterministic sort for sanity, and for very large deletions we'll # delete the "smaller" groups first group_list.sort(key=lambda g: (g.times_seen, g.id)) group_ids = [g.id for g in group_list] Group.objects.filter(id__in=group_ids).exclude(status__in=[ GroupStatus.PENDING_DELETION, GroupStatus.DELETION_IN_PROGRESS ]).update(status=GroupStatus.PENDING_DELETION) eventstream_state = eventstream.start_delete_groups(project.id, group_ids) transaction_id = uuid4().hex GroupHash.objects.filter(project_id=project.id, group__id__in=group_ids).delete() # We remove `GroupInbox` rows here so that they don't end up influencing queries for # `Group` instances that are pending deletion GroupInbox.objects.filter(project_id=project.id, group__id__in=group_ids).delete() delete_groups_task.apply_async( kwargs={ "object_ids": group_ids, "transaction_id": transaction_id, "eventstream_state": eventstream_state, }, countdown=3600, ) for group in group_list: create_audit_entry( request=request, transaction_id=transaction_id, logger=audit_logger, organization_id=project.organization_id, target_object=group.id, ) delete_logger.info( "object.delete.queued", extra={ "object_id": group.id, "transaction_id": transaction_id, "model": type(group).__name__, }, ) issue_deleted.send_robust(group=group, user=request.user, delete_type=delete_type, sender=_delete_groups)
def audit(self): if self.request: create_audit_entry( request=self.request, organization=self.organization, target_object=self.organization.id, event=AuditLogEntryEvent.SENTRY_APP_ADD, data={ 'sentry_app': self.app.name, }, )
def audit(self): from sentry.utils.audit import create_audit_entry if self.request: create_audit_entry( request=self.request, organization=self.organization, target_object=self.organization.id, event=AuditLogEntryEvent.SENTRY_APP_ADD, data={"sentry_app": self.sentry_app.name}, )
def audit(self): if self.request: create_audit_entry( request=self.request, organization=self.organization, target_object=self.organization.id, event=AuditLogEntryEvent.SENTRY_APP_ADD, data={ 'sentry_app': self.sentry_app.name, }, )
def create_audit_log_entry(self, integration, organization, request, action, extra=None): """ Creates an audit log entry for the newly installed integration. """ if action == "install": create_audit_entry( request=request, organization=organization, target_object=integration.id, event=AuditLogEntryEvent.INTEGRATION_ADD, data={"provider": integration.provider, "name": integration.name}, )
def audit(self): from sentry.utils.audit import create_audit_entry if self.request: create_audit_entry( request=self.request, organization=self.install.organization, target_object=self.install.organization.id, event=AuditLogEntryEvent.SENTRY_APP_INSTALL, data={ 'sentry_app': self.sentry_app.name, }, )
def test_audit_entry_org_restore_log(self): Organization.objects.filter(id=self.organization.id).update( status=OrganizationStatus.PENDING_DELETION ) org = Organization.objects.get(id=self.organization.id) Organization.objects.filter(id=self.organization.id).update( status=OrganizationStatus.DELETION_IN_PROGRESS ) org2 = Organization.objects.get(id=self.organization.id) Organization.objects.filter(id=self.organization.id).update( status=OrganizationStatus.VISIBLE ) org3 = Organization.objects.get(id=self.organization.id) orgs = [org, org2, org3] entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.org.id, event=AuditLogEntryEvent.ORG_RESTORE, data=self.org.get_audit_log_data(), ) entry2 = create_audit_entry( request=self.req, organization=self.org, target_object=self.org.id, event=AuditLogEntryEvent.ORG_EDIT, data=self.org.get_audit_log_data(), ) for i in orgs: if ( i.status == OrganizationStatus.PENDING_DELETION or i.status == OrganizationStatus.DELETION_IN_PROGRESS ): assert i.status != OrganizationStatus.VISIBLE assert ("restored") in entry.get_note() assert entry.actor == self.user assert entry.target_object == self.org.id assert entry.event == AuditLogEntryEvent.ORG_RESTORE else: assert i.status == OrganizationStatus.VISIBLE assert ("edited") in entry2.get_note() assert entry2.actor == self.user assert entry2.target_object == self.org.id assert entry2.event == AuditLogEntryEvent.ORG_EDIT
def test_audit_entry_integration_log(self): project = self.create_project() self.login_as(user=self.user) entry = create_audit_entry( request=self.req, organization=self.project.organization, target_object=self.project.id, event=AuditLogEntryEvent.INTEGRATION_ADD, data={ "integration": "webhooks", "project": project.slug }, ) assert ("enabled") in entry.get_note() assert entry.actor == self.user assert entry.target_object == self.project.id assert entry.event == AuditLogEntryEvent.INTEGRATION_ADD entry2 = create_audit_entry( request=self.req, organization=self.project.organization, target_object=self.project.id, event=AuditLogEntryEvent.INTEGRATION_EDIT, data={ "integration": "webhooks", "project": project.slug }, ) assert ("edited") in entry2.get_note() assert entry2.actor == self.user assert entry2.target_object == self.project.id assert entry2.event == AuditLogEntryEvent.INTEGRATION_EDIT entry3 = create_audit_entry( request=self.req, organization=self.project.organization, target_object=self.project.id, event=AuditLogEntryEvent.INTEGRATION_REMOVE, data={ "integration": "webhooks", "project": project.slug }, ) assert ("disable") in entry3.get_note() assert entry3.actor == self.user assert entry3.target_object == self.project.id assert entry3.event == AuditLogEntryEvent.INTEGRATION_REMOVE
def test_audit_entry_org_restore_log(self): Organization.objects.filter( id=self.organization.id, ).update(status=OrganizationStatus.PENDING_DELETION) org = Organization.objects.get(id=self.organization.id) Organization.objects.filter( id=self.organization.id, ).update(status=OrganizationStatus.DELETION_IN_PROGRESS) org2 = Organization.objects.get(id=self.organization.id) Organization.objects.filter( id=self.organization.id, ).update(status=OrganizationStatus.VISIBLE) org3 = Organization.objects.get(id=self.organization.id) orgs = [org, org2, org3] entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.org.id, event=AuditLogEntryEvent.ORG_RESTORE, data=self.org.get_audit_log_data(), ) entry2 = create_audit_entry( request=self.req, organization=self.org, target_object=self.org.id, event=AuditLogEntryEvent.ORG_EDIT, data=self.org.get_audit_log_data(), ) for i in orgs: if i.status == OrganizationStatus.PENDING_DELETION or i.status == OrganizationStatus.DELETION_IN_PROGRESS: assert i.status != OrganizationStatus.VISIBLE assert ('restored') in entry.get_note() assert entry.actor == self.user assert entry.target_object == self.org.id assert entry.event == AuditLogEntryEvent.ORG_RESTORE else: assert i.status == OrganizationStatus.VISIBLE assert ('edited') in entry2.get_note() assert entry2.actor == self.user assert entry2.target_object == self.org.id assert entry2.event == AuditLogEntryEvent.ORG_EDIT
def test_audit_entry_frontend(self): req = FakeHttpRequest(self.create_user()) entry = create_audit_entry(req) assert entry.actor == req.user assert entry.actor_key is None assert entry.ip_address == req.META['REMOTE_ADDR']
def disable_2fa_required(request, organization): require_2fa = organization.flags.require_2fa if require_2fa and require_2fa.is_set: organization.update( flags=models.F('flags').bitand(~Organization.flags.require_2fa)) logger.info('Require 2fa disabled during saml sso setup', extra={ 'organization_id': organization.id, }) create_audit_entry( request=request, organization=organization, target_object=organization.id, event=AuditLogEntryEvent.ORG_EDIT, data={'require_2fa': u'to False when enabling SAML SSO'}, )
def test_audit_entry_frontend(self): req = FakeHttpRequest(self.create_user()) entry = create_audit_entry(req) assert entry.actor == req.user assert entry.actor_key is None assert entry.ip_address == req.META['REMOTE_ADDR'] self.assert_no_delete_log_created()
def disable_2fa_required(self): require_2fa = self.organization.flags.require_2fa if not require_2fa or not require_2fa.is_set: return self.organization.update(flags=F("flags").bitand(~Organization.flags.require_2fa)) logger.info( "Require 2fa disabled during sso setup", extra={"organization_id": self.organization.id} ) create_audit_entry( request=self.request, organization=self.organization, target_object=self.organization.id, event=AuditLogEntryEvent.ORG_EDIT, data={"require_2fa": u"to False when enabling SSO"}, )
def handle_member_removed(self, request): data = request.data channel_data = data["channelData"] # only care if our bot is the new member removed matches = filter(lambda x: x["id"] == data["recipient"]["id"], data["membersRemoved"]) if not matches: return self.respond(status=204) team_id = channel_data["team"]["id"] try: integration = Integration.objects.get(provider=self.provider, external_id=team_id) except Integration.DoesNotExist: logger.info( "msteams.uninstall.missing-integration", extra={"team_id": team_id}, ) return self.respond(status=404) # no matter how many orgs are using the integration # we have to delete the integration because the auth has been revoked # an app can only be installed once for a team (unless it's deleted and re-installed) # this is different than Vercel, for example, which can have multiple installations # for the same team in Vercel with different auth tokens for org in integration.organizations.all(): create_audit_entry( request=request, organization=org, target_object=integration.id, event=AuditLogEntryEvent.INTEGRATION_REMOVE, actor_label="Teams User", data={ "provider": integration.provider, "name": integration.name, "team_id": team_id, }, ) integration.delete() return self.respond(status=204)
def test_audit_entry_integration_log(self): project = self.create_project() self.login_as(user=self.user) entry = create_audit_entry( request=self.req, organization=self.project.organization, target_object=self.project.id, event=AuditLogEntryEvent.INTEGRATION_ADD, data={'integration': 'webhooks', 'project': project.slug}, ) assert ('enabled') in entry.get_note() assert entry.actor == self.user assert entry.target_object == self.project.id assert entry.event == AuditLogEntryEvent.INTEGRATION_ADD entry2 = create_audit_entry( request=self.req, organization=self.project.organization, target_object=self.project.id, event=AuditLogEntryEvent.INTEGRATION_EDIT, data={'integration': 'webhooks', 'project': project.slug}, ) assert ('edited') in entry2.get_note() assert entry2.actor == self.user assert entry2.target_object == self.project.id assert entry2.event == AuditLogEntryEvent.INTEGRATION_EDIT entry3 = create_audit_entry( request=self.req, organization=self.project.organization, target_object=self.project.id, event=AuditLogEntryEvent.INTEGRATION_REMOVE, data={'integration': 'webhooks', 'project': project.slug}, ) assert ('disable') in entry3.get_note() assert entry3.actor == self.user assert entry3.target_object == self.project.id assert entry3.event == AuditLogEntryEvent.INTEGRATION_REMOVE
def accept_invite(self, user=None): om = self.om if user is None: user = self.request.user if self.member_already_exists: self.handle_member_already_exists() om.delete() return try: provider = AuthProvider.objects.get(organization=om.organization) except AuthProvider.DoesNotExist: provider = None # If SSO is required, check for valid AuthIdentity if provider and not provider.flags.allow_unlinked: # AuthIdentity has a unique constraint on provider and user if not AuthIdentity.objects.filter(auth_provider=provider, user=user).exists(): self.handle_member_has_no_sso() return om.set_user(user) om.save() create_audit_entry( self.request, actor=user, organization=om.organization, target_object=om.id, target_user=user, event=AuditLogEntryEvent.MEMBER_ACCEPT, data=om.get_audit_log_data(), ) self.handle_success() metrics.incr("organization.invite-accepted", sample_rate=1.0) return om
def disable_2fa_required(request, organization): require_2fa = organization.flags.require_2fa if require_2fa and require_2fa.is_set: organization.update( flags=models.F('flags').bitand(~Organization.flags.require_2fa) ) logger.info( 'Require 2fa disabled during saml sso setup', extra={ 'organization_id': organization.id, } ) create_audit_entry( request=request, organization=organization, target_object=organization.id, event=AuditLogEntryEvent.ORG_EDIT, data={ 'require_2fa': u'to False when enabling SAML SSO' }, )
def test_audit_entry_org_restore_log(self): entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.org.id, event=AuditLogEntryEvent.ORG_RESTORE, data=self.org.get_audit_log_data(), ) assert ('restored') in entry.get_note() assert entry.actor == self.user assert entry.target_object == self.org.id assert entry.event == AuditLogEntryEvent.ORG_RESTORE
def test_audit_entry_api(self): org = self.create_organization() apikey = ApiKey.objects.create(organization=org, allowed_origins="*") req = FakeHttpRequest(AnonymousUser()) req.auth = apikey entry = create_audit_entry(req) assert entry.actor_key == apikey assert entry.actor is None assert entry.ip_address == req.META["REMOTE_ADDR"] self.assert_no_delete_log_created()
def test_audit_entry_project_edit_log_regression(self): entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.project.id, event=AuditLogEntryEvent.PROJECT_EDIT, data={"new_slug": "new"}, ) assert entry.actor == self.user assert entry.target_object == self.project.id assert entry.event == AuditLogEntryEvent.PROJECT_EDIT assert entry.get_note() == "edited project settings in new_slug to new"
def test_audit_entry_project_edit_log(self): entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.project.id, event=AuditLogEntryEvent.PROJECT_EDIT, data={"old_slug": "old", "new_slug": "new"}, ) assert entry.actor == self.user assert entry.target_object == self.project.id assert entry.event == AuditLogEntryEvent.PROJECT_EDIT assert entry.get_note() == "renamed project slug from old to new"
def test_audit_entry_project_delete_log(self): entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.project.id, event=AuditLogEntryEvent.PROJECT_REMOVE, data=self.project.get_audit_log_data(), ) assert entry.actor == self.user assert entry.target_object == self.project.id assert entry.event == AuditLogEntryEvent.PROJECT_REMOVE deleted_project = DeletedProject.objects.get(slug=self.project.slug) self.assert_valid_deleted_log(deleted_project, self.project)
def test_audit_entry_team_delete_log(self): entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.team.id, event=AuditLogEntryEvent.TEAM_REMOVE, data=self.team.get_audit_log_data(), ) assert entry.actor == self.user assert entry.target_object == self.team.id assert entry.event == AuditLogEntryEvent.TEAM_REMOVE deleted_team = DeletedTeam.objects.get(slug=self.team.slug) self.assert_valid_deleted_log(deleted_team, self.team)
def test_audit_entry_org_delete_log(self): entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.org.id, event=AuditLogEntryEvent.ORG_REMOVE, data=self.org.get_audit_log_data(), ) assert entry.actor == self.user assert entry.target_object == self.org.id assert entry.event == AuditLogEntryEvent.ORG_REMOVE deleted_org = DeletedOrganization.objects.get(slug=self.org.slug) self.assert_valid_deleted_log(deleted_org, self.org)
def test_audit_entry_api(self): org = self.create_organization() apikey = ApiKey.objects.create( organization=org, allowed_origins='*', ) req = FakeHttpRequest(AnonymousUser()) req.auth = apikey entry = create_audit_entry(req) assert entry.actor_key == apikey assert entry.actor is None assert entry.ip_address == req.META['REMOTE_ADDR'] self.assert_no_delete_log_created()
def test_audit_entry_project_delete_log(self): entry = create_audit_entry( request=self.req, organization=self.org, target_object=self.project.id, event=AuditLogEntryEvent.PROJECT_REMOVE, data=self.project.get_audit_log_data(), ) assert entry.actor == self.user assert entry.target_object == self.project.id assert entry.event == AuditLogEntryEvent.PROJECT_REMOVE deleted_project = DeletedProject.objects.get(slug=self.project.slug) self.assert_valid_deleted_log(deleted_project, self.project) assert deleted_project.platform == self.project.platform
def create_audit_entry(self, request, transaction_id=None, **kwargs): return create_audit_entry(request, transaction_id, audit_logger, **kwargs)