feat(jira): per-user auth, lifecycle hooks, admin config endpoints

- Add jira_api_token field to User model + migration b042
- Per-user Jira client: user's corporate email + personal Atlassian token
- Admin-configurable Jira URL/project via system_configs (GET/PATCH /system/jira-config + POST /system/jira-test)
- Auto-create Jira ticket when a test is created (non-fatal)
- Push lifecycle comments on every state transition: draft→red_executing→blue_evaluating→in_review→validated/rejected→draft
- Rich ticket descriptions with technique, MITRE ID, priority from severity, labels
- UserOut.jira_token_set (bool) instead of exposing raw token
- PATCH /users/me/preferences now accepts jira_api_token
This commit is contained in:
kitos
2026-05-26 15:56:28 +02:00
parent f3109644cb
commit 87af1735ce
8 changed files with 631 additions and 46 deletions
+14 -1
View File
@@ -122,10 +122,13 @@ class PasswordChange(BaseModel):
class UserPreferencesUpdate(BaseModel):
"""Payload for updating current user's notification preferences and Jira account."""
"""Payload for updating current user's notification preferences and Jira settings."""
notification_preferences: dict | None = None
jira_account_id: str | None = None
# Personal Jira API token (Atlassian token) — write-only, stored encrypted at rest.
# Set to empty string "" to clear the token.
jira_api_token: str | None = None
class UserOut(BaseModel):
@@ -141,5 +144,15 @@ class UserOut(BaseModel):
last_login: datetime | None = None
notification_preferences: dict | None = None
jira_account_id: str | None = None
# Never return the raw token — just indicate whether it is configured.
jira_token_set: bool = False
model_config = ConfigDict(from_attributes=True)
@classmethod
def model_validate(cls, obj, *args, **kwargs): # type: ignore[override]
instance = super().model_validate(obj, *args, **kwargs)
# Derive jira_token_set from the ORM object without exposing the value
if hasattr(obj, "jira_api_token"):
instance.jira_token_set = bool(obj.jira_api_token)
return instance