def delete_users_orgs(**kwargs): if not settings.DEMO_MODE: return logger.info("delete_users_orgs.start") # delete everything older than a day cutoff_time = timezone.now() - timedelta(days=1) # note this only runs in demo mode (not SaaS) so the underlying tables here are small org_list = Organization.objects.filter( demoorganization__date_assigned__lte=cutoff_time, demoorganization__status=DemoOrgStatus.ACTIVE, status__in=( OrganizationStatus.ACTIVE, OrganizationStatus.PENDING_DELETION, ), ) # first mark orgs for deletion org_list.update(status=OrganizationStatus.PENDING_DELETION) # next delete the users User.objects.filter(demouser__isnull=False, demouser__date_assigned__lte=cutoff_time).delete() # now finally delete the orgs for org in org_list: # apply async so if so we continue if one org aborts logger.info("delete_initializing_orgs.delete", extra={"organization_slug": org.slug}) delete_organization.apply_async(kwargs={"object_id": org.id})
def delete(self, request, organization): """ Delete an Organization `````````````````````` Schedules an organization for deletion. This API endpoint cannot be invoked without a user context for security reasons. This means that at present an organization can only be deleted from the Sentry UI. Deletion happens asynchronously and therefor is not immediate. However once deletion has begun the state of a project changes and will be hidden from most public views. :pparam string organization_slug: the slug of the organization the team should be created for. :auth: required, user-context-needed """ if not request.user.is_authenticated(): return Response({'detail': 'This request requires a user.'}, status=401) if organization.is_default: return Response({'detail': ERR_DEFAULT_ORG}, status=400) updated = Organization.objects.filter( id=organization.id, status=OrganizationStatus.VISIBLE, ).update(status=OrganizationStatus.PENDING_DELETION) if updated: transaction_id = uuid4().hex countdown = 86400 entry = self.create_audit_entry( request=request, organization=organization, target_object=organization.id, event=AuditLogEntryEvent.ORG_REMOVE, data=organization.get_audit_log_data(), transaction_id=transaction_id, ) organization.send_delete_confirmation(entry, countdown) delete_organization.apply_async( kwargs={ 'object_id': organization.id, 'transaction_id': transaction_id, }, countdown=countdown, ) delete_logger.info('object.delete.queued', extra={ 'object_id': organization.id, 'transaction_id': transaction_id, 'model': Organization.__name__, }) return Response(status=204)
def delete_initializing_orgs(**kwargs): """ Deletes orgs that are still in the initializing state. This happens if Sentry is killed while an organization is being created. """ if not settings.DEMO_MODE: return logger.info("delete_initializing_orgs.start") # delete everything older than MAX_INITIALIZATION_TIME max_init_time = settings.DEMO_DATA_GEN_PARAMS["MAX_INITIALIZATION_TIME"] cutoff_time = timezone.now() - timedelta(minutes=max_init_time) # note this only runs in demo mode (not SaaS) so the underlying tables here are small org_list = Organization.objects.filter( demoorganization__date_added__lte=cutoff_time, demoorganization__status=DemoOrgStatus.INITIALIZING, ) # first mark orgs for deletion org_list.update(status=OrganizationStatus.PENDING_DELETION) # now finally delete the orgs for org in org_list: # apply async so if so we continue if one org aborts logger.info("delete_initializing_orgs.delete", extra={"organization_slug": org.slug}) delete_organization.apply_async(kwargs={"object_id": org.id}) # build up the org buffer at the end to replace the orgs being removed build_up_org_buffer()
def handle(self, request, organization): if organization.is_default: messages.add_message(request, messages.ERROR, ERR_DEFAULT_ORG) return self.redirect(reverse('sentry-organization-home', args=[ organization.slug ])) form = self.get_form(request, organization) if form.is_valid(): if organization.status != OrganizationStatus.PENDING_DELETION: organization.update(status=OrganizationStatus.PENDING_DELETION) delete_organization.apply_async(kwargs={ 'object_id': organization.id, }, countdown=60 * 5) messages.add_message(request, messages.SUCCESS, MSG_REMOVE_SUCCESS % (organization.name,)) return self.redirect(reverse('sentry')) context = { 'form': form, 'team_list': organization.team_set.all(), } return self.respond('sentry/remove-organization.html', context)
def handle(self, request, organization): if organization.is_default: messages.add_message(request, messages.ERROR, ERR_DEFAULT_ORG) return self.redirect(reverse('sentry-organization-home', args=[ organization.slug ])) form = self.get_form(request, organization) if form.is_valid(): if organization.status != OrganizationStatus.PENDING_DELETION: organization.update(status=OrganizationStatus.PENDING_DELETION) delete_organization.apply_async(kwargs={ 'object_id': organization.id, }, countdown=60 * 5) messages.add_message(request, messages.SUCCESS, MSG_REMOVE_SUCCESS % (organization.name,)) return self.redirect(reverse('sentry')) context = { 'form': form, 'team_list': organization.team_set.all(), } return self.respond('sentry/remove-organization.html', context)
def delete(self, request, organization): """ Delete an Organization `````````````````````` Schedules an organization for deletion. This API endpoint cannot be invoked without a user context for security reasons. This means that at present an organization can only be deleted from the Sentry UI. Deletion happens asynchronously and therefor is not immediate. However once deletion has begun the state of a project changes and will be hidden from most public views. :pparam string organization_slug: the slug of the organization the team should be created for. :auth: required, user-context-needed """ if not request.user.is_authenticated(): return Response({'detail': 'This request requires a user.'}, status=401) if organization.is_default: return Response({'detail': ERR_DEFAULT_ORG}, status=400) updated = Organization.objects.filter( id=organization.id, status=OrganizationStatus.VISIBLE, ).update(status=OrganizationStatus.PENDING_DELETION) if updated: transaction_id = uuid4().hex countdown = 86400 entry = self.create_audit_entry( request=request, organization=organization, target_object=organization.id, event=AuditLogEntryEvent.ORG_REMOVE, data=organization.get_audit_log_data(), transaction_id=transaction_id, ) organization.send_delete_confirmation(entry, countdown) delete_organization.apply_async( kwargs={ 'object_id': organization.id, 'transaction_id': transaction_id, }, countdown=countdown, ) delete_logger.info('object.delete.queued', extra={ 'object_id': organization.id, 'transaction_id': transaction_id, 'model': Organization.__name__, }) return Response(status=204)
def handle(self, request, organization): if organization.is_default: messages.add_message(request, messages.ERROR, ERR_DEFAULT_ORG) return self.redirect(reverse('sentry-organization-home', args=[organization.slug])) form = self.get_form(request, organization) if form.is_valid(): updated = Organization.objects.filter( id=organization.id, status=OrganizationStatus.VISIBLE, ).update(status=OrganizationStatus.PENDING_DELETION) if updated: transaction_id = uuid4().hex countdown = 86400 entry = self.create_audit_entry( request=request, organization=organization, target_object=organization.id, event=AuditLogEntryEvent.ORG_REMOVE, data=organization.get_audit_log_data(), transaction_id=transaction_id, ) organization.send_delete_confirmation(entry, countdown) delete_organization.apply_async( kwargs={ 'object_id': organization.id, 'transaction_id': transaction_id, 'actor_id': request.user.id, }, countdown=countdown, ) delete_logger.info( 'object.delete.queued', extra={ 'object_id': organization.id, 'transaction_id': transaction_id, 'model': Organization.__name__, } ) messages.add_message( request, messages.SUCCESS, MSG_REMOVE_SUCCESS % (organization.name, ) ) return self.redirect(reverse('sentry')) context = { 'form': form, 'team_list': organization.team_set.all(), } return self.respond('sentry/remove-organization.html', context)
def handle(self, request, organization): if organization.is_default: messages.add_message(request, messages.ERROR, ERR_DEFAULT_ORG) return self.redirect(reverse('sentry-organization-home', args=[organization.slug])) form = self.get_form(request, organization) if form.is_valid(): updated = Organization.objects.filter( id=organization.id, status=OrganizationStatus.VISIBLE, ).update(status=OrganizationStatus.PENDING_DELETION) if updated: transaction_id = uuid4().hex countdown = 86400 entry = self.create_audit_entry( request=request, organization=organization, target_object=organization.id, event=AuditLogEntryEvent.ORG_REMOVE, data=organization.get_audit_log_data(), transaction_id=transaction_id, ) organization.send_delete_confirmation(entry, countdown) delete_organization.apply_async( kwargs={ 'object_id': organization.id, 'transaction_id': transaction_id, }, countdown=countdown, ) delete_logger.info( 'object.delete.queued', extra={ 'object_id': organization.id, 'transaction_id': transaction_id, 'model': Organization.__name__, } ) messages.add_message( request, messages.SUCCESS, MSG_REMOVE_SUCCESS % (organization.name, ) ) return self.redirect(reverse('sentry')) context = { 'form': form, 'team_list': organization.team_set.all(), } return self.respond('sentry/remove-organization.html', context)
def create_demo_org(quick=False) -> Organization: # wrap the main org setup in transaction with transaction.atomic(): name = generate_random_name() slug = slugify(name) demo_org = DemoOrganization.create_org(name=name, slug=slug) org = demo_org.organization logger.info("create_demo_org.created_org", {"organization_slug": slug}) owner = User.objects.get(email=settings.DEMO_ORG_OWNER_EMAIL) OrganizationMember.objects.create(organization=org, user=owner, role=roles.get_top_dog().id) team = org.team_set.create(name=org.name) python_project = Project.objects.create(name="Python", organization=org, platform="python") python_project.add_team(team) react_project = Project.objects.create( name="React", organization=org, platform="javascript-react" ) react_project.add_team(team) # we'll be adding transactions later Project.objects.filter(organization=org).update( flags=F("flags").bitor(Project.flags.has_transactions) ) logger.info( "create_demo_org.post-transaction", extra={"organization_slug": org.slug, "quick": quick}, ) try: handle_react_python_scenario(react_project, python_project, quick=quick) except Exception as e: logger.error( "create_demo_org.population_error", extra={"organization_slug": org.slug, "quick": quick, "error": str(e)}, ) # delete the organization if data population fails org.status = OrganizationStatus.PENDING_DELETION org.save() delete_organization.apply_async(kwargs={"object_id": org.id}) raise # update the org status now that it's populated demo_org.status = DemoOrgStatus.PENDING demo_org.save() return org
def handle_delete(self, request, organization): """ This method exists as a way for getsentry to override this endpoint with less duplication. """ if not request.user.is_authenticated: return self.respond({"detail": ERR_NO_USER}, status=401) if organization.is_default: return self.respond({"detail": ERR_DEFAULT_ORG}, status=400) updated = Organization.objects.filter( id=organization.id, status=OrganizationStatus.VISIBLE).update( status=OrganizationStatus.PENDING_DELETION) if updated: transaction_id = uuid4().hex countdown = 86400 entry = self.create_audit_entry( request=request, organization=organization, target_object=organization.id, event=AuditLogEntryEvent.ORG_REMOVE, data=organization.get_audit_log_data(), transaction_id=transaction_id, ) organization.send_delete_confirmation(entry, countdown) delete_organization.apply_async( kwargs={ "object_id": organization.id, "transaction_id": transaction_id, "actor_id": request.user.id, }, countdown=countdown, ) delete_logger.info( "object.delete.queued", extra={ "object_id": organization.id, "transaction_id": transaction_id, "model": Organization.__name__, }, ) context = serialize( organization, request.user, org_serializers.DetailedOrganizationSerializerWithProjectsAndTeams( ), access=request.access, ) return self.respond(context, status=202)
def handle(self, request, organization): if organization.is_default: messages.add_message(request, messages.ERROR, ERR_DEFAULT_ORG) return self.redirect( reverse('sentry-organization-home', args=[organization.slug])) form = self.get_form(request, organization) if form.is_valid(): transaction_id = uuid4().hex logging.getLogger('sentry.deletions').info( 'remove.organization', extra={ 'organization_id': organization.id, 'organization_slug': organization.slug, 'actor_id': request.user.id, 'transaction_id': transaction_id, 'ip_address': request.META['REMOTE_ADDR'], }) updated = Organization.objects.filter( id=organization.id, status=OrganizationStatus.VISIBLE, ).update(status=OrganizationStatus.PENDING_DELETION) if updated: delete_organization.apply_async( kwargs={ 'object_id': organization.id, 'transaction_id': transaction_id, }, countdown=86400, ) messages.add_message(request, messages.SUCCESS, MSG_REMOVE_SUCCESS % (organization.name, )) return self.redirect(reverse('sentry')) context = { 'form': form, 'team_list': organization.team_set.all(), } return self.respond('sentry/remove-organization.html', context)
def delete_users_orgs(**kwargs): if not settings.DEMO_MODE: return # delete everything older than a day cutoff_time = timezone.now() - timedelta(days=1) # first mark orgs for deletion # note this only runs in demo mode (not SaaS) so the underlying tables here are small org_list = Organization.objects.filter( date_added__lte=cutoff_time, flags=F("flags").bitor(Organization.flags["demo_mode"])) org_list.update(status=OrganizationStatus.PENDING_DELETION) # next delete the users User.objects.filter(date_joined__lte=cutoff_time, flags=F("flags").bitor( User.flags["demo_mode"])).delete() # now finally delete the orgs for org in org_list: # apply async so if so we continue if one org aborts delete_organization.apply_async(kwargs={"object_id": org.id})
def create_demo_org(quick=False) -> Organization: with sentry_sdk.start_transaction(op="create_demo_org", name="create_demo_org", sampled=True): sentry_sdk.set_tag("quick", quick) # wrap the main org setup in transaction with transaction.atomic(): name = generate_random_name() slug = slugify(name) projects = [] demo_org = DemoOrganization.create_org(name=name, slug=slug) org = demo_org.organization logger.info("create_demo_org.created_org", {"organization_slug": slug}) owner = User.objects.get(email=settings.DEMO_ORG_OWNER_EMAIL) OrganizationMember.objects.create( organization=org, user=owner, role=roles.get_top_dog().id ) team = org.team_set.create(name=org.name) def create_project(name, platform): project = Project.objects.create(name=name, organization=org, platform=platform) project.add_team(team) projects.append(project) return project python_project = create_project("Python", "python") react_project = create_project("React", "javascript-react") react_native_project = create_project("React-Native", "react-native") android_project = create_project("Android", "android") ios_project = create_project("iOS", "apple-ios") populate_org_members(org, team) # we'll be adding transactions later Project.objects.filter(organization=org).update( flags=F("flags").bitor(Project.flags.has_transactions) ) logger.info( "create_demo_org.post-transaction", extra={"organization_slug": org.slug, "quick": quick}, ) with sentry_sdk.start_span(op="handle_react_python_mobile_scenario"): try: data_population = DataPopulation(org, quick=quick) data_population.generate_releases(projects) data_population.generate_saved_search(projects) data_population.handle_react_python_scenario(react_project, python_project) data_population.handle_mobile_scenario( ios_project, android_project, react_native_project ) except Exception as e: logger.error( "create_demo_org.population_error", extra={"organization_slug": org.slug, "quick": quick, "error": str(e)}, ) # delete the organization if data population fails org.status = OrganizationStatus.PENDING_DELETION org.save() delete_organization.apply_async(kwargs={"object_id": org.id}) raise # update the org status now that it's populated demo_org.status = DemoOrgStatus.PENDING demo_org.save() logger.info( "create_demo_org.complete", extra={"organization_slug": org.slug, "quick": quick}, ) return org