trueppm-api 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- trueppm_api/__init__.py +27 -0
- trueppm_api/apps/access/__init__.py +0 -0
- trueppm_api/apps/access/apps.py +19 -0
- trueppm_api/apps/access/groups.py +518 -0
- trueppm_api/apps/access/management/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/__init__.py +0 -0
- trueppm_api/apps/access/management/commands/create_admin.py +346 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +483 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +184 -0
- trueppm_api/apps/access/migrations/0002_alter_projectmembership_id.py +29 -0
- trueppm_api/apps/access/migrations/0003_projectmembership_soft_delete.py +24 -0
- trueppm_api/apps/access/migrations/0004_alter_projectmembership_role.py +34 -0
- trueppm_api/apps/access/migrations/0005_program_entity_and_membership.py +76 -0
- trueppm_api/apps/access/migrations/0006_role_ordinal_spacing.py +113 -0
- trueppm_api/apps/access/migrations/0007_projectmembership_joined_at_and_more.py +35 -0
- trueppm_api/apps/access/migrations/0008_projectmembership_source_group.py +25 -0
- trueppm_api/apps/access/migrations/0009_projectmembership_pm_proj_serverver_idx.py +20 -0
- trueppm_api/apps/access/migrations/0010_programmembership_joined_at_and_more.py +38 -0
- trueppm_api/apps/access/migrations/0011_alter_programmembership_unique_together_and_more.py +36 -0
- trueppm_api/apps/access/migrations/0012_programmembership_role_title.py +17 -0
- trueppm_api/apps/access/migrations/0013_squashed_0019_release_0_4.py +302 -0
- trueppm_api/apps/access/migrations/0013_userdefinedmentiongroup.py +82 -0
- trueppm_api/apps/access/migrations/0014_programmembership_progm_serverver_idx.py +19 -0
- trueppm_api/apps/access/migrations/0015_programuserdefinedmentiongroup.py +86 -0
- trueppm_api/apps/access/migrations/0016_externalstakeholder.py +67 -0
- trueppm_api/apps/access/migrations/0017_viewer_ordinal_one.py +143 -0
- trueppm_api/apps/access/migrations/0018_programmembership_sync_seq_and_more.py +52 -0
- trueppm_api/apps/access/migrations/0019_programmembership_reinstated_at_and_more.py +22 -0
- trueppm_api/apps/access/migrations/__init__.py +0 -0
- trueppm_api/apps/access/minter_role.py +38 -0
- trueppm_api/apps/access/models.py +588 -0
- trueppm_api/apps/access/permissions.py +3263 -0
- trueppm_api/apps/access/serializers.py +1007 -0
- trueppm_api/apps/access/services.py +878 -0
- trueppm_api/apps/access/signals.py +171 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +96 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1965 -0
- trueppm_api/apps/agents/__init__.py +6 -0
- trueppm_api/apps/agents/apps.py +11 -0
- trueppm_api/apps/agents/canonical.py +73 -0
- trueppm_api/apps/agents/deferred.py +108 -0
- trueppm_api/apps/agents/management/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/__init__.py +0 -0
- trueppm_api/apps/agents/management/commands/audit_prune.py +137 -0
- trueppm_api/apps/agents/management/commands/audit_verify.py +101 -0
- trueppm_api/apps/agents/middleware.py +44 -0
- trueppm_api/apps/agents/migrations/0001_initial.py +231 -0
- trueppm_api/apps/agents/migrations/0001_squashed_0003_release_0_4.py +344 -0
- trueppm_api/apps/agents/migrations/0002_agentactioncheckpoint.py +71 -0
- trueppm_api/apps/agents/migrations/0003_agentactionrefusaldetail.py +58 -0
- trueppm_api/apps/agents/migrations/__init__.py +0 -0
- trueppm_api/apps/agents/models.py +371 -0
- trueppm_api/apps/agents/refusal.py +103 -0
- trueppm_api/apps/agents/serializers.py +85 -0
- trueppm_api/apps/agents/services.py +397 -0
- trueppm_api/apps/agents/signals.py +30 -0
- trueppm_api/apps/agents/urls.py +15 -0
- trueppm_api/apps/agents/views.py +109 -0
- trueppm_api/apps/csvimport/__init__.py +12 -0
- trueppm_api/apps/csvimport/apps.py +11 -0
- trueppm_api/apps/csvimport/mapping.py +363 -0
- trueppm_api/apps/csvimport/migrations/0001_initial.py +74 -0
- trueppm_api/apps/csvimport/migrations/0001_squashed_0003_release_0_4.py +90 -0
- trueppm_api/apps/csvimport/migrations/0002_schedule_outline_batch_operations.py +37 -0
- trueppm_api/apps/csvimport/migrations/0003_csvimportrequest_date_order_and_more.py +26 -0
- trueppm_api/apps/csvimport/migrations/__init__.py +0 -0
- trueppm_api/apps/csvimport/models.py +111 -0
- trueppm_api/apps/csvimport/parser.py +1825 -0
- trueppm_api/apps/csvimport/services.py +48 -0
- trueppm_api/apps/csvimport/tasks.py +459 -0
- trueppm_api/apps/csvimport/template.py +37 -0
- trueppm_api/apps/csvimport/urls.py +50 -0
- trueppm_api/apps/csvimport/views.py +793 -0
- trueppm_api/apps/history/__init__.py +0 -0
- trueppm_api/apps/history/apps.py +12 -0
- trueppm_api/apps/history/changelog.py +359 -0
- trueppm_api/apps/history/diff_policy.py +178 -0
- trueppm_api/apps/history/serializers.py +104 -0
- trueppm_api/apps/history/signals.py +82 -0
- trueppm_api/apps/history/tasks.py +105 -0
- trueppm_api/apps/history/urls.py +38 -0
- trueppm_api/apps/history/views.py +443 -0
- trueppm_api/apps/idempotency/__init__.py +0 -0
- trueppm_api/apps/idempotency/apps.py +11 -0
- trueppm_api/apps/idempotency/migrations/0001_initial.py +71 -0
- trueppm_api/apps/idempotency/migrations/__init__.py +0 -0
- trueppm_api/apps/idempotency/mixins.py +226 -0
- trueppm_api/apps/idempotency/models.py +75 -0
- trueppm_api/apps/idempotency/tasks.py +61 -0
- trueppm_api/apps/integrations/__init__.py +13 -0
- trueppm_api/apps/integrations/apps.py +59 -0
- trueppm_api/apps/integrations/connections.py +806 -0
- trueppm_api/apps/integrations/encryption.py +92 -0
- trueppm_api/apps/integrations/external_sources.py +863 -0
- trueppm_api/apps/integrations/git_automation_services.py +231 -0
- trueppm_api/apps/integrations/git_webhook_auth.py +243 -0
- trueppm_api/apps/integrations/http.py +597 -0
- trueppm_api/apps/integrations/me_work.py +218 -0
- trueppm_api/apps/integrations/migrations/0001_initial.py +64 -0
- trueppm_api/apps/integrations/migrations/0001_squashed_0006_tasklink_description_tasklink_preview_type_and_more.py +187 -0
- trueppm_api/apps/integrations/migrations/0002_tasklink.py +67 -0
- trueppm_api/apps/integrations/migrations/0003_tasklink_tasklink_serverver_idx.py +17 -0
- trueppm_api/apps/integrations/migrations/0004_tasklink_custom_title_tasklink_labels.py +25 -0
- trueppm_api/apps/integrations/migrations/0005_boardautomation.py +56 -0
- trueppm_api/apps/integrations/migrations/0006_tasklink_description_tasklink_preview_type_and_more.py +40 -0
- trueppm_api/apps/integrations/migrations/0007_squashed_0014_release_0_4.py +225 -0
- trueppm_api/apps/integrations/migrations/0007_tasklink_created_at.py +23 -0
- trueppm_api/apps/integrations/migrations/0008_integrationcredential_config_externalworkitem.py +66 -0
- trueppm_api/apps/integrations/migrations/0009_externalsyncrequest_and_more.py +96 -0
- trueppm_api/apps/integrations/migrations/0010_externalworkitem_due_date.py +17 -0
- trueppm_api/apps/integrations/migrations/0011_tasklink_sync_seq_tasklink_tasklink_syncseq_idx.py +22 -0
- trueppm_api/apps/integrations/migrations/0012_boardautomation_last_delivery_at_and_more.py +42 -0
- trueppm_api/apps/integrations/migrations/0013_externalsyncrequest_attempt_count_and_more.py +26 -0
- trueppm_api/apps/integrations/migrations/0014_tasklink_created_by.py +27 -0
- trueppm_api/apps/integrations/migrations/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +635 -0
- trueppm_api/apps/integrations/notification_channels.py +57 -0
- trueppm_api/apps/integrations/opengraph.py +229 -0
- trueppm_api/apps/integrations/outgoing.py +201 -0
- trueppm_api/apps/integrations/providers.py +528 -0
- trueppm_api/apps/integrations/registry.py +345 -0
- trueppm_api/apps/integrations/serializers.py +453 -0
- trueppm_api/apps/integrations/services.py +171 -0
- trueppm_api/apps/integrations/tasks.py +734 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1158 -0
- trueppm_api/apps/jiraimport/__init__.py +8 -0
- trueppm_api/apps/jiraimport/apps.py +11 -0
- trueppm_api/apps/jiraimport/migrations/0001_initial.py +72 -0
- trueppm_api/apps/jiraimport/migrations/__init__.py +0 -0
- trueppm_api/apps/jiraimport/models.py +70 -0
- trueppm_api/apps/jiraimport/parser.py +350 -0
- trueppm_api/apps/jiraimport/services.py +46 -0
- trueppm_api/apps/jiraimport/tasks.py +280 -0
- trueppm_api/apps/jiraimport/urls.py +15 -0
- trueppm_api/apps/jiraimport/views.py +153 -0
- trueppm_api/apps/msproject/__init__.py +0 -0
- trueppm_api/apps/msproject/apps.py +11 -0
- trueppm_api/apps/msproject/dataclasses.py +153 -0
- trueppm_api/apps/msproject/exporter.py +551 -0
- trueppm_api/apps/msproject/extended_attributes.py +77 -0
- trueppm_api/apps/msproject/importer.py +1095 -0
- trueppm_api/apps/msproject/migrations/0001_import_request.py +77 -0
- trueppm_api/apps/msproject/migrations/0001_squashed_0002_importrequest_creates_project.py +80 -0
- trueppm_api/apps/msproject/migrations/0002_importrequest_creates_project.py +15 -0
- trueppm_api/apps/msproject/migrations/__init__.py +0 -0
- trueppm_api/apps/msproject/models.py +81 -0
- trueppm_api/apps/msproject/parser.py +1276 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +439 -0
- trueppm_api/apps/msproject/urls.py +39 -0
- trueppm_api/apps/msproject/views.py +531 -0
- trueppm_api/apps/notifications/__init__.py +7 -0
- trueppm_api/apps/notifications/apps.py +15 -0
- trueppm_api/apps/notifications/backfill.py +79 -0
- trueppm_api/apps/notifications/categories.py +112 -0
- trueppm_api/apps/notifications/delivery_limits.py +258 -0
- trueppm_api/apps/notifications/digests.py +462 -0
- trueppm_api/apps/notifications/email_backend.py +350 -0
- trueppm_api/apps/notifications/email_health.py +128 -0
- trueppm_api/apps/notifications/migrations/0001_initial.py +207 -0
- trueppm_api/apps/notifications/migrations/0001_squashed_0006_notification_task.py +299 -0
- trueppm_api/apps/notifications/migrations/0002_project_notification_preference.py +67 -0
- trueppm_api/apps/notifications/migrations/0003_projectnotificationpreference_paused.py +17 -0
- trueppm_api/apps/notifications/migrations/0004_clean_unknown_matrix_keys.py +30 -0
- trueppm_api/apps/notifications/migrations/0005_notification_body_notification_event_type_and_more.py +27 -0
- trueppm_api/apps/notifications/migrations/0006_notification_task.py +25 -0
- trueppm_api/apps/notifications/migrations/0007_squashed_0011_release_0_4.py +204 -0
- trueppm_api/apps/notifications/migrations/0007_workspaceemailsettings.py +80 -0
- trueppm_api/apps/notifications/migrations/0008_notification_snoozed_until.py +17 -0
- trueppm_api/apps/notifications/migrations/0009_usernotificationsettings.py +43 -0
- trueppm_api/apps/notifications/migrations/0010_projectnotificationpreference_schema_version.py +20 -0
- trueppm_api/apps/notifications/migrations/0011_usernotificationsettings_digest_hour_and_more.py +96 -0
- trueppm_api/apps/notifications/migrations/__init__.py +0 -0
- trueppm_api/apps/notifications/models.py +1148 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +1094 -0
- trueppm_api/apps/notifications/services.py +1489 -0
- trueppm_api/apps/notifications/tasks.py +762 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +919 -0
- trueppm_api/apps/observability/__init__.py +0 -0
- trueppm_api/apps/observability/apps.py +31 -0
- trueppm_api/apps/observability/logging.py +213 -0
- trueppm_api/apps/observability/migrations/0001_initial.py +35 -0
- trueppm_api/apps/observability/migrations/0001_squashed_0003_alter_purgerun_trigger.py +154 -0
- trueppm_api/apps/observability/migrations/0002_purgerun_retentionpolicy_retentionschedule.py +131 -0
- trueppm_api/apps/observability/migrations/0003_alter_purgerun_trigger.py +21 -0
- trueppm_api/apps/observability/migrations/0004_alter_retentionpolicy_key.py +28 -0
- trueppm_api/apps/observability/migrations/__init__.py +0 -0
- trueppm_api/apps/observability/models.py +141 -0
- trueppm_api/apps/observability/otel/__init__.py +45 -0
- trueppm_api/apps/observability/otel/attributes.py +247 -0
- trueppm_api/apps/observability/otel/export_health.py +533 -0
- trueppm_api/apps/observability/otel/instrumentation.py +351 -0
- trueppm_api/apps/observability/otel/metrics.py +566 -0
- trueppm_api/apps/observability/otel/provider.py +511 -0
- trueppm_api/apps/observability/otel/request_attributes.py +204 -0
- trueppm_api/apps/observability/purge_registry.py +124 -0
- trueppm_api/apps/observability/retention.py +139 -0
- trueppm_api/apps/observability/selectors.py +993 -0
- trueppm_api/apps/observability/serializers.py +142 -0
- trueppm_api/apps/observability/services.py +407 -0
- trueppm_api/apps/observability/tasks.py +266 -0
- trueppm_api/apps/observability/urls.py +27 -0
- trueppm_api/apps/observability/views.py +537 -0
- trueppm_api/apps/profiles/__init__.py +0 -0
- trueppm_api/apps/profiles/apps.py +15 -0
- trueppm_api/apps/profiles/constants.py +54 -0
- trueppm_api/apps/profiles/migrations/0001_initial.py +55 -0
- trueppm_api/apps/profiles/migrations/0001_squashed_0004_userprofile_role_context.py +125 -0
- trueppm_api/apps/profiles/migrations/0002_userprofile_hidden_views.py +17 -0
- trueppm_api/apps/profiles/migrations/0003_projectvisit.py +63 -0
- trueppm_api/apps/profiles/migrations/0004_userprofile_role_context.py +26 -0
- trueppm_api/apps/profiles/migrations/0005_squashed_0009_release_0_4.py +128 -0
- trueppm_api/apps/profiles/migrations/0005_userprofile_schedule_in_deliver.py +20 -0
- trueppm_api/apps/profiles/migrations/0006_userprofile_date_format_userprofile_timezone.py +36 -0
- trueppm_api/apps/profiles/migrations/0007_userpin.py +91 -0
- trueppm_api/apps/profiles/migrations/0008_remove_userprofile_schedule_in_deliver.py +71 -0
- trueppm_api/apps/profiles/migrations/0009_auth_user_email_upper_idx.py +81 -0
- trueppm_api/apps/profiles/migrations/__init__.py +0 -0
- trueppm_api/apps/profiles/models.py +276 -0
- trueppm_api/apps/profiles/serializers.py +199 -0
- trueppm_api/apps/profiles/services.py +541 -0
- trueppm_api/apps/profiles/views.py +124 -0
- trueppm_api/apps/projects/__init__.py +0 -0
- trueppm_api/apps/projects/actual_date_rules.py +163 -0
- trueppm_api/apps/projects/amend.py +133 -0
- trueppm_api/apps/projects/apps.py +38 -0
- trueppm_api/apps/projects/asset_feed.py +459 -0
- trueppm_api/apps/projects/asset_views.py +317 -0
- trueppm_api/apps/projects/attachment_policy.py +278 -0
- trueppm_api/apps/projects/authentication.py +458 -0
- trueppm_api/apps/projects/backfill.py +459 -0
- trueppm_api/apps/projects/backlog_services.py +324 -0
- trueppm_api/apps/projects/backlog_views.py +302 -0
- trueppm_api/apps/projects/batch_operation_views.py +264 -0
- trueppm_api/apps/projects/blocker_services.py +510 -0
- trueppm_api/apps/projects/board_activity.py +462 -0
- trueppm_api/apps/projects/board_activity_views.py +186 -0
- trueppm_api/apps/projects/board_lanes.py +134 -0
- trueppm_api/apps/projects/bulk_settings.py +194 -0
- trueppm_api/apps/projects/bundled_templates.py +215 -0
- trueppm_api/apps/projects/calendar_settings.py +180 -0
- trueppm_api/apps/projects/ceremony_views.py +244 -0
- trueppm_api/apps/projects/commit_moment.py +212 -0
- trueppm_api/apps/projects/config_notice.py +1330 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +132 -0
- trueppm_api/apps/projects/decisions_views.py +97 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +423 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4278 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2001 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1827 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1909 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1410 -0
- trueppm_api/apps/projects/guardrail_policy_source.py +269 -0
- trueppm_api/apps/projects/inbound_sync.py +555 -0
- trueppm_api/apps/projects/iteration_label.py +85 -0
- trueppm_api/apps/projects/key_views.py +468 -0
- trueppm_api/apps/projects/keys.py +205 -0
- trueppm_api/apps/projects/lifecycle.py +191 -0
- trueppm_api/apps/projects/management/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/__init__.py +0 -0
- trueppm_api/apps/projects/management/commands/backfill_in_progress_status.py +92 -0
- trueppm_api/apps/projects/management/commands/create_demo_share_link.py +368 -0
- trueppm_api/apps/projects/management/commands/export_program.py +50 -0
- trueppm_api/apps/projects/management/commands/import_seed.py +165 -0
- trueppm_api/apps/projects/management/commands/load_sample_project.py +514 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +406 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +192 -0
- trueppm_api/apps/projects/management/commands/shift_sample_dates.py +120 -0
- trueppm_api/apps/projects/mcp_settings.py +305 -0
- trueppm_api/apps/projects/methodology.py +178 -0
- trueppm_api/apps/projects/migration_helpers.py +75 -0
- trueppm_api/apps/projects/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5659 -0
- trueppm_api/apps/projects/migrations/0002_alter_uuid_pk_serialize_false.py +57 -0
- trueppm_api/apps/projects/migrations/0003_rename_task_project_index.py +25 -0
- trueppm_api/apps/projects/migrations/0004_soft_delete_and_dependency_versioned.py +70 -0
- trueppm_api/apps/projects/migrations/0005_task_assignee.py +33 -0
- trueppm_api/apps/projects/migrations/0006_task_utilization_index.py +31 -0
- trueppm_api/apps/projects/migrations/0007_task_is_milestone.py +28 -0
- trueppm_api/apps/projects/migrations/0008_baseline.py +119 -0
- trueppm_api/apps/projects/migrations/0009_historicaldependency_historicalproject_and_more.py +210 -0
- trueppm_api/apps/projects/migrations/0010_risk.py +229 -0
- trueppm_api/apps/projects/migrations/0011_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0012_task_status.py +27 -0
- trueppm_api/apps/projects/migrations/0013_task_planned_start.py +24 -0
- trueppm_api/apps/projects/migrations/0014_short_hex_ids.py +74 -0
- trueppm_api/apps/projects/migrations/0015_backfill_short_ids.py +48 -0
- trueppm_api/apps/projects/migrations/0016_task_actual_dates.py +42 -0
- trueppm_api/apps/projects/migrations/0017_boardcolumnconfig.py +43 -0
- trueppm_api/apps/projects/migrations/0018_estimation_governance.py +62 -0
- trueppm_api/apps/projects/migrations/0019_backfill_wbs_paths.py +28 -0
- trueppm_api/apps/projects/migrations/0020_task_status_5col.py +65 -0
- trueppm_api/apps/projects/migrations/0021_boardsavedview.py +64 -0
- trueppm_api/apps/projects/migrations/0022_task_status_changed_at_priority_rank.py +35 -0
- trueppm_api/apps/projects/migrations/0023_risk_framework_fields.py +96 -0
- trueppm_api/apps/projects/migrations/0024_riskcomment.py +52 -0
- trueppm_api/apps/projects/migrations/0025_sprint.py +352 -0
- trueppm_api/apps/projects/migrations/0026_project_methodology.py +30 -0
- trueppm_api/apps/projects/migrations/0027_sprint_retro.py +89 -0
- trueppm_api/apps/projects/migrations/0028_normalize_notes_field.py +42 -0
- trueppm_api/apps/projects/migrations/0029_complete_implies_full_progress.py +38 -0
- trueppm_api/apps/projects/migrations/0030_review_implies_full_progress.py +37 -0
- trueppm_api/apps/projects/migrations/0031_task_remaining_points.py +22 -0
- trueppm_api/apps/projects/migrations/0032_task_is_subtask_and_sprint_scope_change.py +90 -0
- trueppm_api/apps/projects/migrations/0033_task_my_work_index.py +38 -0
- trueppm_api/apps/projects/migrations/0034_inbound_task_sync.py +313 -0
- trueppm_api/apps/projects/migrations/0035_retro_versioned_and_suggested_assignee.py +173 -0
- trueppm_api/apps/projects/migrations/0036_program_entity_and_membership.py +157 -0
- trueppm_api/apps/projects/migrations/0037_sprint_capacity_points.py +20 -0
- trueppm_api/apps/projects/migrations/0038_taskcomment_commentreaction_commentacknowledgement_and_more.py +233 -0
- trueppm_api/apps/projects/migrations/0039_apitoken_and_more.py +88 -0
- trueppm_api/apps/projects/migrations/0040_program_general_fields.py +119 -0
- trueppm_api/apps/projects/migrations/0041_ceremony_template_and_phase_gate_config.py +210 -0
- trueppm_api/apps/projects/migrations/0041_project_general_fields.py +106 -0
- trueppm_api/apps/projects/migrations/0042_merge_20260522_0410.py +12 -0
- trueppm_api/apps/projects/migrations/0042_program_rollup_config.py +95 -0
- trueppm_api/apps/projects/migrations/0042_workflow_color_and_custom_fields.py +94 -0
- trueppm_api/apps/projects/migrations/0043_merge_20260522_1118.py +10 -0
- trueppm_api/apps/projects/migrations/0044_merge_20260522_rollup.py +10 -0
- trueppm_api/apps/projects/migrations/0044_project_archive_program_close.py +119 -0
- trueppm_api/apps/projects/migrations/0045_program_risk_policy.py +78 -0
- trueppm_api/apps/projects/migrations/0046_merge_20260523_2248.py +12 -0
- trueppm_api/apps/projects/migrations/0047_apitokenauditentry_program_and_more.py +55 -0
- trueppm_api/apps/projects/migrations/0048_historicalprogram_color_program_color.py +32 -0
- trueppm_api/apps/projects/migrations/0049_backlog_item.py +112 -0
- trueppm_api/apps/projects/migrations/0050_backlog_item_trgm_search.py +26 -0
- trueppm_api/apps/projects/migrations/0051_alter_calendar_working_days_alter_dependency_lag_and_more.py +144 -0
- trueppm_api/apps/projects/migrations/0052_recurring_tasks.py +278 -0
- trueppm_api/apps/projects/migrations/0053_dependency_dep_pred_serverver_idx_and_more.py +56 -0
- trueppm_api/apps/projects/migrations/0054_sprintscopechange_goal_impact_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0055_historicaltask_sprint_pending_and_more.py +51 -0
- trueppm_api/apps/projects/migrations/0056_historicalproject_prioritization_model_and_more.py +276 -0
- trueppm_api/apps/projects/migrations/0057_historicalsprint_binding_committed_snapshot_and_more.py +58 -0
- trueppm_api/apps/projects/migrations/0058_historicaltask_delivery_mode_and_more.py +72 -0
- trueppm_api/apps/projects/migrations/0059_forecastsnapshot.py +80 -0
- trueppm_api/apps/projects/migrations/0060_historicalprojectsignalprivacypolicy_and_more.py +90 -0
- trueppm_api/apps/projects/migrations/0061_baselinetask_story_points.py +17 -0
- trueppm_api/apps/projects/migrations/0062_project_is_sample.py +22 -0
- trueppm_api/apps/projects/migrations/0063_historicalsprint_wip_limit_sprint_wip_limit.py +22 -0
- trueppm_api/apps/projects/migrations/0064_sprinttaskoutcome.py +97 -0
- trueppm_api/apps/projects/migrations/0065_historicalsprint_goal_outcome_sprint_goal_outcome.py +32 -0
- trueppm_api/apps/projects/migrations/0066_historicalproject_lead_project_lead.py +38 -0
- trueppm_api/apps/projects/migrations/0067_sprint_exclude_from_velocity.py +22 -0
- trueppm_api/apps/projects/migrations/0068_project_recalculated_at.py +23 -0
- trueppm_api/apps/projects/migrations/0069_iteration_label.py +22 -0
- trueppm_api/apps/projects/migrations/0070_historicalprogram_iteration_label_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0071_backfill_iteration_label_inherit.py +35 -0
- trueppm_api/apps/projects/migrations/0072_pulseresponse_retroboarditem.py +129 -0
- trueppm_api/apps/projects/migrations/0073_sprinttaskoutcome_demo_ready.py +17 -0
- trueppm_api/apps/projects/migrations/0074_historicaltask_blocked_reason_task_blocked_reason.py +30 -0
- trueppm_api/apps/projects/migrations/0075_risk_decimal_short_id.py +42 -0
- trueppm_api/apps/projects/migrations/0076_sprinttaskoutcome_review_polish.py +45 -0
- trueppm_api/apps/projects/migrations/0077_task_blocker_fields.py +111 -0
- trueppm_api/apps/projects/migrations/0078_alter_risktask_unique_together_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0079_historicalproject_status_date_project_status_date.py +22 -0
- trueppm_api/apps/projects/migrations/0080_alter_historicaltask_type_alter_task_type.py +46 -0
- trueppm_api/apps/projects/migrations/0081_historicalprogram_allow_guests_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0082_project_last_sync_version.py +90 -0
- trueppm_api/apps/projects/migrations/0083_historicalprogram_mc_history_attribution_audience_and_more.py +108 -0
- trueppm_api/apps/projects/migrations/0084_tasknote.py +71 -0
- trueppm_api/apps/projects/migrations/0085_sprint_ix_sprint_velocity.py +33 -0
- trueppm_api/apps/projects/migrations/0086_board_card_search_trgm.py +33 -0
- trueppm_api/apps/projects/migrations/0087_historicalprogram_task_duration_change_percent_policy_and_more.py +155 -0
- trueppm_api/apps/projects/migrations/0088_historicalprogram_allowed_attachment_types_and_more.py +61 -0
- trueppm_api/apps/projects/migrations/0089_signalceilingraiseproposal_signalceilingraisevote_and_more.py +157 -0
- trueppm_api/apps/projects/migrations/0090_historicaltask_proj_histdate_index.py +59 -0
- trueppm_api/apps/projects/migrations/0091_project_board_cadence.py +39 -0
- trueppm_api/apps/projects/migrations/0092_program_target_date.py +22 -0
- trueppm_api/apps/projects/migrations/0093_cross_project_dependency_consent.py +58 -0
- trueppm_api/apps/projects/migrations/0094_project_decisions_policy.py +90 -0
- trueppm_api/apps/projects/migrations/0095_poker_session_vote.py +127 -0
- trueppm_api/apps/projects/migrations/0095_squashed_0097_release_0_3.py +225 -0
- trueppm_api/apps/projects/migrations/0096_crossprojectslipconflict.py +91 -0
- trueppm_api/apps/projects/migrations/0097_backlogitem_backlogitem_tags_gin_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0098_historicalproject_show_baselines_and_more.py +52 -0
- trueppm_api/apps/projects/migrations/0099_apitoken_scopes.py +48 -0
- trueppm_api/apps/projects/migrations/0100_task_dep_serverver_idx_concurrent.py +75 -0
- trueppm_api/apps/projects/migrations/0101_dependency_deleted_at_task_deleted_at.py +22 -0
- trueppm_api/apps/projects/migrations/0102_project_deleted_at_project_proj_isdel_deletedat_idx.py +25 -0
- trueppm_api/apps/projects/migrations/0103_taskdurationchangeevent_task_dur_evt_sprint_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0104_historicalproject_stale_task_threshold_days_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0104_trash_restore_1113.py +38 -0
- trueppm_api/apps/projects/migrations/0105_historical_changelog_indexes.py +76 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -0
- trueppm_api/apps/projects/migrations/0106_squashed_0158_release_0_4.py +2357 -0
- trueppm_api/apps/projects/migrations/0107_task_activity_event.py +70 -0
- trueppm_api/apps/projects/migrations/0108_remove_apitoken_api_token_scope_xor_and_more.py +117 -0
- trueppm_api/apps/projects/migrations/0109_projectexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0110_sharelink.py +123 -0
- trueppm_api/apps/projects/migrations/0111_projectcalendarlayer.py +67 -0
- trueppm_api/apps/projects/migrations/0112_sharelink_expires_at_alter_sharelink_content_kind.py +31 -0
- trueppm_api/apps/projects/migrations/0113_alter_historicaltask_percent_complete_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0114_historicalproject_default_member_role_and_more.py +28 -0
- trueppm_api/apps/projects/migrations/0115_historicalproject_end_date_shift_threshold_days_and_more.py +22 -0
- trueppm_api/apps/projects/migrations/0116_alter_taskactivityevent_event_type.py +28 -0
- trueppm_api/apps/projects/migrations/0117_label_tasklabel_label_tasks_and_more.py +126 -0
- trueppm_api/apps/projects/migrations/0118_programexportjob.py +82 -0
- trueppm_api/apps/projects/migrations/0119_historicalprogram_calendar_program_calendar.py +36 -0
- trueppm_api/apps/projects/migrations/0120_alter_backlogitem_item_type.py +29 -0
- trueppm_api/apps/projects/migrations/0121_task_three_point_estimate_ordered.py +64 -0
- trueppm_api/apps/projects/migrations/0122_task_relation.py +173 -0
- trueppm_api/apps/projects/migrations/0123_remove_historicalproject_agile_features_and_more.py +20 -0
- trueppm_api/apps/projects/migrations/0124_dependency_is_driving.py +17 -0
- trueppm_api/apps/projects/migrations/0125_historicalprogram_estimation_scale_and_more.py +68 -0
- trueppm_api/apps/projects/migrations/0126_projectcustomfield_show_on_card_taskcustomfieldvalue.py +80 -0
- trueppm_api/apps/projects/migrations/0127_historicalprogram_mcp_enabled_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0128_acceptancecriterion_sync_seq_apitoken_sync_seq_and_more.py +192 -0
- trueppm_api/apps/projects/migrations/0129_sharelink_show_milestone_dates.py +20 -0
- trueppm_api/apps/projects/migrations/0130_programimportjob.py +85 -0
- trueppm_api/apps/projects/migrations/0131_historicalprogram_sprint_picker_ready_only_default_and_more.py +32 -0
- trueppm_api/apps/projects/migrations/0132_task_scheduled_start.py +17 -0
- trueppm_api/apps/projects/migrations/0133_project_status_date_floor_armed_at.py +17 -0
- trueppm_api/apps/projects/migrations/0134_task_edited_at_task_seeded_at_task_source_id_and_more.py +65 -0
- trueppm_api/apps/projects/migrations/0135_projecttemplate_templateapplication_and_more.py +175 -0
- trueppm_api/apps/projects/migrations/0136_schedule_outline_batch_operations.py +118 -0
- trueppm_api/apps/projects/migrations/0137_collapse_dual_scope_api_tokens.py +54 -0
- trueppm_api/apps/projects/migrations/0138_apitokenauditentry_api_token_audit_owner_idx.py +18 -0
- trueppm_api/apps/projects/migrations/0139_historicaltask_duration_unit_task_duration_unit.py +32 -0
- trueppm_api/apps/projects/migrations/0140_historicaltask_auto_container_and_more.py +113 -0
- trueppm_api/apps/projects/migrations/0141_historicalproject_draft_started_at_and_more.py +44 -0
- trueppm_api/apps/projects/migrations/0142_baseline_calendar_hours_per_day_and_more.py +31 -0
- trueppm_api/apps/projects/migrations/0143_sprintcloserequest_failure_reason_and_more.py +42 -0
- trueppm_api/apps/projects/migrations/0144_projecttemplate_source_project_and_more.py +35 -0
- trueppm_api/apps/projects/migrations/0145_seed_bundled_templates.py +65 -0
- trueppm_api/apps/projects/migrations/0146_historicaltask_board_lane_task_board_lane.py +32 -0
- trueppm_api/apps/projects/migrations/0147_structuraloperation.py +128 -0
- trueppm_api/apps/projects/migrations/0148_task_unique_task_wbs_path_per_project_live.py +76 -0
- trueppm_api/apps/projects/migrations/0149_drop_workshops_tables.py +42 -0
- trueppm_api/apps/projects/migrations/0150_clear_uncommitted_cpm_output.py +130 -0
- trueppm_api/apps/projects/migrations/0151_config_notice_request.py +70 -0
- trueppm_api/apps/projects/migrations/0152_aggregate_task_wbs_name_idx.py +20 -0
- trueppm_api/apps/projects/migrations/0153_program_sample_anchor_date.py +22 -0
- trueppm_api/apps/projects/migrations/0154_object_keys.py +184 -0
- trueppm_api/apps/projects/migrations/0155_task_milestone_at_day_end.py +20 -0
- trueppm_api/apps/projects/migrations/0156_program_import_job_replaced_project_ids.py +17 -0
- trueppm_api/apps/projects/migrations/0157_forecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/0158_baselinetask_finish_at_day_start.py +17 -0
- trueppm_api/apps/projects/migrations/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +9209 -0
- trueppm_api/apps/projects/poker_services.py +154 -0
- trueppm_api/apps/projects/poker_views.py +398 -0
- trueppm_api/apps/projects/product_backlog_services.py +450 -0
- trueppm_api/apps/projects/program_label_services.py +94 -0
- trueppm_api/apps/projects/program_label_views.py +189 -0
- trueppm_api/apps/projects/program_rollup.py +903 -0
- trueppm_api/apps/projects/program_schedule.py +767 -0
- trueppm_api/apps/projects/program_views.py +3445 -0
- trueppm_api/apps/projects/project_templates.py +422 -0
- trueppm_api/apps/projects/receivers.py +234 -0
- trueppm_api/apps/projects/refusal_codes.py +140 -0
- trueppm_api/apps/projects/reorder_services.py +145 -0
- trueppm_api/apps/projects/restructure_hooks.py +54 -0
- trueppm_api/apps/projects/retro_board_services.py +368 -0
- trueppm_api/apps/projects/retro_services.py +285 -0
- trueppm_api/apps/projects/risk_import.py +396 -0
- trueppm_api/apps/projects/row_identity.py +113 -0
- trueppm_api/apps/projects/schema_migrations.py +228 -0
- trueppm_api/apps/projects/schemas/seed_v1.json +492 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +891 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/demo_landing_overlay.py +532 -0
- trueppm_api/apps/projects/seed/exporter.py +1717 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2764 -0
- trueppm_api/apps/projects/seed/reanchor.py +551 -0
- trueppm_api/apps/projects/seed/reldates.py +126 -0
- trueppm_api/apps/projects/seed/replace.py +128 -0
- trueppm_api/apps/projects/seed/replay.py +1695 -0
- trueppm_api/apps/projects/seed/replay_ctx.py +41 -0
- trueppm_api/apps/projects/seed/samples.py +427 -0
- trueppm_api/apps/projects/seed/validation.py +1169 -0
- trueppm_api/apps/projects/serializers.py +12904 -0
- trueppm_api/apps/projects/services.py +7525 -0
- trueppm_api/apps/projects/share_serializers.py +142 -0
- trueppm_api/apps/projects/share_services.py +373 -0
- trueppm_api/apps/projects/share_views.py +395 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +910 -0
- trueppm_api/apps/projects/signal_privacy_views.py +485 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +288 -0
- trueppm_api/apps/projects/sprint_cadence.py +453 -0
- trueppm_api/apps/projects/sprint_picker_settings.py +132 -0
- trueppm_api/apps/projects/standup.py +304 -0
- trueppm_api/apps/projects/standup_views.py +99 -0
- trueppm_api/apps/projects/structural_operation_services.py +727 -0
- trueppm_api/apps/projects/structural_operation_views.py +311 -0
- trueppm_api/apps/projects/surface_visibility.py +126 -0
- trueppm_api/apps/projects/task_batch_services.py +379 -0
- trueppm_api/apps/projects/task_bulk.py +1244 -0
- trueppm_api/apps/projects/task_classification.py +561 -0
- trueppm_api/apps/projects/task_classification_defaults.py +95 -0
- trueppm_api/apps/projects/task_duration_settings.py +142 -0
- trueppm_api/apps/projects/task_grouping.py +754 -0
- trueppm_api/apps/projects/tasks.py +1979 -0
- trueppm_api/apps/projects/template_divergence.py +168 -0
- trueppm_api/apps/projects/template_services.py +160 -0
- trueppm_api/apps/projects/template_tasks.py +261 -0
- trueppm_api/apps/projects/template_views.py +829 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1016 -0
- trueppm_api/apps/projects/utilization.py +909 -0
- trueppm_api/apps/projects/views.py +21225 -0
- trueppm_api/apps/projects/wbs_paths.py +119 -0
- trueppm_api/apps/resources/__init__.py +0 -0
- trueppm_api/apps/resources/apps.py +8 -0
- trueppm_api/apps/resources/capacity.py +121 -0
- trueppm_api/apps/resources/migrations/0001_initial.py +68 -0
- trueppm_api/apps/resources/migrations/0001_squashed_0013_alter_projectresource_unique_together_and_more.py +281 -0
- trueppm_api/apps/resources/migrations/0002_alter_uuid_pk_serialize_false.py +36 -0
- trueppm_api/apps/resources/migrations/0003_resource_soft_delete.py +30 -0
- trueppm_api/apps/resources/migrations/0004_add_taskresource_resource_index.py +25 -0
- trueppm_api/apps/resources/migrations/0005_resource_job_role.py +20 -0
- trueppm_api/apps/resources/migrations/0006_skill.py +34 -0
- trueppm_api/apps/resources/migrations/0007_resource_skill.py +63 -0
- trueppm_api/apps/resources/migrations/0008_project_resource.py +63 -0
- trueppm_api/apps/resources/migrations/0009_task_skill_requirement.py +60 -0
- trueppm_api/apps/resources/migrations/0010_alter_resourceskill_options_and_more.py +20 -0
- trueppm_api/apps/resources/migrations/0011_alter_projectresource_notes.py +17 -0
- trueppm_api/apps/resources/migrations/0012_add_resource_user_fk.py +26 -0
- trueppm_api/apps/resources/migrations/0013_alter_projectresource_unique_together_and_more.py +53 -0
- trueppm_api/apps/resources/migrations/0014_projectresource_sync_seq_resource_sync_seq_and_more.py +37 -0
- trueppm_api/apps/resources/migrations/0014_squashed_0015_release_0_4.py +47 -0
- trueppm_api/apps/resources/migrations/0015_projectresource_deactivated_with_resource.py +17 -0
- trueppm_api/apps/resources/migrations/__init__.py +0 -0
- trueppm_api/apps/resources/models.py +298 -0
- trueppm_api/apps/resources/serializers.py +426 -0
- trueppm_api/apps/resources/services.py +465 -0
- trueppm_api/apps/resources/urls.py +28 -0
- trueppm_api/apps/resources/views.py +1752 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +144 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -0
- trueppm_api/apps/scheduling/finish_reading.py +198 -0
- trueppm_api/apps/scheduling/forecast_history_settings.py +215 -0
- trueppm_api/apps/scheduling/forecast_staleness.py +313 -0
- trueppm_api/apps/scheduling/graph_guard.py +263 -0
- trueppm_api/apps/scheduling/management/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/__init__.py +0 -0
- trueppm_api/apps/scheduling/management/commands/prune_forecast_snapshots.py +53 -0
- trueppm_api/apps/scheduling/mc_admission.py +181 -0
- trueppm_api/apps/scheduling/migrations/0001_failed_task.py +62 -0
- trueppm_api/apps/scheduling/migrations/0001_squashed_0007_projectforecastsnapshot.py +309 -0
- trueppm_api/apps/scheduling/migrations/0002_schedulerequest.py +78 -0
- trueppm_api/apps/scheduling/migrations/0003_add_schedule_request_reason.py +27 -0
- trueppm_api/apps/scheduling/migrations/0004_velocity_suggestion.py +94 -0
- trueppm_api/apps/scheduling/migrations/0005_montecarlorun.py +61 -0
- trueppm_api/apps/scheduling/migrations/0006_montecarlorun_distribution.py +17 -0
- trueppm_api/apps/scheduling/migrations/0007_projectforecastsnapshot.py +65 -0
- trueppm_api/apps/scheduling/migrations/0008_alter_schedulerequest_reason.py +28 -0
- trueppm_api/apps/scheduling/migrations/0008_folded_schedulerequest_reason.py +34 -0
- trueppm_api/apps/scheduling/migrations/0009_alter_schedulerequest_reason.py +29 -0
- trueppm_api/apps/scheduling/migrations/0010_failedtask_resolution_note_failedtask_resolved_at_and_more.py +43 -0
- trueppm_api/apps/scheduling/migrations/0010_squashed_0016_release_0_4.py +106 -0
- trueppm_api/apps/scheduling/migrations/0011_failedtask_project.py +26 -0
- trueppm_api/apps/scheduling/migrations/0012_montecarlorun_diagnostic.py +17 -0
- trueppm_api/apps/scheduling/migrations/0013_montecarlorun_status_date.py +17 -0
- trueppm_api/apps/scheduling/migrations/0014_montecarlorun_plan_version.py +17 -0
- trueppm_api/apps/scheduling/migrations/0015_projectforecastsnapshot_cpm_finish_at_day_start.py +17 -0
- trueppm_api/apps/scheduling/migrations/0016_montecarlorun_finish_readings.py +32 -0
- trueppm_api/apps/scheduling/migrations/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +542 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +334 -0
- trueppm_api/apps/scheduling/serializers.py +723 -0
- trueppm_api/apps/scheduling/services.py +1251 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +2248 -0
- trueppm_api/apps/scheduling/telemetry.py +80 -0
- trueppm_api/apps/scheduling/units.py +35 -0
- trueppm_api/apps/scheduling/urls.py +57 -0
- trueppm_api/apps/scheduling/views.py +2908 -0
- trueppm_api/apps/sso/__init__.py +0 -0
- trueppm_api/apps/sso/apps.py +19 -0
- trueppm_api/apps/sso/extensions.py +130 -0
- trueppm_api/apps/sso/management/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/__init__.py +0 -0
- trueppm_api/apps/sso/management/commands/seed_sso_keycloak.py +172 -0
- trueppm_api/apps/sso/migrations/0001_initial.py +119 -0
- trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py +169 -0
- trueppm_api/apps/sso/migrations/__init__.py +0 -0
- trueppm_api/apps/sso/models.py +148 -0
- trueppm_api/apps/sso/serializers.py +446 -0
- trueppm_api/apps/sso/services.py +1473 -0
- trueppm_api/apps/sso/urls.py +47 -0
- trueppm_api/apps/sso/views.py +935 -0
- trueppm_api/apps/sync/__init__.py +0 -0
- trueppm_api/apps/sync/apps.py +15 -0
- trueppm_api/apps/sync/backfill.py +208 -0
- trueppm_api/apps/sync/broadcast.py +369 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +548 -0
- trueppm_api/apps/sync/migrations/0001_initial.py +59 -0
- trueppm_api/apps/sync/migrations/0001_squashed_0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +77 -0
- trueppm_api/apps/sync/migrations/0002_remove_syncbatch_syncbatch_project_client_batch_uniq_and_more.py +41 -0
- trueppm_api/apps/sync/migrations/0003_boardevent.py +46 -0
- trueppm_api/apps/sync/migrations/0003_squashed_0005_release_0_4.py +77 -0
- trueppm_api/apps/sync/migrations/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/0006_boardevent_per_project_seq.py +77 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +272 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +488 -0
- trueppm_api/apps/sync/serializers.py +688 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +556 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +957 -0
- trueppm_api/apps/sync/ws_auth.py +193 -0
- trueppm_api/apps/taskruns/__init__.py +0 -0
- trueppm_api/apps/taskruns/apps.py +11 -0
- trueppm_api/apps/taskruns/migrations/0001_initial.py +92 -0
- trueppm_api/apps/taskruns/migrations/__init__.py +0 -0
- trueppm_api/apps/taskruns/models.py +68 -0
- trueppm_api/apps/taskruns/serializers.py +92 -0
- trueppm_api/apps/taskruns/tasks.py +60 -0
- trueppm_api/apps/taskruns/tracker.py +286 -0
- trueppm_api/apps/taskruns/urls.py +46 -0
- trueppm_api/apps/taskruns/views.py +195 -0
- trueppm_api/apps/teams/__init__.py +8 -0
- trueppm_api/apps/teams/apps.py +16 -0
- trueppm_api/apps/teams/migrations/0001_initial.py +139 -0
- trueppm_api/apps/teams/migrations/0001_squashed_0002_default_teams.py +159 -0
- trueppm_api/apps/teams/migrations/0002_default_teams.py +62 -0
- trueppm_api/apps/teams/migrations/0003_team_sync_seq_teammembership_sync_seq.py +22 -0
- trueppm_api/apps/teams/migrations/0004_backfill_default_teams_on_upgrade.py +49 -0
- trueppm_api/apps/teams/migrations/__init__.py +0 -0
- trueppm_api/apps/teams/models.py +145 -0
- trueppm_api/apps/teams/permissions.py +149 -0
- trueppm_api/apps/teams/serializers.py +95 -0
- trueppm_api/apps/teams/services.py +440 -0
- trueppm_api/apps/teams/signals.py +61 -0
- trueppm_api/apps/teams/urls.py +34 -0
- trueppm_api/apps/teams/views.py +201 -0
- trueppm_api/apps/timetracking/__init__.py +0 -0
- trueppm_api/apps/timetracking/apps.py +9 -0
- trueppm_api/apps/timetracking/migrations/0001_initial.py +117 -0
- trueppm_api/apps/timetracking/migrations/0001_squashed_0004_release_0_4.py +167 -0
- trueppm_api/apps/timetracking/migrations/0002_timesheetsubmission.py +46 -0
- trueppm_api/apps/timetracking/migrations/0003_timeentry_deleted_at_timeentry_deleted_by.py +31 -0
- trueppm_api/apps/timetracking/migrations/0004_timeentry_sync_seq_and_more.py +24 -0
- trueppm_api/apps/timetracking/migrations/__init__.py +0 -0
- trueppm_api/apps/timetracking/models.py +178 -0
- trueppm_api/apps/timetracking/serializers.py +175 -0
- trueppm_api/apps/timetracking/services.py +211 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +597 -0
- trueppm_api/apps/webhooks/__init__.py +0 -0
- trueppm_api/apps/webhooks/apps.py +8 -0
- trueppm_api/apps/webhooks/backfill.py +90 -0
- trueppm_api/apps/webhooks/dispatch.py +279 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +249 -0
- trueppm_api/apps/webhooks/migrations/0002_alter_webhook_events.py +32 -0
- trueppm_api/apps/webhooks/migrations/0003_delivery_status_created_idx.py +17 -0
- trueppm_api/apps/webhooks/migrations/0004_webhook_program_alter_webhook_project_and_more.py +64 -0
- trueppm_api/apps/webhooks/migrations/0005_webhook_delivery_sequence_numbers.py +30 -0
- trueppm_api/apps/webhooks/migrations/0006_webhook_format_alter_webhook_events_and_more.py +61 -0
- trueppm_api/apps/webhooks/migrations/0007_sprint_lifecycle_events.py +63 -0
- trueppm_api/apps/webhooks/migrations/0008_risk_baseline_comment_events.py +73 -0
- trueppm_api/apps/webhooks/migrations/0009_encrypt_webhook_secret_and_failure_counters.py +100 -0
- trueppm_api/apps/webhooks/migrations/0010_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/0011_webhookeditor.py +48 -0
- trueppm_api/apps/webhooks/migrations/0012_alter_webhook_disabled_at.py +21 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +437 -0
- trueppm_api/apps/webhooks/serializers.py +326 -0
- trueppm_api/apps/webhooks/tasks.py +680 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +533 -0
- trueppm_api/apps/workflow_engine/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/apps.py +23 -0
- trueppm_api/apps/workflow_engine/migrations/0001_initial.py +230 -0
- trueppm_api/apps/workflow_engine/migrations/0001_squashed_0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +234 -0
- trueppm_api/apps/workflow_engine/migrations/0002_remove_workflowhistoryevent_workflow_history_lookup_idx.py +16 -0
- trueppm_api/apps/workflow_engine/migrations/0003_workflowoutboxrow_workflow_outbox_recovery_idx.py +21 -0
- trueppm_api/apps/workflow_engine/migrations/__init__.py +0 -0
- trueppm_api/apps/workflow_engine/models.py +265 -0
- trueppm_api/apps/workflow_engine/tasks.py +371 -0
- trueppm_api/apps/workspace/__init__.py +0 -0
- trueppm_api/apps/workspace/apps.py +10 -0
- trueppm_api/apps/workspace/backfill.py +111 -0
- trueppm_api/apps/workspace/export.py +254 -0
- trueppm_api/apps/workspace/migrations/0001_initial.py +326 -0
- trueppm_api/apps/workspace/migrations/0001_squashed_0014_workspacemembership_availability_effective_from_and_more.py +829 -0
- trueppm_api/apps/workspace/migrations/0002_remove_workspace_fiscal_year_start_and_more.py +85 -0
- trueppm_api/apps/workspace/migrations/0003_workspaceexportjob.py +73 -0
- trueppm_api/apps/workspace/migrations/0004_alter_workspaceinvite_status.py +27 -0
- trueppm_api/apps/workspace/migrations/0005_workspace_iteration_label_and_more.py +26 -0
- trueppm_api/apps/workspace/migrations/0006_alter_groupmembership_unique_together_and_more.py +45 -0
- trueppm_api/apps/workspace/migrations/0007_workspace_public_sharing_override_policy.py +21 -0
- trueppm_api/apps/workspace/migrations/0008_workspace_mc_history_attribution_audience_and_more.py +44 -0
- trueppm_api/apps/workspace/migrations/0009_workspace_methodology_and_more.py +186 -0
- trueppm_api/apps/workspace/migrations/0010_workspace_logo.py +38 -0
- trueppm_api/apps/workspace/migrations/0011_historicalworkspace_task_duration_change_percent_override_policy_and_more.py +56 -0
- trueppm_api/apps/workspace/migrations/0012_historicalworkspace_allowed_attachment_types_and_more.py +64 -0
- trueppm_api/apps/workspace/migrations/0013_auditevent.py +70 -0
- trueppm_api/apps/workspace/migrations/0014_workspacemembership_availability_effective_from_and_more.py +39 -0
- trueppm_api/apps/workspace/migrations/0015_squashed_0027_release_0_4.py +199 -0
- trueppm_api/apps/workspace/migrations/0015_trash_restore_1113.py +34 -0
- trueppm_api/apps/workspace/migrations/0016_historicalworkspace_calendar_and_more.py +55 -0
- trueppm_api/apps/workspace/migrations/0017_historicalworkspace_estimation_scale_and_more.py +38 -0
- trueppm_api/apps/workspace/migrations/0018_historicalworkspace_feedback_enabled_and_more.py +32 -0
- trueppm_api/apps/workspace/migrations/0019_historicalworkspace_mcp_enabled_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0020_viewer_ordinal_one.py +34 -0
- trueppm_api/apps/workspace/migrations/0021_group_sync_seq_groupmembership_sync_seq_and_more.py +27 -0
- trueppm_api/apps/workspace/migrations/0022_historicalworkspace_sprint_picker_ready_only_default_and_more.py +22 -0
- trueppm_api/apps/workspace/migrations/0023_alter_auditevent_event_type.py +32 -0
- trueppm_api/apps/workspace/migrations/0024_alter_auditevent_event_type.py +33 -0
- trueppm_api/apps/workspace/migrations/0025_alter_auditevent_event_type.py +36 -0
- trueppm_api/apps/workspace/migrations/0026_alter_auditevent_event_type.py +37 -0
- trueppm_api/apps/workspace/migrations/0027_alter_auditevent_event_type.py +42 -0
- trueppm_api/apps/workspace/migrations/__init__.py +0 -0
- trueppm_api/apps/workspace/models.py +857 -0
- trueppm_api/apps/workspace/permissions.py +187 -0
- trueppm_api/apps/workspace/serializers.py +590 -0
- trueppm_api/apps/workspace/services.py +967 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +616 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1581 -0
- trueppm_api/asgi.py +49 -0
- trueppm_api/celery.py +40 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +934 -0
- trueppm_api/core/constant_time.py +51 -0
- trueppm_api/core/csp.py +54 -0
- trueppm_api/core/db.py +87 -0
- trueppm_api/core/db_session.py +37 -0
- trueppm_api/core/demo_read_only.py +314 -0
- trueppm_api/core/exception_handlers.py +157 -0
- trueppm_api/core/export_downloads.py +56 -0
- trueppm_api/core/extension_providers.py +56 -0
- trueppm_api/core/extension_signals.py +147 -0
- trueppm_api/core/idempotent.py +260 -0
- trueppm_api/core/metadata.py +42 -0
- trueppm_api/core/middleware.py +51 -0
- trueppm_api/core/openapi.py +1221 -0
- trueppm_api/core/password_reset.py +513 -0
- trueppm_api/core/protect_conflict.py +100 -0
- trueppm_api/core/ratelimit.py +148 -0
- trueppm_api/core/redis_throttle.py +82 -0
- trueppm_api/core/refresh_cookie_policy.py +156 -0
- trueppm_api/core/request_body.py +86 -0
- trueppm_api/core/security_checks.py +1034 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +514 -0
- trueppm_api/core/valkey.py +214 -0
- trueppm_api/core/valkey_cache.py +66 -0
- trueppm_api/core/valkey_checks.py +104 -0
- trueppm_api/core/valkey_config.py +39 -0
- trueppm_api/core/worker_broker_wait.py +149 -0
- trueppm_api/core/worker_heartbeat.py +134 -0
- trueppm_api/core/worker_selfheal.py +205 -0
- trueppm_api/fields.py +36 -0
- trueppm_api/permissions.py +30 -0
- trueppm_api/py.typed +0 -0
- trueppm_api/routing.py +11 -0
- trueppm_api/settings/__init__.py +0 -0
- trueppm_api/settings/base.py +2605 -0
- trueppm_api/settings/dev.py +296 -0
- trueppm_api/settings/prod.py +265 -0
- trueppm_api/urls.py +217 -0
- trueppm_api/workflows/__init__.py +38 -0
- trueppm_api/workflows/backends/__init__.py +6 -0
- trueppm_api/workflows/backends/default.py +297 -0
- trueppm_api/workflows/consumers/__init__.py +11 -0
- trueppm_api/workflows/consumers/requeue_failed_task.py +65 -0
- trueppm_api/workflows/interface.py +148 -0
- trueppm_api/workflows/registry.py +106 -0
- trueppm_api/workflows/services.py +90 -0
- trueppm_api/wsgi.py +11 -0
- trueppm_api-0.4.0.dist-info/METADATA +273 -0
- trueppm_api-0.4.0.dist-info/RECORD +799 -0
- trueppm_api-0.4.0.dist-info/WHEEL +4 -0
- trueppm_api-0.4.0.dist-info/licenses/LICENSE +201 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICE +7 -0
- trueppm_api-0.4.0.dist-info/licenses/NOTICES +104 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/GPL-3.0.txt +674 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/LGPL-3.0.txt +165 -0
- trueppm_api-0.4.0.dist-info/licenses/licenses/MPL-2.0.txt +373 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
"""``manage.py revoke_api_tokens`` — the operator's API-token breach-recovery lever (#2878).
|
|
2
|
+
|
|
3
|
+
**Why this exists.** ``administration/security.md`` told operators that rotating the JWT
|
|
4
|
+
signing key after a suspected token leak or an admin off-boarding signs everyone out. It
|
|
5
|
+
does — for sessions and JWTs. An API token is resolved by a SHA-256 hash lookup
|
|
6
|
+
(``apps/projects/authentication.py``), carries no signature, and is completely untouched
|
|
7
|
+
by key rotation: after following the documented procedure to the letter, every leaked
|
|
8
|
+
token on the instance is still live, and nothing in the runbook said so. This command is
|
|
9
|
+
the missing half of that procedure.
|
|
10
|
+
|
|
11
|
+
**Scope is a required choice, not a default.** Exactly one of:
|
|
12
|
+
|
|
13
|
+
* ``--user <username-or-email>`` — every *personal* token owned by one account,
|
|
14
|
+
**plus** (#4576) that account's refresh tokens, share links, git-automation
|
|
15
|
+
webhook secret, outgoing webhooks, and live project WebSocket connections. The
|
|
16
|
+
off-boarding / "this person's laptop was stolen" case — ``administration/
|
|
17
|
+
management-commands.md`` documents this as the lever an operator runs for
|
|
18
|
+
exactly that incident, so it must contain everything the account could still
|
|
19
|
+
reach, not tokens alone. Named by account, regardless of whether that account
|
|
20
|
+
currently owns any live personal token.
|
|
21
|
+
* ``--all-personal`` — every personal token on the instance, plus (#4576) the
|
|
22
|
+
same refresh-token / durable-grant / live-socket sweep for each account that
|
|
23
|
+
owns one of the matched tokens. The "our secret store leaked" case. Leaves
|
|
24
|
+
project- and program-scoped integration tokens alone, so team CI keeps
|
|
25
|
+
working, and leaves alone any account that owns durable grants but no live
|
|
26
|
+
personal token — ``--user`` is the lever for containing that account by name.
|
|
27
|
+
* ``--all`` — every token of every kind, including integration tokens, **plus**
|
|
28
|
+
every active share link, configured git-automation webhook secret, and
|
|
29
|
+
registered outgoing webhook instance-wide (#2939, #4461) — the other three
|
|
30
|
+
durable grants a leaked token or compromised session can mint that no other
|
|
31
|
+
lever reaches. This *will* break every inbound sync, public share link, and
|
|
32
|
+
outgoing webhook delivery until an admin re-mints/re-enables; it is the
|
|
33
|
+
full-compromise lever.
|
|
34
|
+
|
|
35
|
+
**Dry-run by default.** The command reports what it would revoke and exits; ``--commit``
|
|
36
|
+
performs the revocation, prompting for confirmation unless ``--yes`` is given. Revocation
|
|
37
|
+
is one-way — nothing in the codebase clears ``revoked_at`` — so a mistyped scope cannot
|
|
38
|
+
be undone, which is exactly why it does not act without being told twice.
|
|
39
|
+
|
|
40
|
+
Every revoked token gets an ``ApiTokenAuditEntry`` row with
|
|
41
|
+
``detail["reason"] = "operator_bulk_revoke"``, so the incident timeline can distinguish
|
|
42
|
+
an operator's containment sweep from a user's routine rotation. ``actor`` is null: a
|
|
43
|
+
command run from a shell has no Django user, and inventing one would be worse than
|
|
44
|
+
recording the absence.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from __future__ import annotations
|
|
48
|
+
|
|
49
|
+
from typing import Any
|
|
50
|
+
|
|
51
|
+
from django.contrib.auth import get_user_model
|
|
52
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
53
|
+
from django.db import transaction
|
|
54
|
+
from django.db.models import Q
|
|
55
|
+
from django.utils import timezone
|
|
56
|
+
|
|
57
|
+
REASON = "operator_bulk_revoke"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Command(BaseCommand):
|
|
61
|
+
help = "Revoke API tokens instance-wide or for one account (breach recovery, #2878)."
|
|
62
|
+
|
|
63
|
+
def add_arguments(self, parser: Any) -> None:
|
|
64
|
+
scope = parser.add_mutually_exclusive_group(required=True)
|
|
65
|
+
scope.add_argument(
|
|
66
|
+
"--user",
|
|
67
|
+
type=str,
|
|
68
|
+
help="Revoke this account's personal access tokens, refresh tokens, share "
|
|
69
|
+
"links, git-automation secret, outgoing webhooks, and live project "
|
|
70
|
+
"sockets. Accepts username or email.",
|
|
71
|
+
)
|
|
72
|
+
scope.add_argument(
|
|
73
|
+
"--all-personal",
|
|
74
|
+
action="store_true",
|
|
75
|
+
help="Revoke every personal access token on the instance, plus the refresh "
|
|
76
|
+
"tokens, share links, git-automation secrets, outgoing webhooks, and "
|
|
77
|
+
"live project sockets of each account that owns one. Leaves project- "
|
|
78
|
+
"and program-scoped integration tokens, and accounts with no personal "
|
|
79
|
+
"token, alone.",
|
|
80
|
+
)
|
|
81
|
+
scope.add_argument(
|
|
82
|
+
"--all",
|
|
83
|
+
action="store_true",
|
|
84
|
+
help="Revoke EVERY API token, including project- and program-scoped "
|
|
85
|
+
"integration tokens. Breaks all inbound sync until re-minted.",
|
|
86
|
+
)
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--commit",
|
|
89
|
+
action="store_true",
|
|
90
|
+
help="Actually revoke. Without this the command only reports.",
|
|
91
|
+
)
|
|
92
|
+
parser.add_argument(
|
|
93
|
+
"--yes",
|
|
94
|
+
action="store_true",
|
|
95
|
+
help="Skip the interactive confirmation (for use in a runbook script).",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def _resolve_target_user(self, options: dict[str, Any]) -> Any | None:
|
|
99
|
+
"""The single account ``--user`` names, or None if ``--user`` was not given.
|
|
100
|
+
|
|
101
|
+
Accepts either username or email so a runbook does not have to know which
|
|
102
|
+
the operator has to hand during an incident.
|
|
103
|
+
|
|
104
|
+
FAILS CLOSED on ambiguity. Django's stock ``auth.User`` puts **no**
|
|
105
|
+
uniqueness constraint on ``email`` at all, and ``username`` uniqueness is
|
|
106
|
+
case-*sensitive*, so ``__iexact`` can match several rows two ways. A
|
|
107
|
+
``.first()`` here would silently pick the lowest pk — during an incident,
|
|
108
|
+
while reporting success — so the leaked token stays live and an uninvolved
|
|
109
|
+
account's automations break instead. ``apps/sso/services.py`` and
|
|
110
|
+
``core/password_reset.py`` already treat this lookup as hazardous; this is
|
|
111
|
+
the third caller and it must not be the one that guesses.
|
|
112
|
+
"""
|
|
113
|
+
if not options["user"]:
|
|
114
|
+
return None
|
|
115
|
+
User = get_user_model()
|
|
116
|
+
identifier = str(options["user"]).strip()
|
|
117
|
+
candidates = list(
|
|
118
|
+
User.objects.filter(
|
|
119
|
+
Q(username__iexact=identifier) | Q(email__iexact=identifier)
|
|
120
|
+
).order_by("pk")[:3]
|
|
121
|
+
)
|
|
122
|
+
if not candidates:
|
|
123
|
+
raise CommandError(f"No account matches {identifier!r} by username or email.")
|
|
124
|
+
if len(candidates) > 1:
|
|
125
|
+
listed = ", ".join(f"pk={u.pk} username={u.username!r}" for u in candidates)
|
|
126
|
+
raise CommandError(
|
|
127
|
+
f"{identifier!r} matches more than one account ({listed}). Re-run with "
|
|
128
|
+
"an unambiguous username so the sweep cannot contain the wrong "
|
|
129
|
+
"account."
|
|
130
|
+
)
|
|
131
|
+
return candidates[0]
|
|
132
|
+
|
|
133
|
+
def _resolve_scope(self, options: dict[str, Any], target_user: Any | None) -> tuple[Any, str]:
|
|
134
|
+
from trueppm_api.apps.projects.models import ApiToken
|
|
135
|
+
|
|
136
|
+
active = ApiToken.objects.filter(is_deleted=False, revoked_at__isnull=True)
|
|
137
|
+
if target_user is not None:
|
|
138
|
+
label = f"personal tokens owned by {target_user.username}"
|
|
139
|
+
return active.filter(owner=target_user), label
|
|
140
|
+
if options["all_personal"]:
|
|
141
|
+
return active.filter(owner__isnull=False), "all personal access tokens on this instance"
|
|
142
|
+
return active, "ALL API tokens on this instance (including integration tokens)"
|
|
143
|
+
|
|
144
|
+
def _report_matched_tokens(self, doomed: list[Any], scope_label: str) -> None:
|
|
145
|
+
self.stdout.write(f"Scope: {scope_label}")
|
|
146
|
+
self.stdout.write(f"Active tokens matched: {len(doomed)}")
|
|
147
|
+
for token in doomed:
|
|
148
|
+
kind = "personal" if token.owner_id else ("project" if token.project_id else "program")
|
|
149
|
+
self.stdout.write(f" {token.token_prefix}… ({kind}) {token.name}")
|
|
150
|
+
|
|
151
|
+
def _grant_counts_for(self, user: Any | None) -> tuple[int, int, int]:
|
|
152
|
+
"""(share links, git-automation secrets, outgoing webhooks) matching ``user``.
|
|
153
|
+
|
|
154
|
+
Read-only mirror of ``revoke_personal_durable_grants``'s own filters (#2939,
|
|
155
|
+
#4461, #4576) — kept as a second copy rather than a shared helper because the
|
|
156
|
+
service function performs the ``update()``, and a dry run must not touch a
|
|
157
|
+
row it is only reporting on. ``user=None`` is the instance-wide population
|
|
158
|
+
``--all`` sweeps; a concrete user is one account's personal grants, used by
|
|
159
|
+
``--user``/``--all-personal``.
|
|
160
|
+
"""
|
|
161
|
+
from trueppm_api.apps.integrations.models import BoardAutomation
|
|
162
|
+
from trueppm_api.apps.projects.models import ShareLink
|
|
163
|
+
from trueppm_api.apps.webhooks.models import Webhook
|
|
164
|
+
|
|
165
|
+
links = ShareLink.objects.filter(revoked_at__isnull=True)
|
|
166
|
+
secrets_qs = BoardAutomation.objects.exclude(secret_ciphertext=b"")
|
|
167
|
+
webhooks_qs = Webhook.objects.filter(is_active=True)
|
|
168
|
+
if user is not None:
|
|
169
|
+
links = links.filter(created_by=user)
|
|
170
|
+
secrets_qs = secrets_qs.filter(configured_by=user)
|
|
171
|
+
webhooks_qs = webhooks_qs.filter(Q(created_by=user) | Q(editors__user=user))
|
|
172
|
+
return links.count(), secrets_qs.count(), webhooks_qs.count()
|
|
173
|
+
|
|
174
|
+
def _count_pending_invites(self, options: dict[str, Any], affected_users: list[Any]) -> int:
|
|
175
|
+
"""Read-only mirror of ``revoke_pending_workspace_invites``'s filter (#4589).
|
|
176
|
+
|
|
177
|
+
``--all`` counts every pending/failed invite instance-wide; ``--user`` and
|
|
178
|
+
``--all-personal`` count those minted by each affected account.
|
|
179
|
+
"""
|
|
180
|
+
from trueppm_api.apps.workspace.models import InviteStatus, WorkspaceInvite
|
|
181
|
+
|
|
182
|
+
live = WorkspaceInvite.objects.filter(
|
|
183
|
+
status__in=(InviteStatus.PENDING, InviteStatus.FAILED)
|
|
184
|
+
)
|
|
185
|
+
if options["all"]:
|
|
186
|
+
total = live.count()
|
|
187
|
+
elif affected_users:
|
|
188
|
+
total = live.filter(invited_by__in=affected_users).count()
|
|
189
|
+
else:
|
|
190
|
+
return 0
|
|
191
|
+
self.stdout.write(f"Pending workspace invites matched: {total}")
|
|
192
|
+
return total
|
|
193
|
+
|
|
194
|
+
def _count_outstanding_refresh_tokens(self, user: Any) -> int:
|
|
195
|
+
"""Read-only mirror of ``revoke_all_refresh_tokens``'s filter, for dry-run counts."""
|
|
196
|
+
try:
|
|
197
|
+
from rest_framework_simplejwt.token_blacklist.models import OutstandingToken
|
|
198
|
+
except ImportError: # pragma: no cover - only when the app is uninstalled
|
|
199
|
+
return 0
|
|
200
|
+
return OutstandingToken.objects.filter(user=user, blacklistedtoken__isnull=True).count()
|
|
201
|
+
|
|
202
|
+
def _count_live_project_memberships(self, user_id: Any) -> int:
|
|
203
|
+
"""Read-only mirror of ``evict_all_live_project_sockets``'s filter.
|
|
204
|
+
|
|
205
|
+
Counts projects the account holds a *live* membership in, i.e. the number
|
|
206
|
+
of eviction broadcasts that call would schedule — not a live socket count,
|
|
207
|
+
which the command has no way to observe.
|
|
208
|
+
"""
|
|
209
|
+
from trueppm_api.apps.access.models import ProjectMembership
|
|
210
|
+
|
|
211
|
+
return ProjectMembership.objects.filter(user_id=user_id, is_deleted=False).count()
|
|
212
|
+
|
|
213
|
+
def _count_durable_grants(self, options: dict[str, Any]) -> tuple[int, int, int]:
|
|
214
|
+
"""(links_matched, secrets_matched, webhooks_matched) for ``--all``'s report.
|
|
215
|
+
|
|
216
|
+
--all is the full-compromise lever: also sweep the three non-token durable
|
|
217
|
+
grants a leaked token (or a compromised session) can mint (#2939, #4461).
|
|
218
|
+
Counted ahead of --commit so a dry run reports the whole blast radius, not
|
|
219
|
+
just the token count.
|
|
220
|
+
"""
|
|
221
|
+
if not options["all"]:
|
|
222
|
+
return 0, 0, 0
|
|
223
|
+
links_matched, secrets_matched, webhooks_matched = self._grant_counts_for(None)
|
|
224
|
+
self.stdout.write(f"Active share links matched: {links_matched}")
|
|
225
|
+
self.stdout.write(f"Configured git-automation secrets matched: {secrets_matched}")
|
|
226
|
+
self.stdout.write(f"Active outgoing webhooks matched: {webhooks_matched}")
|
|
227
|
+
return links_matched, secrets_matched, webhooks_matched
|
|
228
|
+
|
|
229
|
+
def _resolve_affected_users(
|
|
230
|
+
self, options: dict[str, Any], target_user: Any | None, doomed: list[Any]
|
|
231
|
+
) -> list[Any]:
|
|
232
|
+
"""The account(s) this run also contains beyond their API tokens (#4576).
|
|
233
|
+
|
|
234
|
+
``--user`` always sweeps the one named account, whether or not it currently
|
|
235
|
+
owns any live personal token — the operator is containing an *account* (the
|
|
236
|
+
documented off-boarding / stolen-laptop lever), not a token population, and
|
|
237
|
+
password reset (``core/password_reset.py``) and workspace off-boarding
|
|
238
|
+
(``apps/workspace/views.py:_revoke_offboarded_credentials``) already sweep
|
|
239
|
+
on that same basis.
|
|
240
|
+
|
|
241
|
+
``--all-personal`` has no independent "every user" population to draw on;
|
|
242
|
+
its own scope is "every personal access token on the instance" (see the
|
|
243
|
+
module docstring), so the accounts it sweeps for the other grant classes
|
|
244
|
+
are exactly the owners of the personal tokens it is about to revoke. An
|
|
245
|
+
account that owns durable grants but no live personal token sits outside
|
|
246
|
+
``--all-personal``'s stated scope — ``--user`` is the lever for containing
|
|
247
|
+
it by name, ``--all`` the lever for the whole instance regardless of token
|
|
248
|
+
ownership.
|
|
249
|
+
|
|
250
|
+
``--all`` already sweeps every grant instance-wide via
|
|
251
|
+
``revoke_personal_durable_grants(user=None)`` in ``_commit_revocation`` and
|
|
252
|
+
must not be duplicated here, so it returns an empty list.
|
|
253
|
+
"""
|
|
254
|
+
if target_user is not None:
|
|
255
|
+
return [target_user]
|
|
256
|
+
if options["all_personal"]:
|
|
257
|
+
owner_ids = {token.owner_id for token in doomed if token.owner_id is not None}
|
|
258
|
+
if not owner_ids:
|
|
259
|
+
return []
|
|
260
|
+
User = get_user_model()
|
|
261
|
+
return list(User.objects.filter(pk__in=owner_ids).order_by("pk"))
|
|
262
|
+
return []
|
|
263
|
+
|
|
264
|
+
def _report_account_sweep(self, affected_users: list[Any]) -> tuple[int, int, int, int, int]:
|
|
265
|
+
"""Dry-run totals for the non-token sweep ``--user``/``--all-personal`` apply (#4576).
|
|
266
|
+
|
|
267
|
+
Returns (refresh tokens, share links, git-automation secrets, outgoing
|
|
268
|
+
webhooks, live project sockets) summed across every affected account, and
|
|
269
|
+
prints each class's total the same way ``_count_durable_grants`` reports
|
|
270
|
+
``--all``'s instance-wide blast radius. One query set per affected account
|
|
271
|
+
— acceptable here: this command runs interactively during incident
|
|
272
|
+
response against a bounded, operator-named scope, not on a request path.
|
|
273
|
+
"""
|
|
274
|
+
if not affected_users:
|
|
275
|
+
return 0, 0, 0, 0, 0
|
|
276
|
+
refresh_total = links_total = secrets_total = webhooks_total = sockets_total = 0
|
|
277
|
+
for user in affected_users:
|
|
278
|
+
refresh_total += self._count_outstanding_refresh_tokens(user)
|
|
279
|
+
links, secrets_count, webhooks = self._grant_counts_for(user)
|
|
280
|
+
links_total += links
|
|
281
|
+
secrets_total += secrets_count
|
|
282
|
+
webhooks_total += webhooks
|
|
283
|
+
sockets_total += self._count_live_project_memberships(user.pk)
|
|
284
|
+
self.stdout.write(f"Outstanding refresh tokens matched: {refresh_total}")
|
|
285
|
+
self.stdout.write(f"Active share links matched: {links_total}")
|
|
286
|
+
self.stdout.write(f"Configured git-automation secrets matched: {secrets_total}")
|
|
287
|
+
self.stdout.write(f"Active outgoing webhooks matched: {webhooks_total}")
|
|
288
|
+
self.stdout.write(f"Live project socket connections matched: {sockets_total}")
|
|
289
|
+
return refresh_total, links_total, secrets_total, webhooks_total, sockets_total
|
|
290
|
+
|
|
291
|
+
def _confirm(self, options: dict[str, Any], total: int) -> None:
|
|
292
|
+
if options["yes"]:
|
|
293
|
+
return
|
|
294
|
+
answer = input(f"Revoke {total} grant(s)? This cannot be undone. [y/N] ")
|
|
295
|
+
if answer.strip().lower() not in ("y", "yes"):
|
|
296
|
+
raise CommandError("Aborted.")
|
|
297
|
+
|
|
298
|
+
def _commit_revocation(
|
|
299
|
+
self, doomed: list[Any], affected_users: list[Any], options: dict[str, Any]
|
|
300
|
+
) -> tuple[int, int, int, int, int, int]:
|
|
301
|
+
"""Revoke `doomed`, audit each one, and sweep the other durable grant classes.
|
|
302
|
+
|
|
303
|
+
Under ``--all`` this sweeps every grant instance-wide
|
|
304
|
+
(``revoke_personal_durable_grants(user=None)``) — unchanged from before
|
|
305
|
+
#4576. Under ``--user``/``--all-personal`` (#4576) it instead sweeps each
|
|
306
|
+
``affected_users`` account's refresh tokens, share links, git-automation
|
|
307
|
+
secret, outgoing webhooks, and live project sockets — the same
|
|
308
|
+
composition password reset (``core/password_reset.py``) and workspace
|
|
309
|
+
off-boarding (``apps/workspace/views.py``) already use, so the documented
|
|
310
|
+
off-boarding / stolen-laptop lever actually contains the account it names
|
|
311
|
+
rather than leaving everything but its PATs live.
|
|
312
|
+
|
|
313
|
+
Everything here runs inside one transaction — including the calls to
|
|
314
|
+
``revoke_all_refresh_tokens`` and ``revoke_personal_durable_grants``, both
|
|
315
|
+
plain DB updates — so the token revocation, the sweep, and the audit trail
|
|
316
|
+
commit or roll back together. ``evict_all_live_project_sockets`` is the one
|
|
317
|
+
exception: it defers its own work with ``transaction.on_commit`` (mirrored
|
|
318
|
+
from ``core/password_reset.py``), so a rolled-back run evicts nobody.
|
|
319
|
+
|
|
320
|
+
Returns (tokens revoked, share links revoked, git-automation secrets
|
|
321
|
+
cleared, outgoing webhooks deactivated, refresh tokens revoked, live
|
|
322
|
+
project sockets scheduled for eviction). The last two are always 0 for
|
|
323
|
+
``--all`` and for a run with no affected accounts.
|
|
324
|
+
"""
|
|
325
|
+
from trueppm_api.apps.projects.models import (
|
|
326
|
+
ApiToken,
|
|
327
|
+
ApiTokenAuditAction,
|
|
328
|
+
ApiTokenAuditEntry,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
now = timezone.now()
|
|
332
|
+
with transaction.atomic():
|
|
333
|
+
revoked = ApiToken.objects.filter(pk__in=[token.pk for token in doomed]).update(
|
|
334
|
+
revoked_at=now
|
|
335
|
+
)
|
|
336
|
+
# One audit row per token, in the same transaction as the revocation, so the
|
|
337
|
+
# sweep and its record commit together. The scope XOR on ApiTokenAuditEntry
|
|
338
|
+
# requires exactly one of owner/project/program, mirroring the token's own.
|
|
339
|
+
ApiTokenAuditEntry.objects.bulk_create(
|
|
340
|
+
[
|
|
341
|
+
ApiTokenAuditEntry(
|
|
342
|
+
owner_id=token.owner_id,
|
|
343
|
+
project_id=token.project_id,
|
|
344
|
+
program_id=token.program_id,
|
|
345
|
+
token=token,
|
|
346
|
+
token_prefix=token.token_prefix,
|
|
347
|
+
actor=None,
|
|
348
|
+
action=ApiTokenAuditAction.REVOKED.value,
|
|
349
|
+
detail={"name": token.name, "reason": REASON},
|
|
350
|
+
)
|
|
351
|
+
for token in doomed
|
|
352
|
+
]
|
|
353
|
+
)
|
|
354
|
+
links_revoked = secrets_cleared = webhooks_deactivated = 0
|
|
355
|
+
refresh_revoked = sockets_evicted = 0
|
|
356
|
+
if options["all"]:
|
|
357
|
+
from trueppm_api.apps.access.services import revoke_personal_durable_grants
|
|
358
|
+
|
|
359
|
+
links_revoked, secrets_cleared, webhooks_deactivated = (
|
|
360
|
+
revoke_personal_durable_grants(actor=None)
|
|
361
|
+
)
|
|
362
|
+
elif affected_users:
|
|
363
|
+
from trueppm_api.apps.access.services import (
|
|
364
|
+
revoke_all_refresh_tokens,
|
|
365
|
+
revoke_personal_durable_grants,
|
|
366
|
+
)
|
|
367
|
+
from trueppm_api.apps.access.signals import evict_all_live_project_sockets
|
|
368
|
+
|
|
369
|
+
for user in affected_users:
|
|
370
|
+
# Counted before the evict call schedules its on_commit work —
|
|
371
|
+
# the count and the eviction read the same live-membership rows,
|
|
372
|
+
# and neither mutates ProjectMembership, so order doesn't matter.
|
|
373
|
+
sockets_evicted += self._count_live_project_memberships(user.pk)
|
|
374
|
+
refresh_revoked += revoke_all_refresh_tokens(user)
|
|
375
|
+
u_links, u_secrets, u_webhooks = revoke_personal_durable_grants(
|
|
376
|
+
user, actor=None
|
|
377
|
+
)
|
|
378
|
+
links_revoked += u_links
|
|
379
|
+
secrets_cleared += u_secrets
|
|
380
|
+
webhooks_deactivated += u_webhooks
|
|
381
|
+
evict_all_live_project_sockets(user.pk)
|
|
382
|
+
return (
|
|
383
|
+
revoked,
|
|
384
|
+
links_revoked,
|
|
385
|
+
secrets_cleared,
|
|
386
|
+
webhooks_deactivated,
|
|
387
|
+
refresh_revoked,
|
|
388
|
+
sockets_evicted,
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
def handle(self, *args: Any, **options: Any) -> None:
|
|
392
|
+
target_user = self._resolve_target_user(options)
|
|
393
|
+
queryset, scope_label = self._resolve_scope(options, target_user)
|
|
394
|
+
doomed = list(queryset.only("pk", "token_prefix", "name", "owner", "project", "program"))
|
|
395
|
+
self._report_matched_tokens(doomed, scope_label)
|
|
396
|
+
links_matched, secrets_matched, webhooks_matched = self._count_durable_grants(options)
|
|
397
|
+
|
|
398
|
+
# #4576: --user and --all-personal are documented as the off-boarding /
|
|
399
|
+
# breach-containment levers, so they must also sweep refresh tokens, the
|
|
400
|
+
# three non-token durable grants, and live project sockets for every
|
|
401
|
+
# account the token revocation above targets — not PATs alone. See
|
|
402
|
+
# _resolve_affected_users for exactly which accounts that is per scope.
|
|
403
|
+
affected_users = self._resolve_affected_users(options, target_user, doomed)
|
|
404
|
+
(
|
|
405
|
+
refresh_matched,
|
|
406
|
+
acct_links_matched,
|
|
407
|
+
acct_secrets_matched,
|
|
408
|
+
acct_webhooks_matched,
|
|
409
|
+
sockets_matched,
|
|
410
|
+
) = self._report_account_sweep(affected_users)
|
|
411
|
+
links_matched += acct_links_matched
|
|
412
|
+
invites_matched = self._count_pending_invites(options, affected_users)
|
|
413
|
+
secrets_matched += acct_secrets_matched
|
|
414
|
+
webhooks_matched += acct_webhooks_matched
|
|
415
|
+
|
|
416
|
+
if not any(
|
|
417
|
+
(
|
|
418
|
+
doomed,
|
|
419
|
+
links_matched,
|
|
420
|
+
secrets_matched,
|
|
421
|
+
webhooks_matched,
|
|
422
|
+
refresh_matched,
|
|
423
|
+
sockets_matched,
|
|
424
|
+
invites_matched,
|
|
425
|
+
)
|
|
426
|
+
):
|
|
427
|
+
self.stdout.write(self.style.SUCCESS("Nothing to revoke."))
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
if not options["commit"]:
|
|
431
|
+
self.stdout.write(
|
|
432
|
+
self.style.WARNING("Dry run — nothing revoked. Re-run with --commit to apply.")
|
|
433
|
+
)
|
|
434
|
+
return
|
|
435
|
+
|
|
436
|
+
self._confirm(
|
|
437
|
+
options,
|
|
438
|
+
len(doomed)
|
|
439
|
+
+ links_matched
|
|
440
|
+
+ secrets_matched
|
|
441
|
+
+ webhooks_matched
|
|
442
|
+
+ refresh_matched
|
|
443
|
+
+ sockets_matched
|
|
444
|
+
+ invites_matched,
|
|
445
|
+
)
|
|
446
|
+
(
|
|
447
|
+
revoked,
|
|
448
|
+
links_revoked,
|
|
449
|
+
secrets_cleared,
|
|
450
|
+
webhooks_deactivated,
|
|
451
|
+
refresh_revoked,
|
|
452
|
+
sockets_evicted,
|
|
453
|
+
) = self._commit_revocation(doomed, affected_users, options)
|
|
454
|
+
|
|
455
|
+
self.stdout.write(self.style.SUCCESS(f"Revoked {revoked} token(s)."))
|
|
456
|
+
if options["all"]:
|
|
457
|
+
self.stdout.write(
|
|
458
|
+
self.style.SUCCESS(
|
|
459
|
+
f"Revoked {links_revoked} share link(s), cleared "
|
|
460
|
+
f"{secrets_cleared} git-automation secret(s), deactivated "
|
|
461
|
+
f"{webhooks_deactivated} outgoing webhook(s)."
|
|
462
|
+
)
|
|
463
|
+
)
|
|
464
|
+
elif affected_users:
|
|
465
|
+
self.stdout.write(
|
|
466
|
+
self.style.SUCCESS(
|
|
467
|
+
f"Revoked {refresh_revoked} refresh token(s), {links_revoked} share "
|
|
468
|
+
f"link(s), cleared {secrets_cleared} git-automation secret(s), "
|
|
469
|
+
f"deactivated {webhooks_deactivated} outgoing webhook(s), and "
|
|
470
|
+
f"scheduled eviction of {sockets_evicted} live project connection(s)."
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
if invites_matched:
|
|
474
|
+
scope = "instance-wide" if options["all"] else "sent by the targeted account(s)"
|
|
475
|
+
self.stdout.write(
|
|
476
|
+
self.style.SUCCESS(
|
|
477
|
+
f"Revoked {invites_matched} pending workspace invite(s) {scope}."
|
|
478
|
+
)
|
|
479
|
+
)
|
|
480
|
+
self.stdout.write(
|
|
481
|
+
"Sessions and JWTs are a separate credential class — rotate JWT_SIGNING_KEY "
|
|
482
|
+
"as well if you have not already (administration/security.md)."
|
|
483
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Initial migration for the access app — creates ProjectMembership."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
import django.db.models.deletion
|
|
8
|
+
from django.conf import settings
|
|
9
|
+
from django.db import migrations, models
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Migration(migrations.Migration):
|
|
13
|
+
initial = True
|
|
14
|
+
|
|
15
|
+
dependencies: list[tuple[str, str]] = [ # type: ignore[misc]
|
|
16
|
+
("projects", "0001_initial"),
|
|
17
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
operations = [
|
|
21
|
+
migrations.CreateModel(
|
|
22
|
+
name="ProjectMembership",
|
|
23
|
+
fields=[
|
|
24
|
+
("id", models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True)),
|
|
25
|
+
("server_version", models.BigIntegerField(default=0, editable=False)),
|
|
26
|
+
(
|
|
27
|
+
"project",
|
|
28
|
+
models.ForeignKey(
|
|
29
|
+
on_delete=django.db.models.deletion.PROTECT,
|
|
30
|
+
related_name="memberships",
|
|
31
|
+
to="projects.project",
|
|
32
|
+
),
|
|
33
|
+
),
|
|
34
|
+
(
|
|
35
|
+
"user",
|
|
36
|
+
models.ForeignKey(
|
|
37
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
38
|
+
related_name="memberships",
|
|
39
|
+
to=settings.AUTH_USER_MODEL,
|
|
40
|
+
),
|
|
41
|
+
),
|
|
42
|
+
(
|
|
43
|
+
"role",
|
|
44
|
+
models.IntegerField(
|
|
45
|
+
choices=[
|
|
46
|
+
(0, "Viewer"),
|
|
47
|
+
(1, "Member"),
|
|
48
|
+
(2, "Scheduler"),
|
|
49
|
+
(3, "Admin"),
|
|
50
|
+
(4, "Owner"),
|
|
51
|
+
]
|
|
52
|
+
),
|
|
53
|
+
),
|
|
54
|
+
],
|
|
55
|
+
options={"db_table": "access_project_membership"},
|
|
56
|
+
),
|
|
57
|
+
migrations.AlterUniqueTogether(
|
|
58
|
+
name="projectmembership",
|
|
59
|
+
unique_together={("project", "user")},
|
|
60
|
+
),
|
|
61
|
+
]
|