trueppm-api 0.4.0b2__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 +14 -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 +164 -0
- trueppm_api/apps/access/management/commands/migrate_locked.py +96 -0
- trueppm_api/apps/access/management/commands/revoke_api_tokens.py +209 -0
- trueppm_api/apps/access/migrations/0001_initial.py +61 -0
- trueppm_api/apps/access/migrations/0001_squashed_0012_programmembership_role_title.py +167 -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_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 +132 -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/models.py +588 -0
- trueppm_api/apps/access/permissions.py +2837 -0
- trueppm_api/apps/access/serializers.py +946 -0
- trueppm_api/apps/access/services.py +667 -0
- trueppm_api/apps/access/signals.py +103 -0
- trueppm_api/apps/access/tasks.py +50 -0
- trueppm_api/apps/access/throttles.py +95 -0
- trueppm_api/apps/access/urls.py +191 -0
- trueppm_api/apps/access/views.py +1931 -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/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 +108 -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/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 +418 -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 +177 -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 +837 -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 +403 -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_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/__init__.py +0 -0
- trueppm_api/apps/integrations/models.py +622 -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 +519 -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 +731 -0
- trueppm_api/apps/integrations/throttles.py +165 -0
- trueppm_api/apps/integrations/urls.py +77 -0
- trueppm_api/apps/integrations/views.py +1106 -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 +277 -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 +500 -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 +1231 -0
- trueppm_api/apps/msproject/serializers.py +136 -0
- trueppm_api/apps/msproject/services.py +58 -0
- trueppm_api/apps/msproject/tasks.py +436 -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 +447 -0
- trueppm_api/apps/notifications/email_backend.py +330 -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 +294 -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_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 +1197 -0
- trueppm_api/apps/notifications/receivers.py +155 -0
- trueppm_api/apps/notifications/schema_migrations.py +56 -0
- trueppm_api/apps/notifications/serializers.py +961 -0
- trueppm_api/apps/notifications/services.py +1451 -0
- trueppm_api/apps/notifications/tasks.py +711 -0
- trueppm_api/apps/notifications/throttles.py +98 -0
- trueppm_api/apps/notifications/urls.py +97 -0
- trueppm_api/apps/notifications/views.py +904 -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 +551 -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 +969 -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 +523 -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_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 +466 -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 +449 -0
- trueppm_api/apps/projects/backfill.py +287 -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 +204 -0
- trueppm_api/apps/projects/config_notice.py +1325 -0
- trueppm_api/apps/projects/custom_field_values.py +266 -0
- trueppm_api/apps/projects/decisions_services.py +93 -0
- trueppm_api/apps/projects/decisions_views.py +94 -0
- trueppm_api/apps/projects/estimation_scale.py +126 -0
- trueppm_api/apps/projects/export_bundle.py +353 -0
- trueppm_api/apps/projects/fixtures/seeds/atlas-platform-launch.json +4256 -0
- trueppm_api/apps/projects/fixtures/seeds/aurora-mobile-app.json +2000 -0
- trueppm_api/apps/projects/fixtures/seeds/bayside-civic-center.json +1825 -0
- trueppm_api/apps/projects/fixtures/seeds/ga-launch.json +1905 -0
- trueppm_api/apps/projects/fixtures/seeds/helios-crm-replacement.json +1409 -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/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 +247 -0
- trueppm_api/apps/projects/management/commands/seed_capacity.py +405 -0
- trueppm_api/apps/projects/management/commands/seed_integration_fixtures.py +178 -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/migrations/0001_initial.py +208 -0
- trueppm_api/apps/projects/migrations/0001_squashed_0094_project_decisions_policy.py +5595 -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 +44 -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 +61 -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 +62 -0
- trueppm_api/apps/projects/migrations/0106_boardsavedview_schema_version.py +20 -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/__init__.py +0 -0
- trueppm_api/apps/projects/models.py +8946 -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 +575 -0
- trueppm_api/apps/projects/program_schedule.py +745 -0
- trueppm_api/apps/projects/program_views.py +3220 -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 +493 -0
- trueppm_api/apps/projects/schemas/seed_v2.json +892 -0
- trueppm_api/apps/projects/seed/__init__.py +61 -0
- trueppm_api/apps/projects/seed/exporter.py +1644 -0
- trueppm_api/apps/projects/seed/forecast_backfill.py +247 -0
- trueppm_api/apps/projects/seed/importer.py +2390 -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 +126 -0
- trueppm_api/apps/projects/seed/replay.py +1691 -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 +1149 -0
- trueppm_api/apps/projects/serializers.py +12310 -0
- trueppm_api/apps/projects/services.py +6966 -0
- trueppm_api/apps/projects/share_serializers.py +132 -0
- trueppm_api/apps/projects/share_services.py +325 -0
- trueppm_api/apps/projects/share_views.py +374 -0
- trueppm_api/apps/projects/sharing_settings.py +187 -0
- trueppm_api/apps/projects/signal_privacy_services.py +909 -0
- trueppm_api/apps/projects/signal_privacy_views.py +476 -0
- trueppm_api/apps/projects/signals.py +132 -0
- trueppm_api/apps/projects/slip_conflict.py +266 -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 +1152 -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 +1917 -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 +258 -0
- trueppm_api/apps/projects/template_views.py +799 -0
- trueppm_api/apps/projects/throttles.py +311 -0
- trueppm_api/apps/projects/urls.py +1011 -0
- trueppm_api/apps/projects/utilization.py +885 -0
- trueppm_api/apps/projects/views.py +20572 -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/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 +1749 -0
- trueppm_api/apps/scheduling/__init__.py +0 -0
- trueppm_api/apps/scheduling/apps.py +153 -0
- trueppm_api/apps/scheduling/calendars.py +119 -0
- trueppm_api/apps/scheduling/deadletter.py +91 -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/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/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/__init__.py +0 -0
- trueppm_api/apps/scheduling/models.py +516 -0
- trueppm_api/apps/scheduling/receivers.py +160 -0
- trueppm_api/apps/scheduling/risk_premium.py +283 -0
- trueppm_api/apps/scheduling/serializers.py +564 -0
- trueppm_api/apps/scheduling/services.py +1182 -0
- trueppm_api/apps/scheduling/signals.py +56 -0
- trueppm_api/apps/scheduling/tasks.py +1984 -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 +2564 -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 +437 -0
- trueppm_api/apps/sso/services.py +1385 -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 +320 -0
- trueppm_api/apps/sync/conflict.py +262 -0
- trueppm_api/apps/sync/consumers.py +471 -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/0004_seed_sync_seq.py +24 -0
- trueppm_api/apps/sync/migrations/0005_program_sync_sequence.py +35 -0
- trueppm_api/apps/sync/migrations/__init__.py +0 -0
- trueppm_api/apps/sync/models.py +225 -0
- trueppm_api/apps/sync/pagination.py +231 -0
- trueppm_api/apps/sync/sequence.py +371 -0
- trueppm_api/apps/sync/serializers.py +687 -0
- trueppm_api/apps/sync/tasks.py +260 -0
- trueppm_api/apps/sync/throttles.py +175 -0
- trueppm_api/apps/sync/upload.py +501 -0
- trueppm_api/apps/sync/urls.py +17 -0
- trueppm_api/apps/sync/views.py +930 -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 +142 -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/__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 +434 -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/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 +117 -0
- trueppm_api/apps/timetracking/urls.py +47 -0
- trueppm_api/apps/timetracking/views.py +526 -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 +144 -0
- trueppm_api/apps/webhooks/migrations/0001_initial.py +146 -0
- trueppm_api/apps/webhooks/migrations/0001_squashed_0007_sprint_lifecycle_events.py +243 -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 +90 -0
- trueppm_api/apps/webhooks/migrations/__init__.py +0 -0
- trueppm_api/apps/webhooks/models.py +372 -0
- trueppm_api/apps/webhooks/serializers.py +318 -0
- trueppm_api/apps/webhooks/tasks.py +663 -0
- trueppm_api/apps/webhooks/urls.py +22 -0
- trueppm_api/apps/webhooks/views.py +383 -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 +368 -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 +242 -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_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 +919 -0
- trueppm_api/apps/workspace/signals.py +37 -0
- trueppm_api/apps/workspace/tasks.py +555 -0
- trueppm_api/apps/workspace/urls.py +113 -0
- trueppm_api/apps/workspace/views.py +1502 -0
- trueppm_api/asgi.py +42 -0
- trueppm_api/celery.py +31 -0
- trueppm_api/core/__init__.py +0 -0
- trueppm_api/core/admin_site.py +206 -0
- trueppm_api/core/auth_views.py +916 -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/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 +949 -0
- trueppm_api/core/password_reset.py +500 -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 +884 -0
- trueppm_api/core/storage_config.py +105 -0
- trueppm_api/core/text_decode.py +168 -0
- trueppm_api/core/throttling.py +344 -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/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 +2455 -0
- trueppm_api/settings/dev.py +273 -0
- trueppm_api/settings/prod.py +228 -0
- trueppm_api/urls.py +153 -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.0b2.dist-info/METADATA +70 -0
- trueppm_api-0.4.0b2.dist-info/RECORD +759 -0
- trueppm_api-0.4.0b2.dist-info/WHEEL +4 -0
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""App config for ``apps/sso`` — basic multi-provider SSO (ADR-0517, supersedes ADR-0187).
|
|
2
|
+
|
|
3
|
+
The two extension seams (``register_oidc_identity_mapper`` and
|
|
4
|
+
``register_local_login_policy_provider``) default to safe community behaviour
|
|
5
|
+
with no provider registered, so this app needs no OSS-side ``ready()`` wiring —
|
|
6
|
+
the module-level fallbacks in :mod:`trueppm_api.apps.sso.extensions` are the
|
|
7
|
+
community defaults. ``trueppm-enterprise`` registers richer providers against the
|
|
8
|
+
same seams from its own ``AppConfig.ready()`` (ADR-0177), with zero OSS change.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from django.apps import AppConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SsoConfig(AppConfig):
|
|
17
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
18
|
+
name = "trueppm_api.apps.sso"
|
|
19
|
+
verbose_name = "Single sign-on"
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""OSS extension seams for SSO (ADR-0187 §4–5, ADR-0177 idiom).
|
|
2
|
+
|
|
3
|
+
Two single-slot provider registries that govern the two capabilities the
|
|
4
|
+
OSS/Enterprise boundary excludes from the community edition. Each degrades to a
|
|
5
|
+
**safe community default** when no provider is registered, so the OSS edition is
|
|
6
|
+
fully functional standalone and ``trueppm-enterprise`` attaches with zero OSS
|
|
7
|
+
change (the one-way dependency holds: enterprise → OSS, never the reverse).
|
|
8
|
+
|
|
9
|
+
1. ``oidc_role_for`` — group→role mapping. OSS ignores claims and returns the
|
|
10
|
+
admin-chosen ``default_role``. Enterprise registers a mapper that reads
|
|
11
|
+
``groups``/custom claims (requesting the wider scope through its own provider
|
|
12
|
+
extension) and maps them to roles, with an auth-event audit trail. The
|
|
13
|
+
mapper's result is clamped at this seam: any role above
|
|
14
|
+
``WorkspaceRole.ADMIN`` (in particular ``OWNER``) or any value that is not a
|
|
15
|
+
valid ``WorkspaceRole`` ordinal is refused in favor of ``default_role`` —
|
|
16
|
+
``OWNER`` is unreachable through this seam regardless of what a mapper
|
|
17
|
+
returns.
|
|
18
|
+
2. ``local_login_allowed`` — enforced-SSO / disable-local-accounts. OSS always
|
|
19
|
+
returns ``True``; password login is never blocked. Enterprise registers a
|
|
20
|
+
provider that enforces ``allow_password_signin=False`` for non-exempt accounts.
|
|
21
|
+
|
|
22
|
+
Extension inputs are treated as **untrusted** even though enterprise is "ours"
|
|
23
|
+
(threat model, Boundary 5): both seams have a typed, narrow signature and the
|
|
24
|
+
callers fail safe on a raising provider.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import logging
|
|
30
|
+
from collections.abc import Callable
|
|
31
|
+
from typing import TYPE_CHECKING, Any
|
|
32
|
+
|
|
33
|
+
from trueppm_api.apps.workspace.models import WorkspaceRole
|
|
34
|
+
from trueppm_api.core.extension_providers import guard_single_provider
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from trueppm_api.apps.sso.models import SsoProviderPolicy
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger("trueppm.sso")
|
|
40
|
+
|
|
41
|
+
# Enterprise registers a callable mapping (claims, config) → workspace-role int.
|
|
42
|
+
# OSS leaves it None → the auto-create role is always ``config.default_role``.
|
|
43
|
+
_IDENTITY_MAPPER: Callable[[dict[str, Any], SsoProviderPolicy], int] | None = None
|
|
44
|
+
|
|
45
|
+
# Enterprise registers a callable user → bool ("may this user complete a password
|
|
46
|
+
# login?"). OSS leaves it None → password login is always allowed.
|
|
47
|
+
_LOCAL_LOGIN_POLICY: Callable[[Any], bool] | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def register_oidc_identity_mapper(
|
|
51
|
+
provider: Callable[[dict[str, Any], SsoProviderPolicy], int] | None,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Register (or clear, with ``None``) the claims→role mapper. Enterprise calls this."""
|
|
54
|
+
global _IDENTITY_MAPPER
|
|
55
|
+
_IDENTITY_MAPPER = guard_single_provider(_IDENTITY_MAPPER, provider, "oidc identity mapper")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def oidc_role_for(claims: dict[str, Any], config: SsoProviderPolicy) -> int:
|
|
59
|
+
"""Resolve the workspace-role ordinal to grant an auto-created SSO member.
|
|
60
|
+
|
|
61
|
+
OSS default: ignore the claims entirely and return ``config.default_role`` —
|
|
62
|
+
there is no ``groups`` scope and no claim→role logic in the community edition.
|
|
63
|
+
A registered enterprise mapper may read claims; if it raises, returns a
|
|
64
|
+
non-int, returns an ordinal that is not a valid :class:`WorkspaceRole`, or
|
|
65
|
+
returns a role above ``WorkspaceRole.ADMIN``, we fall back to
|
|
66
|
+
``default_role`` (fail safe — never grant *more* than the admin-configured
|
|
67
|
+
floor on a buggy mapper).
|
|
68
|
+
|
|
69
|
+
**Enterprise extension-point contract:** this is the enforcement point, not
|
|
70
|
+
an advisory clamp — ``WorkspaceRole.OWNER`` is categorically unreachable
|
|
71
|
+
through this seam, regardless of what the registered mapper returns. This
|
|
72
|
+
mirrors the OSS write serializer's ``default_role`` restriction (SSO
|
|
73
|
+
auto-create must never mint an Owner) and treats the enterprise mapper's
|
|
74
|
+
result as untrusted input per the extension-point threat model (Boundary
|
|
75
|
+
5) — a bug in the mapper is not a privilege-escalation path.
|
|
76
|
+
"""
|
|
77
|
+
if _IDENTITY_MAPPER is None:
|
|
78
|
+
return int(config.default_role)
|
|
79
|
+
try:
|
|
80
|
+
role = _IDENTITY_MAPPER(claims, config)
|
|
81
|
+
except Exception:
|
|
82
|
+
logger.exception("oidc identity mapper raised; falling back to default_role")
|
|
83
|
+
return int(config.default_role)
|
|
84
|
+
if not isinstance(role, int):
|
|
85
|
+
logger.error("oidc identity mapper returned non-int %r; using default_role", role)
|
|
86
|
+
return int(config.default_role)
|
|
87
|
+
if role not in WorkspaceRole.values or role > WorkspaceRole.ADMIN:
|
|
88
|
+
logger.error(
|
|
89
|
+
"oidc identity mapper returned unclamped role %r (above ADMIN ceiling "
|
|
90
|
+
"or not a valid WorkspaceRole); using default_role",
|
|
91
|
+
role,
|
|
92
|
+
)
|
|
93
|
+
return int(config.default_role)
|
|
94
|
+
return role
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def register_local_login_policy_provider(provider: Callable[[Any], bool] | None) -> None:
|
|
98
|
+
"""Register (or clear, with ``None``) the password-login policy. Enterprise calls this."""
|
|
99
|
+
global _LOCAL_LOGIN_POLICY
|
|
100
|
+
_LOCAL_LOGIN_POLICY = guard_single_provider(_LOCAL_LOGIN_POLICY, provider, "local login policy")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def local_login_policy_enforced() -> bool:
|
|
104
|
+
"""Whether an edition actually enforces the ``allow_password_signin`` setting.
|
|
105
|
+
|
|
106
|
+
OSS returns ``False`` — the community edition never blocks password login, so
|
|
107
|
+
persisting ``allow_password_signin`` would be a set-but-ignored no-op. The SSO
|
|
108
|
+
write serializer uses this to *reject* the field in OSS instead of silently
|
|
109
|
+
swallowing it (#2025). Enterprise registers a policy provider, flipping this to
|
|
110
|
+
``True``, and its own serializer accepts and honors the field.
|
|
111
|
+
"""
|
|
112
|
+
return _LOCAL_LOGIN_POLICY is not None
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def local_login_allowed(user: Any) -> bool:
|
|
116
|
+
"""Whether ``user`` may complete a username/password login.
|
|
117
|
+
|
|
118
|
+
OSS default: always ``True`` — the community edition never enforces
|
|
119
|
+
"Allow password sign-in: OFF" (that enforcement is an enterprise capability;
|
|
120
|
+
ADR-0187 §4). A registered enterprise provider may return ``False`` to block
|
|
121
|
+
local login; if it raises, OSS fails **open** (login allowed) so a buggy
|
|
122
|
+
enterprise policy can never lock every admin out of the install.
|
|
123
|
+
"""
|
|
124
|
+
if _LOCAL_LOGIN_POLICY is None:
|
|
125
|
+
return True
|
|
126
|
+
try:
|
|
127
|
+
return bool(_LOCAL_LOGIN_POLICY(user))
|
|
128
|
+
except Exception:
|
|
129
|
+
logger.exception("local login policy provider raised; allowing password login")
|
|
130
|
+
return True
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""Seed a Keycloak OIDC provider for the ``sso:integration`` nightly CI job (#2274).
|
|
2
|
+
|
|
3
|
+
Provisions the one enabled ``keycloak`` provider (an allauth ``SocialApp`` +
|
|
4
|
+
:class:`SsoProviderPolicy`) pointing at the live Keycloak service that the CI job
|
|
5
|
+
runs, plus a workspace-admin account the Playwright spec authenticates as to hit
|
|
6
|
+
the admin-only ``test-connection`` endpoint. Idempotent — re-running updates the
|
|
7
|
+
existing rows rather than duplicating them.
|
|
8
|
+
|
|
9
|
+
The provider mirrors what an operator configures in Workspace Settings → SSO for a
|
|
10
|
+
self-hosted Keycloak realm: issuer (base URL + realm), confidential client id +
|
|
11
|
+
secret, a permitted email domain, and auto-create so the seeded Keycloak test
|
|
12
|
+
user becomes a TruePPM member on first login (exercising the full ``resolve_user``
|
|
13
|
+
path, not just the handshake).
|
|
14
|
+
|
|
15
|
+
Environment variables (all have CI-friendly defaults; the job sets them
|
|
16
|
+
explicitly so the values match the baked realm export):
|
|
17
|
+
|
|
18
|
+
SSO_KEYCLOAK_ISSUER e.g. http://keycloak:8080/realms/trueppm-ci
|
|
19
|
+
SSO_KEYCLOAK_CLIENT_ID confidential client id (default: trueppm-web)
|
|
20
|
+
SSO_KEYCLOAK_CLIENT_SECRET confidential client secret
|
|
21
|
+
SSO_KEYCLOAK_ALLOWED_DOMAIN domain of the Keycloak test user's email
|
|
22
|
+
SSO_ADMIN_EMAIL workspace-admin account for test-connection
|
|
23
|
+
INTEGRATION_USER_PASSWORD password for the admin account (shared with the
|
|
24
|
+
web:integration fixtures)
|
|
25
|
+
|
|
26
|
+
The issuer host must be present in ``EGRESS_ALLOWLISTED_HOSTS`` (ADR-0590) or the
|
|
27
|
+
SSRF egress guard blocks discovery/token/JWKS — the CI job sets both together.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import os
|
|
33
|
+
import secrets
|
|
34
|
+
|
|
35
|
+
from allauth.socialaccount.models import SocialApp
|
|
36
|
+
from django.conf import settings
|
|
37
|
+
from django.contrib.auth import get_user_model
|
|
38
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
39
|
+
from django.db import transaction
|
|
40
|
+
|
|
41
|
+
from trueppm_api.apps.sso import services
|
|
42
|
+
from trueppm_api.apps.sso.models import SsoProviderPolicy
|
|
43
|
+
from trueppm_api.apps.workspace.models import (
|
|
44
|
+
MemberStatus,
|
|
45
|
+
Workspace,
|
|
46
|
+
WorkspaceMembership,
|
|
47
|
+
WorkspaceRole,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
_SLUG = "keycloak"
|
|
51
|
+
_DEFAULT_ISSUER = "http://keycloak:8080/realms/trueppm-ci"
|
|
52
|
+
_DEFAULT_CLIENT_ID = "trueppm-web"
|
|
53
|
+
_DEFAULT_CLIENT_SECRET = "trueppm-ci-secret" # matches the baked realm export
|
|
54
|
+
_DEFAULT_ALLOWED_DOMAIN = "trueppm-ci.test"
|
|
55
|
+
_DEFAULT_ADMIN_EMAIL = "sso-admin@trueppm-ci.test"
|
|
56
|
+
|
|
57
|
+
_PASSWORD_ENV = "INTEGRATION_USER_PASSWORD"
|
|
58
|
+
_DEBUG_DEFAULT_PASSWORD = "ci-integration-pw" # only honored under settings.DEBUG
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _resolve_admin_password() -> tuple[str, str]:
|
|
62
|
+
"""Resolve the seeded admin's password + its source (mirrors #1375).
|
|
63
|
+
|
|
64
|
+
The seeded admin is a real, loginable, workspace-admin account, so a fixed weak
|
|
65
|
+
password must never reach a public (non-DEBUG) instance. Resolution order:
|
|
66
|
+
``INTEGRATION_USER_PASSWORD`` env (operator/CI opt-in) → the static default
|
|
67
|
+
under DEBUG (local dev) → a random token otherwise. Returns ``(password,
|
|
68
|
+
source)`` where source is ``"env"`` / ``"debug"`` / ``"generated"``.
|
|
69
|
+
"""
|
|
70
|
+
env_pw = os.environ.get(_PASSWORD_ENV)
|
|
71
|
+
if env_pw:
|
|
72
|
+
return env_pw, "env"
|
|
73
|
+
if settings.DEBUG:
|
|
74
|
+
return _DEBUG_DEFAULT_PASSWORD, "debug"
|
|
75
|
+
return secrets.token_urlsafe(16), "generated"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Command(BaseCommand):
|
|
79
|
+
"""Seed the Keycloak SSO provider + a workspace-admin (idempotent)."""
|
|
80
|
+
|
|
81
|
+
help = "Seed a live-Keycloak OIDC provider for the sso:integration CI job."
|
|
82
|
+
|
|
83
|
+
def add_arguments(self, parser: object) -> None:
|
|
84
|
+
parser.add_argument( # type: ignore[attr-defined]
|
|
85
|
+
"--force",
|
|
86
|
+
action="store_true",
|
|
87
|
+
help=(
|
|
88
|
+
"Allow the seed to run even when a throwaway password would be "
|
|
89
|
+
"generated (DEBUG off and no INTEGRATION_USER_PASSWORD)."
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
@transaction.atomic
|
|
94
|
+
def handle(self, *args: object, **options: object) -> None:
|
|
95
|
+
# Prod guard (mirrors seed_integration_fixtures, #1375): this command
|
|
96
|
+
# creates an *enabled* SSO provider and a loginable workspace-admin account.
|
|
97
|
+
# On a non-DEBUG instance with no env password the only resolvable password
|
|
98
|
+
# is a useless random throwaway, so refuse to create the admin unless the
|
|
99
|
+
# operator explicitly opts in — it must never mint a known-credential admin
|
|
100
|
+
# (or a stray CI provider) on a real instance.
|
|
101
|
+
password, password_source = _resolve_admin_password()
|
|
102
|
+
if password_source == "generated" and not options.get("force"):
|
|
103
|
+
raise CommandError(
|
|
104
|
+
"Refusing to seed the Keycloak CI provider on a non-DEBUG instance "
|
|
105
|
+
"without a password — this creates an enabled SSO provider and a "
|
|
106
|
+
f"loginable workspace-admin. Set {_PASSWORD_ENV} (as the CI job does) "
|
|
107
|
+
"or pass --force to override."
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
issuer = os.environ.get("SSO_KEYCLOAK_ISSUER", _DEFAULT_ISSUER)
|
|
111
|
+
client_id = os.environ.get("SSO_KEYCLOAK_CLIENT_ID", _DEFAULT_CLIENT_ID)
|
|
112
|
+
client_secret = os.environ.get("SSO_KEYCLOAK_CLIENT_SECRET", _DEFAULT_CLIENT_SECRET)
|
|
113
|
+
allowed_domain = os.environ.get("SSO_KEYCLOAK_ALLOWED_DOMAIN", _DEFAULT_ALLOWED_DOMAIN)
|
|
114
|
+
admin_email = os.environ.get("SSO_ADMIN_EMAIL", _DEFAULT_ADMIN_EMAIL)
|
|
115
|
+
|
|
116
|
+
workspace = Workspace.load()
|
|
117
|
+
|
|
118
|
+
# allauth SocialApp holds the endpoint/claim knowledge; the real secret
|
|
119
|
+
# lives Fernet-encrypted on the policy side row (SocialApp.secret stays
|
|
120
|
+
# empty — ADR-0517 §3.1), so update_or_create the app with an empty secret.
|
|
121
|
+
# get-or-create-ok: CI seed command, fixed constant provider slug — no duplicate reachable
|
|
122
|
+
app, _ = SocialApp.objects.update_or_create(
|
|
123
|
+
provider_id=_SLUG,
|
|
124
|
+
defaults={
|
|
125
|
+
"provider": services.ALLAUTH_OPENID_CONNECT,
|
|
126
|
+
"name": "Keycloak (CI)",
|
|
127
|
+
"client_id": client_id,
|
|
128
|
+
"secret": "",
|
|
129
|
+
"settings": {"server_url": issuer},
|
|
130
|
+
},
|
|
131
|
+
)
|
|
132
|
+
app.sites.add(int(getattr(settings, "SITE_ID", 1)))
|
|
133
|
+
|
|
134
|
+
policy, _ = SsoProviderPolicy.objects.update_or_create(
|
|
135
|
+
social_app=app,
|
|
136
|
+
defaults={
|
|
137
|
+
"workspace": workspace,
|
|
138
|
+
"slug": _SLUG,
|
|
139
|
+
"enabled": True,
|
|
140
|
+
"allowed_email_domains": [allowed_domain.lower()],
|
|
141
|
+
# Auto-create so the seeded Keycloak user becomes a member on first
|
|
142
|
+
# login — the smoke exercises the full resolve/create path.
|
|
143
|
+
"auto_create_members": True,
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
policy.set_client_secret(client_secret)
|
|
147
|
+
policy.save(update_fields=["secret_ciphertext"])
|
|
148
|
+
|
|
149
|
+
# Workspace-admin for the admin-only test-connection call. Least privilege:
|
|
150
|
+
# an explicit WorkspaceMembership at ADMIN (not a Django superuser) is enough
|
|
151
|
+
# to clear IsWorkspaceAdminStrict, and is workspace-scoped rather than a
|
|
152
|
+
# global backdoor. username == email so the password login (/auth/token/)
|
|
153
|
+
# succeeds (Django's default USERNAME_FIELD is "username").
|
|
154
|
+
user_model = get_user_model()
|
|
155
|
+
admin, _ = user_model.objects.update_or_create(
|
|
156
|
+
email=admin_email,
|
|
157
|
+
defaults={"username": admin_email},
|
|
158
|
+
)
|
|
159
|
+
# nosemgrep: unvalidated-password # CI fixture, not an interactive signup.
|
|
160
|
+
admin.set_password(password)
|
|
161
|
+
admin.save(update_fields=["password"])
|
|
162
|
+
WorkspaceMembership.objects.update_or_create(
|
|
163
|
+
workspace=workspace,
|
|
164
|
+
user=admin,
|
|
165
|
+
defaults={"role": WorkspaceRole.ADMIN, "status": MemberStatus.ACTIVE},
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
self.stdout.write(self.style.SUCCESS("Keycloak SSO provider seeded"))
|
|
169
|
+
self.stdout.write(f" issuer: {issuer}")
|
|
170
|
+
self.stdout.write(f" client: {client_id}")
|
|
171
|
+
self.stdout.write(f" domain: {allowed_domain}")
|
|
172
|
+
self.stdout.write(f" admin: {admin_email}")
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Generated by Django 5.2.14 on 2026-06-30 12:54
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
import django.contrib.postgres.fields
|
|
6
|
+
import django.db.models.deletion
|
|
7
|
+
from django.conf import settings
|
|
8
|
+
from django.db import migrations, models
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _default_scopes() -> list[str]:
|
|
12
|
+
# Inlined default (ADR-0517 drops this model in 0002). Kept as a module-level
|
|
13
|
+
# callable so the historical migration replays deterministically after the
|
|
14
|
+
# ``OIDCProvider.scopes`` field (and its former model helper) were removed.
|
|
15
|
+
return ["openid", "email", "profile"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Migration(migrations.Migration):
|
|
19
|
+
initial = True
|
|
20
|
+
|
|
21
|
+
dependencies = [
|
|
22
|
+
(
|
|
23
|
+
"workspace",
|
|
24
|
+
"0001_squashed_0014_workspacemembership_availability_effective_from_and_more",
|
|
25
|
+
),
|
|
26
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
operations = [
|
|
30
|
+
migrations.CreateModel(
|
|
31
|
+
name="OIDCIdentity",
|
|
32
|
+
fields=[
|
|
33
|
+
(
|
|
34
|
+
"id",
|
|
35
|
+
models.UUIDField(
|
|
36
|
+
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
("issuer", models.CharField(max_length=512)),
|
|
40
|
+
("subject", models.CharField(max_length=255)),
|
|
41
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
42
|
+
(
|
|
43
|
+
"user",
|
|
44
|
+
models.ForeignKey(
|
|
45
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
46
|
+
related_name="oidc_identities",
|
|
47
|
+
to=settings.AUTH_USER_MODEL,
|
|
48
|
+
),
|
|
49
|
+
),
|
|
50
|
+
],
|
|
51
|
+
options={
|
|
52
|
+
"db_table": "sso_oidc_identity",
|
|
53
|
+
"constraints": [
|
|
54
|
+
models.UniqueConstraint(
|
|
55
|
+
fields=("issuer", "subject"), name="uniq_oidc_identity_iss_sub"
|
|
56
|
+
)
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
),
|
|
60
|
+
migrations.CreateModel(
|
|
61
|
+
name="OIDCProvider",
|
|
62
|
+
fields=[
|
|
63
|
+
(
|
|
64
|
+
"id",
|
|
65
|
+
models.UUIDField(
|
|
66
|
+
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
|
67
|
+
),
|
|
68
|
+
),
|
|
69
|
+
("enabled", models.BooleanField(default=False)),
|
|
70
|
+
("display_name", models.CharField(blank=True, default="", max_length=255)),
|
|
71
|
+
("issuer_url", models.URLField(blank=True, default="")),
|
|
72
|
+
("client_id", models.CharField(blank=True, default="", max_length=255)),
|
|
73
|
+
("client_secret_ciphertext", models.BinaryField(blank=True, default=b"")),
|
|
74
|
+
(
|
|
75
|
+
"scopes",
|
|
76
|
+
django.contrib.postgres.fields.ArrayField(
|
|
77
|
+
base_field=models.CharField(max_length=64),
|
|
78
|
+
default=_default_scopes,
|
|
79
|
+
size=None,
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
(
|
|
83
|
+
"allowed_email_domains",
|
|
84
|
+
django.contrib.postgres.fields.ArrayField(
|
|
85
|
+
base_field=models.CharField(max_length=255),
|
|
86
|
+
blank=True,
|
|
87
|
+
default=list,
|
|
88
|
+
size=None,
|
|
89
|
+
),
|
|
90
|
+
),
|
|
91
|
+
("auto_create_members", models.BooleanField(default=False)),
|
|
92
|
+
(
|
|
93
|
+
"default_role",
|
|
94
|
+
models.PositiveSmallIntegerField(
|
|
95
|
+
choices=[(100, "Member"), (300, "Admin"), (400, "Owner")], default=100
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
("allow_password_signin", models.BooleanField(default=True)),
|
|
99
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
100
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
101
|
+
(
|
|
102
|
+
"workspace",
|
|
103
|
+
models.ForeignKey(
|
|
104
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
105
|
+
related_name="oidc_providers",
|
|
106
|
+
to="workspace.workspace",
|
|
107
|
+
),
|
|
108
|
+
),
|
|
109
|
+
],
|
|
110
|
+
options={
|
|
111
|
+
"db_table": "sso_oidc_provider",
|
|
112
|
+
"constraints": [
|
|
113
|
+
models.UniqueConstraint(
|
|
114
|
+
fields=("workspace",), name="uniq_oidc_provider_workspace"
|
|
115
|
+
)
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
),
|
|
119
|
+
]
|
trueppm_api/apps/sso/migrations/0002_remove_oidcprovider_workspace_ssoproviderpolicy_and_more.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
# Generated by Django 5.2.16 on 2026-07-18 01:11
|
|
2
|
+
#
|
|
3
|
+
# ADR-0517: adopt allauth.socialaccount. Create the SsoProviderPolicy side row,
|
|
4
|
+
# migrate every OIDCProvider → (SocialApp + SsoProviderPolicy) and every
|
|
5
|
+
# OIDCIdentity → SocialAccount, then drop the bespoke sso_oidc_* tables. SSO is
|
|
6
|
+
# unshipped, so this is a NO-OP on a fresh install (no OIDCProvider rows exist);
|
|
7
|
+
# it exists for dev-DB parity and to keep makemigrations --check honest.
|
|
8
|
+
|
|
9
|
+
import uuid
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import django.contrib.postgres.fields
|
|
13
|
+
import django.db.models.deletion
|
|
14
|
+
from django.conf import settings as django_settings
|
|
15
|
+
from django.db import migrations, models
|
|
16
|
+
|
|
17
|
+
# The two allauth provider modules we enable; the migrated singleton is generic OIDC.
|
|
18
|
+
_ALLAUTH_OPENID_CONNECT = "openid_connect"
|
|
19
|
+
_MIGRATED_SLUG = "generic"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _copy_oidc_config_to_allauth(apps: Any, schema_editor: Any) -> None:
|
|
23
|
+
"""Port each OIDCProvider → SocialApp + SsoProviderPolicy, each OIDCIdentity → SocialAccount.
|
|
24
|
+
|
|
25
|
+
Uses ``apps.get_model`` (never a direct import) so it runs against the historical
|
|
26
|
+
schema. The client secret is already Fernet-encrypted under the same
|
|
27
|
+
``INTEGRATION_ENCRYPTION_KEY``, so the ciphertext bytes are copied verbatim into
|
|
28
|
+
the side row — ``SocialApp.secret`` stays empty (control 2, ADR-0517 §3.1).
|
|
29
|
+
|
|
30
|
+
Assumes the OSS single-workspace topology: every OIDCProvider collapses into one
|
|
31
|
+
SocialApp under the constant "generic" slug. Multiple OIDCProviders across
|
|
32
|
+
multiple workspaces would collide on that slug — but that cannot occur in OSS,
|
|
33
|
+
which is single-workspace (Workspace is a singleton).
|
|
34
|
+
"""
|
|
35
|
+
OIDCProvider = apps.get_model("sso", "OIDCProvider")
|
|
36
|
+
OIDCIdentity = apps.get_model("sso", "OIDCIdentity")
|
|
37
|
+
SsoProviderPolicy = apps.get_model("sso", "SsoProviderPolicy")
|
|
38
|
+
SocialApp = apps.get_model("socialaccount", "SocialApp")
|
|
39
|
+
SocialAccount = apps.get_model("socialaccount", "SocialAccount")
|
|
40
|
+
Site = apps.get_model("sites", "Site")
|
|
41
|
+
|
|
42
|
+
site_id = int(getattr(django_settings, "SITE_ID", 1))
|
|
43
|
+
site = Site.objects.filter(pk=site_id).first()
|
|
44
|
+
|
|
45
|
+
for provider in OIDCProvider.objects.all():
|
|
46
|
+
app, _created = SocialApp.objects.get_or_create(
|
|
47
|
+
provider=_ALLAUTH_OPENID_CONNECT,
|
|
48
|
+
provider_id=_MIGRATED_SLUG,
|
|
49
|
+
defaults={
|
|
50
|
+
"name": provider.display_name or "OIDC",
|
|
51
|
+
"client_id": provider.client_id,
|
|
52
|
+
"secret": "",
|
|
53
|
+
"settings": {"server_url": provider.issuer_url},
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
if site is not None:
|
|
57
|
+
app.sites.add(site)
|
|
58
|
+
|
|
59
|
+
SsoProviderPolicy.objects.get_or_create(
|
|
60
|
+
social_app=app,
|
|
61
|
+
defaults={
|
|
62
|
+
"workspace_id": provider.workspace_id,
|
|
63
|
+
"slug": _MIGRATED_SLUG,
|
|
64
|
+
"enabled": provider.enabled,
|
|
65
|
+
"allowed_email_domains": list(provider.allowed_email_domains or []),
|
|
66
|
+
"auto_create_members": provider.auto_create_members,
|
|
67
|
+
"default_role": provider.default_role,
|
|
68
|
+
"allow_password_signin": provider.allow_password_signin,
|
|
69
|
+
"github_org": "",
|
|
70
|
+
"secret_ciphertext": bytes(provider.client_secret_ciphertext or b""),
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
for identity in OIDCIdentity.objects.all():
|
|
75
|
+
SocialAccount.objects.get_or_create(
|
|
76
|
+
provider=_MIGRATED_SLUG,
|
|
77
|
+
uid=identity.subject,
|
|
78
|
+
defaults={
|
|
79
|
+
"user_id": identity.user_id,
|
|
80
|
+
"extra_data": {"iss": identity.issuer},
|
|
81
|
+
},
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _noop_reverse(apps: Any, schema_editor: Any) -> None:
|
|
86
|
+
"""Reverse is a no-op: the bespoke tables are gone; we do not reconstruct them."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Migration(migrations.Migration):
|
|
90
|
+
dependencies = [
|
|
91
|
+
("socialaccount", "0006_alter_socialaccount_extra_data"),
|
|
92
|
+
("sites", "0002_alter_domain_unique"),
|
|
93
|
+
("sso", "0001_initial"),
|
|
94
|
+
("workspace", "0016_historicalworkspace_calendar_and_more"),
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
operations = [
|
|
98
|
+
migrations.CreateModel(
|
|
99
|
+
name="SsoProviderPolicy",
|
|
100
|
+
fields=[
|
|
101
|
+
(
|
|
102
|
+
"id",
|
|
103
|
+
models.UUIDField(
|
|
104
|
+
default=uuid.uuid4, editable=False, primary_key=True, serialize=False
|
|
105
|
+
),
|
|
106
|
+
),
|
|
107
|
+
("enabled", models.BooleanField(default=False)),
|
|
108
|
+
("slug", models.CharField(max_length=64)),
|
|
109
|
+
(
|
|
110
|
+
"allowed_email_domains",
|
|
111
|
+
django.contrib.postgres.fields.ArrayField(
|
|
112
|
+
base_field=models.CharField(max_length=255),
|
|
113
|
+
blank=True,
|
|
114
|
+
default=list,
|
|
115
|
+
size=None,
|
|
116
|
+
),
|
|
117
|
+
),
|
|
118
|
+
("auto_create_members", models.BooleanField(default=False)),
|
|
119
|
+
(
|
|
120
|
+
"default_role",
|
|
121
|
+
models.PositiveSmallIntegerField(
|
|
122
|
+
choices=[(100, "Member"), (300, "Admin"), (400, "Owner")], default=100
|
|
123
|
+
),
|
|
124
|
+
),
|
|
125
|
+
("allow_password_signin", models.BooleanField(default=True)),
|
|
126
|
+
("github_org", models.CharField(blank=True, default="", max_length=255)),
|
|
127
|
+
("secret_ciphertext", models.BinaryField(blank=True, default=b"")),
|
|
128
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
129
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
130
|
+
(
|
|
131
|
+
"social_app",
|
|
132
|
+
models.OneToOneField(
|
|
133
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
134
|
+
related_name="trueppm_policy",
|
|
135
|
+
to="socialaccount.socialapp",
|
|
136
|
+
),
|
|
137
|
+
),
|
|
138
|
+
(
|
|
139
|
+
"workspace",
|
|
140
|
+
models.ForeignKey(
|
|
141
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
142
|
+
related_name="sso_provider_policies",
|
|
143
|
+
to="workspace.workspace",
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
],
|
|
147
|
+
options={
|
|
148
|
+
"db_table": "sso_provider_policy",
|
|
149
|
+
},
|
|
150
|
+
),
|
|
151
|
+
migrations.AddConstraint(
|
|
152
|
+
model_name="ssoproviderpolicy",
|
|
153
|
+
constraint=models.UniqueConstraint(
|
|
154
|
+
fields=("workspace", "slug"), name="uniq_sso_policy_workspace_slug"
|
|
155
|
+
),
|
|
156
|
+
),
|
|
157
|
+
# Data copy MUST run before the fields/models it reads are dropped below.
|
|
158
|
+
migrations.RunPython(_copy_oidc_config_to_allauth, _noop_reverse),
|
|
159
|
+
migrations.RemoveField(
|
|
160
|
+
model_name="oidcprovider",
|
|
161
|
+
name="workspace",
|
|
162
|
+
),
|
|
163
|
+
migrations.DeleteModel(
|
|
164
|
+
name="OIDCIdentity",
|
|
165
|
+
),
|
|
166
|
+
migrations.DeleteModel(
|
|
167
|
+
name="OIDCProvider",
|
|
168
|
+
),
|
|
169
|
+
]
|
|
File without changes
|