From 6337bdecbf3c6500b5f943c461a26db491334a19 Mon Sep 17 00:00:00 2001 From: Alicia Matsumoto Date: Mon, 25 Aug 2025 11:46:17 -0400 Subject: [PATCH 1/7] add new perm --- src/dispatch/auth/permissions.py | 31 +++++++++++++++++++++++++++++++ src/dispatch/task/views.py | 20 +++++++++++++------- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/dispatch/auth/permissions.py b/src/dispatch/auth/permissions.py index bd0b1a95da46..611d744f7c73 100644 --- a/src/dispatch/auth/permissions.py +++ b/src/dispatch/auth/permissions.py @@ -16,6 +16,7 @@ from dispatch.organization import service as organization_service from dispatch.organization.models import OrganizationRead from dispatch.participant_role.enums import ParticipantRoleType +from dispatch.task import service as task_service log = logging.getLogger(__name__) @@ -305,6 +306,36 @@ def has_required_permissions( return True +class IncidentViewPermissionForTasks(BasePermission): + """ + Permissions dependency to apply incident view permissions to task-based requests. + """ + def has_required_permissions(self, request: Request) -> bool: + pk = PrimaryKeyModel(id=request.path_params["task_id"]) + current_task = task_service.get(db_session=request.state.db, task_id=pk.id) + + if not current_task or not current_task.incident: + return False + + # minimal object with the attributes required for IncidentViewPermission + incident_request = type('IncidentRequest', (), { + 'path_params': {**request.path_params, 'incident_id': current_task.incident.id}, + 'state': request.state + })() + + # copy necessary request attributes + for attr in ['headers', 'method', 'url', 'query_params']: + if hasattr(request, attr): + setattr(incident_request, attr, getattr(request, attr)) + + return any_permission( + permissions=[IncidentViewPermission], + request=incident_request, + ) + + + + class IncidentEditPermission(BasePermission): def has_required_permissions( self, diff --git a/src/dispatch/task/views.py b/src/dispatch/task/views.py index 3a7eedddf01a..162f1209ff64 100644 --- a/src/dispatch/task/views.py +++ b/src/dispatch/task/views.py @@ -1,8 +1,13 @@ import json -from fastapi import APIRouter, HTTPException, Query, status +from fastapi import APIRouter, HTTPException, Query, status, Depends from dispatch.auth.service import CurrentUser +from dispatch.auth.permissions import ( + IncidentViewPermission, + PermissionsDependency, + IncidentViewPermissionForTasks +) from dispatch.common.utils.views import create_pydantic_include from dispatch.database.core import DbSession from dispatch.database.service import CommonParameters, search_filter_sort_paginate @@ -43,7 +48,7 @@ def get_tasks(common: CommonParameters, include: list[str] = Query([], alias="in return json.loads(TaskPagination(**pagination).json()) -@router.post("", response_model=TaskRead, tags=["tasks"]) +@router.post("", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) def create_task( db_session: DbSession, task_in: TaskCreate, @@ -64,10 +69,11 @@ def create_task( return task -@router.post("/ticket/{task_id}", tags=["tasks"]) +@router.post("/ticket/{task_id}", tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) def create_ticket( db_session: DbSession, task_id: PrimaryKey, + current_user: CurrentUser ): """Creates a ticket for an existing task.""" task = get(db_session=db_session, task_id=task_id) @@ -79,8 +85,8 @@ def create_ticket( return create_task_ticket(task=task, db_session=db_session) -@router.put("/{task_id}", response_model=TaskRead, tags=["tasks"]) -def update_task(db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate): +@router.put("/{task_id}", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) +def update_task(db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate, current_user: CurrentUser): """Updates an existing task.""" task = get(db_session=db_session, task_id=task_id) if not task: @@ -104,8 +110,8 @@ def update_task(db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate) return task -@router.delete("/{task_id}", response_model=None, tags=["tasks"]) -def delete_task(db_session: DbSession, task_id: PrimaryKey): +@router.delete("/{task_id}", response_model=None, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) +def delete_task(db_session: DbSession, task_id: PrimaryKey, current_user: CurrentUser): """Deletes an existing task.""" task = get(db_session=db_session, task_id=task_id) if not task: From d2baa9b083de5bb0180a035712a28837b16d6ee8 Mon Sep 17 00:00:00 2001 From: Alicia Matsumoto Date: Mon, 25 Aug 2025 11:46:36 -0400 Subject: [PATCH 2/7] add new perm --- src/dispatch/task/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dispatch/task/views.py b/src/dispatch/task/views.py index 162f1209ff64..26c9999f6154 100644 --- a/src/dispatch/task/views.py +++ b/src/dispatch/task/views.py @@ -4,7 +4,6 @@ from dispatch.auth.service import CurrentUser from dispatch.auth.permissions import ( - IncidentViewPermission, PermissionsDependency, IncidentViewPermissionForTasks ) From e5102f2dd56d1206683c79301723238c448fc89b Mon Sep 17 00:00:00 2001 From: Alicia Matsumoto Date: Mon, 25 Aug 2025 13:00:39 -0400 Subject: [PATCH 3/7] account for other methods --- src/dispatch/auth/permissions.py | 69 ++++++++++++++++++-------------- src/dispatch/task/views.py | 10 ++--- 2 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/dispatch/auth/permissions.py b/src/dispatch/auth/permissions.py index 611d744f7c73..876613f7d501 100644 --- a/src/dispatch/auth/permissions.py +++ b/src/dispatch/auth/permissions.py @@ -306,36 +306,6 @@ def has_required_permissions( return True -class IncidentViewPermissionForTasks(BasePermission): - """ - Permissions dependency to apply incident view permissions to task-based requests. - """ - def has_required_permissions(self, request: Request) -> bool: - pk = PrimaryKeyModel(id=request.path_params["task_id"]) - current_task = task_service.get(db_session=request.state.db, task_id=pk.id) - - if not current_task or not current_task.incident: - return False - - # minimal object with the attributes required for IncidentViewPermission - incident_request = type('IncidentRequest', (), { - 'path_params': {**request.path_params, 'incident_id': current_task.incident.id}, - 'state': request.state - })() - - # copy necessary request attributes - for attr in ['headers', 'method', 'url', 'query_params']: - if hasattr(request, attr): - setattr(incident_request, attr, getattr(request, attr)) - - return any_permission( - permissions=[IncidentViewPermission], - request=incident_request, - ) - - - - class IncidentEditPermission(BasePermission): def has_required_permissions( self, @@ -365,6 +335,45 @@ def has_required_permissions( request=request, ) +class IncidentEditPermissionForTasks(BasePermission): + """ + Permissions dependency to apply incident edit permissions to task-based requests. + """ + def has_required_permissions(self, request: Request) -> bool: + incident_id = None + # for task creation, retrieve the incident id from the payload + if request.method == 'POST' and hasattr(request, '_body'): + try: + import json + body = json.loads(request._body.decode()) + incident_id = body['incident']['id'] + except (json.JSONDecodeError, KeyError, AttributeError): + log.error("Encountered create_task request without expected incident ID. Cannot properly ascertain incident permissions.") + return False + else: # otherwise, retrieve via the task id + pk = PrimaryKeyModel(id=request.path_params["task_id"]) + current_task = task_service.get(db_session=request.state.db, task_id=pk.id) + if not current_task or not current_task.incident: + return False + incident_id = current_task.incident.id + + print(incident_id) + # minimal object with the attributes required for IncidentViewPermission + incident_request = type('IncidentRequest', (), { + 'path_params': {**request.path_params, 'incident_id': incident_id}, + 'state': request.state + })() + + # copy necessary request attributes + for attr in ['headers', 'method', 'url', 'query_params']: + if hasattr(request, attr): + setattr(incident_request, attr, getattr(request, attr)) + + return any_permission( + permissions=[IncidentEditPermission], + request=incident_request, + ) + class IncidentReporterPermission(BasePermission): def has_required_permissions( diff --git a/src/dispatch/task/views.py b/src/dispatch/task/views.py index 26c9999f6154..7ffcc03bb7ca 100644 --- a/src/dispatch/task/views.py +++ b/src/dispatch/task/views.py @@ -5,7 +5,7 @@ from dispatch.auth.service import CurrentUser from dispatch.auth.permissions import ( PermissionsDependency, - IncidentViewPermissionForTasks + IncidentEditPermissionForTasks ) from dispatch.common.utils.views import create_pydantic_include from dispatch.database.core import DbSession @@ -47,7 +47,7 @@ def get_tasks(common: CommonParameters, include: list[str] = Query([], alias="in return json.loads(TaskPagination(**pagination).json()) -@router.post("", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) +@router.post("", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) def create_task( db_session: DbSession, task_in: TaskCreate, @@ -68,7 +68,7 @@ def create_task( return task -@router.post("/ticket/{task_id}", tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) +@router.post("/ticket/{task_id}", tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) def create_ticket( db_session: DbSession, task_id: PrimaryKey, @@ -84,7 +84,7 @@ def create_ticket( return create_task_ticket(task=task, db_session=db_session) -@router.put("/{task_id}", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) +@router.put("/{task_id}", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) def update_task(db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate, current_user: CurrentUser): """Updates an existing task.""" task = get(db_session=db_session, task_id=task_id) @@ -109,7 +109,7 @@ def update_task(db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate, return task -@router.delete("/{task_id}", response_model=None, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentViewPermissionForTasks]))]) +@router.delete("/{task_id}", response_model=None, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) def delete_task(db_session: DbSession, task_id: PrimaryKey, current_user: CurrentUser): """Deletes an existing task.""" task = get(db_session=db_session, task_id=task_id) From 73d570e99776edfdc1e4e1c68605c4d69341e994 Mon Sep 17 00:00:00 2001 From: Alicia Matsumoto Date: Mon, 25 Aug 2025 13:02:47 -0400 Subject: [PATCH 4/7] conflict --- package-lock.json | 96 ++--------------------------------------------- 1 file changed, 3 insertions(+), 93 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b00efdee37b..a5789a6a16d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,96 +1,6 @@ { - "name": "dispatch-e2e", - "version": "1.0.0", - "lockfileVersion": 3, + "name": "dispatch", + "lockfileVersion": 2, "requires": true, - "packages": { - "": { - "name": "dispatch-e2e", - "version": "1.0.0", - "devDependencies": { - "@playwright/test": "^1.40.0", - "@types/node": "^20.0.0" - } - }, - "node_modules/@playwright/test": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", - "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright": "1.54.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@types/node": { - "version": "20.19.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", - "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/playwright": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", - "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.54.1" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "node_modules/playwright-core": { - "version": "1.54.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", - "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - } - } + "packages": {} } From 8c2929efb118bad94596d9d43c036d45b1229c8e Mon Sep 17 00:00:00 2001 From: Alicia Matsumoto Date: Mon, 25 Aug 2025 13:04:59 -0400 Subject: [PATCH 5/7] revert package --- package-lock.json | 96 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index a5789a6a16d3..9b00efdee37b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,96 @@ { - "name": "dispatch", - "lockfileVersion": 2, + "name": "dispatch-e2e", + "version": "1.0.0", + "lockfileVersion": 3, "requires": true, - "packages": {} + "packages": { + "": { + "name": "dispatch-e2e", + "version": "1.0.0", + "devDependencies": { + "@playwright/test": "^1.40.0", + "@types/node": "^20.0.0" + } + }, + "node_modules/@playwright/test": { + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.1.tgz", + "integrity": "sha512-FS8hQ12acieG2dYSksmLOF7BNxnVf2afRJdCuM1eMSxj6QTSE6G4InGF7oApGgDb65MX7AwMVlIkpru0yZA4Xw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.54.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "20.19.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.9.tgz", + "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.1.tgz", + "integrity": "sha512-peWpSwIBmSLi6aW2auvrUtf2DqY16YYcCMO8rTVx486jKmDTJg7UAhyrraP98GB8BoPURZP8+nxO7TSd4cPr5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.54.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.1.tgz", + "integrity": "sha512-Nbjs2zjj0htNhzgiy5wu+3w09YetDx5pkrpI/kZotDlDUaYk0HVA5xrBVPdow4SAUIlhgKcJeJg4GRKW6xHusA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + } + } } From f2d92b8d6ec8deec576b4605bc602357d0a02fce Mon Sep 17 00:00:00 2001 From: Alicia Matsumoto Date: Mon, 25 Aug 2025 13:24:19 -0400 Subject: [PATCH 6/7] remove debug --- src/dispatch/auth/permissions.py | 28 +++++++++++++-------- src/dispatch/task/views.py | 42 +++++++++++++++++++++----------- 2 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/dispatch/auth/permissions.py b/src/dispatch/auth/permissions.py index 876613f7d501..ad15538881a0 100644 --- a/src/dispatch/auth/permissions.py +++ b/src/dispatch/auth/permissions.py @@ -335,37 +335,45 @@ def has_required_permissions( request=request, ) + class IncidentEditPermissionForTasks(BasePermission): """ Permissions dependency to apply incident edit permissions to task-based requests. """ + def has_required_permissions(self, request: Request) -> bool: incident_id = None # for task creation, retrieve the incident id from the payload - if request.method == 'POST' and hasattr(request, '_body'): + if request.method == "POST" and hasattr(request, "_body"): try: import json + body = json.loads(request._body.decode()) - incident_id = body['incident']['id'] + incident_id = body["incident"]["id"] except (json.JSONDecodeError, KeyError, AttributeError): - log.error("Encountered create_task request without expected incident ID. Cannot properly ascertain incident permissions.") + log.error( + "Encountered create_task request without expected incident ID. Cannot properly ascertain incident permissions." + ) return False - else: # otherwise, retrieve via the task id + else: # otherwise, retrieve via the task id pk = PrimaryKeyModel(id=request.path_params["task_id"]) current_task = task_service.get(db_session=request.state.db, task_id=pk.id) if not current_task or not current_task.incident: return False incident_id = current_task.incident.id - print(incident_id) # minimal object with the attributes required for IncidentViewPermission - incident_request = type('IncidentRequest', (), { - 'path_params': {**request.path_params, 'incident_id': incident_id}, - 'state': request.state - })() + incident_request = type( + "IncidentRequest", + (), + { + "path_params": {**request.path_params, "incident_id": incident_id}, + "state": request.state, + }, + )() # copy necessary request attributes - for attr in ['headers', 'method', 'url', 'query_params']: + for attr in ["headers", "method", "url", "query_params"]: if hasattr(request, attr): setattr(incident_request, attr, getattr(request, attr)) diff --git a/src/dispatch/task/views.py b/src/dispatch/task/views.py index 7ffcc03bb7ca..bc0a437f1ecc 100644 --- a/src/dispatch/task/views.py +++ b/src/dispatch/task/views.py @@ -3,10 +3,7 @@ from dispatch.auth.service import CurrentUser -from dispatch.auth.permissions import ( - PermissionsDependency, - IncidentEditPermissionForTasks -) +from dispatch.auth.permissions import PermissionsDependency, IncidentEditPermissionForTasks from dispatch.common.utils.views import create_pydantic_include from dispatch.database.core import DbSession from dispatch.database.service import CommonParameters, search_filter_sort_paginate @@ -47,7 +44,12 @@ def get_tasks(common: CommonParameters, include: list[str] = Query([], alias="in return json.loads(TaskPagination(**pagination).json()) -@router.post("", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) +@router.post( + "", + response_model=TaskRead, + tags=["tasks"], + dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], +) def create_task( db_session: DbSession, task_in: TaskCreate, @@ -68,12 +70,12 @@ def create_task( return task -@router.post("/ticket/{task_id}", tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) -def create_ticket( - db_session: DbSession, - task_id: PrimaryKey, - current_user: CurrentUser -): +@router.post( + "/ticket/{task_id}", + tags=["tasks"], + dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], +) +def create_ticket(db_session: DbSession, task_id: PrimaryKey, current_user: CurrentUser): """Creates a ticket for an existing task.""" task = get(db_session=db_session, task_id=task_id) if not task: @@ -84,8 +86,15 @@ def create_ticket( return create_task_ticket(task=task, db_session=db_session) -@router.put("/{task_id}", response_model=TaskRead, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) -def update_task(db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate, current_user: CurrentUser): +@router.put( + "/{task_id}", + response_model=TaskRead, + tags=["tasks"], + dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], +) +def update_task( + db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate, current_user: CurrentUser +): """Updates an existing task.""" task = get(db_session=db_session, task_id=task_id) if not task: @@ -109,7 +118,12 @@ def update_task(db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate, return task -@router.delete("/{task_id}", response_model=None, tags=["tasks"], dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))]) +@router.delete( + "/{task_id}", + response_model=None, + tags=["tasks"], + dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], +) def delete_task(db_session: DbSession, task_id: PrimaryKey, current_user: CurrentUser): """Deletes an existing task.""" task = get(db_session=db_session, task_id=task_id) From 88c682f530dfc6299b7c69d4728d83a3d6ff982d Mon Sep 17 00:00:00 2001 From: Alicia Matsumoto Date: Mon, 25 Aug 2025 15:26:52 -0400 Subject: [PATCH 7/7] nits --- src/dispatch/auth/permissions.py | 5 ++--- src/dispatch/task/views.py | 10 +++++----- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/dispatch/auth/permissions.py b/src/dispatch/auth/permissions.py index ad15538881a0..9f68dbb1e06f 100644 --- a/src/dispatch/auth/permissions.py +++ b/src/dispatch/auth/permissions.py @@ -1,5 +1,6 @@ import logging from abc import ABC, abstractmethod +import json from fastapi import HTTPException from starlette.requests import Request @@ -336,7 +337,7 @@ def has_required_permissions( ) -class IncidentEditPermissionForTasks(BasePermission): +class IncidentTaskCreateEditPermission(BasePermission): """ Permissions dependency to apply incident edit permissions to task-based requests. """ @@ -346,8 +347,6 @@ def has_required_permissions(self, request: Request) -> bool: # for task creation, retrieve the incident id from the payload if request.method == "POST" and hasattr(request, "_body"): try: - import json - body = json.loads(request._body.decode()) incident_id = body["incident"]["id"] except (json.JSONDecodeError, KeyError, AttributeError): diff --git a/src/dispatch/task/views.py b/src/dispatch/task/views.py index bc0a437f1ecc..843bb95b81c2 100644 --- a/src/dispatch/task/views.py +++ b/src/dispatch/task/views.py @@ -3,7 +3,7 @@ from dispatch.auth.service import CurrentUser -from dispatch.auth.permissions import PermissionsDependency, IncidentEditPermissionForTasks +from dispatch.auth.permissions import PermissionsDependency, IncidentTaskCreateEditPermission from dispatch.common.utils.views import create_pydantic_include from dispatch.database.core import DbSession from dispatch.database.service import CommonParameters, search_filter_sort_paginate @@ -48,7 +48,7 @@ def get_tasks(common: CommonParameters, include: list[str] = Query([], alias="in "", response_model=TaskRead, tags=["tasks"], - dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], + dependencies=[Depends(PermissionsDependency([IncidentTaskCreateEditPermission]))], ) def create_task( db_session: DbSession, @@ -73,7 +73,7 @@ def create_task( @router.post( "/ticket/{task_id}", tags=["tasks"], - dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], + dependencies=[Depends(PermissionsDependency([IncidentTaskCreateEditPermission]))], ) def create_ticket(db_session: DbSession, task_id: PrimaryKey, current_user: CurrentUser): """Creates a ticket for an existing task.""" @@ -90,7 +90,7 @@ def create_ticket(db_session: DbSession, task_id: PrimaryKey, current_user: Curr "/{task_id}", response_model=TaskRead, tags=["tasks"], - dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], + dependencies=[Depends(PermissionsDependency([IncidentTaskCreateEditPermission]))], ) def update_task( db_session: DbSession, task_id: PrimaryKey, task_in: TaskUpdate, current_user: CurrentUser @@ -122,7 +122,7 @@ def update_task( "/{task_id}", response_model=None, tags=["tasks"], - dependencies=[Depends(PermissionsDependency([IncidentEditPermissionForTasks]))], + dependencies=[Depends(PermissionsDependency([IncidentTaskCreateEditPermission]))], ) def delete_task(db_session: DbSession, task_id: PrimaryKey, current_user: CurrentUser): """Deletes an existing task."""