feat: add canOperateReports and canFlagReportContent functions for report management
This commit is contained in:
@@ -113,6 +113,11 @@ bool canManageReports(const UserContext &user)
|
||||
return isAdmin(user) || hasPermission(user, "reports.manage");
|
||||
}
|
||||
|
||||
bool canOperateReports(const UserContext &user)
|
||||
{
|
||||
return !isAdmin(user) && canManageReports(user);
|
||||
}
|
||||
|
||||
bool isAdmin(const UserContext &user)
|
||||
{
|
||||
return hasRole(user, "admin") || hasPermission(user, "admin.manage");
|
||||
|
||||
@@ -23,6 +23,7 @@ std::optional<UserContext> authenticate(const drogon::HttpRequestPtr &request);
|
||||
bool hasRole(const UserContext &user, const std::string &role);
|
||||
bool hasPermission(const UserContext &user, const std::string &permission);
|
||||
bool canManageReports(const UserContext &user);
|
||||
bool canOperateReports(const UserContext &user);
|
||||
bool isAdmin(const UserContext &user);
|
||||
bool mustChangePassword(const UserContext &user);
|
||||
} // namespace visione::auth
|
||||
|
||||
+4
-3
@@ -32,13 +32,14 @@ Authorization: Bearer <token>
|
||||
- `GET /reports/{id}/intervention-summary`: operator-facing report summary with
|
||||
citizen contact data, media and latest messages.
|
||||
- `PATCH /reports/{id}/status`: update status.
|
||||
- `POST /reports/{id}/take`: operator/admin takes an open report.
|
||||
- `POST /reports/{id}/take`: assigned-queue operator takes an open report.
|
||||
- `POST /reports/{id}/messages`: write a report message.
|
||||
- `GET /reports/{id}/messages`: list report messages.
|
||||
- `POST /reports/{id}/messages/{message_id}/flags`: flag a message for moderation.
|
||||
- `POST /reports/{id}/messages/{message_id}/flags`: assigned operator flags a
|
||||
report message for moderation.
|
||||
- `POST /reports/{id}/assignments`: assign a team.
|
||||
- `POST /reports/{id}/feedback`: add citizen feedback.
|
||||
- `POST /reports/{id}/flags`: flag a report for moderation.
|
||||
- `POST /reports/{id}/flags`: assigned operator flags a report for moderation.
|
||||
- `POST /reports/{id}/media`: upload up to five multipart media files. Supported
|
||||
types are JPEG, PNG, WebP, MP4, WebM and QuickTime.
|
||||
|
||||
|
||||
@@ -8,7 +8,8 @@ assign and resolve interventions while preserving history and basic analytics.
|
||||
- `citizen`: registers, requests OTP login, creates reports, follows own reports,
|
||||
sends messages and leaves feedback after resolution.
|
||||
- `operator`: views reports, takes open reports, assigns teams, updates managed
|
||||
reports and messages citizens.
|
||||
reports, messages citizens and flags abusive/fake citizen content on managed
|
||||
reports.
|
||||
- `admin`: manages users, roles, reference data, teams, areas and all reports.
|
||||
|
||||
The system admin seed must change password before using protected operational
|
||||
@@ -25,7 +26,7 @@ or administrative APIs.
|
||||
|
||||
Valid milestone 2 transitions:
|
||||
|
||||
- `OPEN -> UNDER_REVIEW` when an operator/admin takes the report.
|
||||
- `OPEN -> UNDER_REVIEW` when an operator takes the report.
|
||||
- `UNDER_REVIEW -> ASSIGNED` when a team is assigned.
|
||||
- `ASSIGNED -> IN_PROGRESS`.
|
||||
- `IN_PROGRESS -> RESOLVED`.
|
||||
|
||||
@@ -3056,7 +3056,7 @@
|
||||
},
|
||||
"/api/v1/reports/{id}/flags": {
|
||||
"post": {
|
||||
"description": "Creates a moderation flag for a report.",
|
||||
"description": "Creates a moderation flag for a report by assigned operator only.",
|
||||
"operationId": "ReportController.flagReport",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -3313,7 +3313,7 @@
|
||||
},
|
||||
"/api/v1/reports/{id}/messages/{message_id}/flags": {
|
||||
"post": {
|
||||
"description": "Creates a moderation flag for a report message.",
|
||||
"description": "Creates a moderation flag for a report message by assigned operator only.",
|
||||
"operationId": "ReportController.flagMessage",
|
||||
"parameters": [
|
||||
{
|
||||
@@ -3436,7 +3436,7 @@
|
||||
},
|
||||
"/api/v1/reports/{id}/take": {
|
||||
"post": {
|
||||
"description": "Assigns an open report to the current operator.",
|
||||
"description": "Assigns an open report to current non-admin operator.",
|
||||
"operationId": "ReportController.takeReport",
|
||||
"parameters": [
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <drogon/orm/Field.h>
|
||||
#include <drogon/orm/Result.h>
|
||||
|
||||
#include <json/json.h>
|
||||
|
||||
+21
-12
@@ -109,6 +109,20 @@ bool canModifyReport(const auth::UserContext &user, long long reportId)
|
||||
return !result.empty();
|
||||
}
|
||||
|
||||
bool canFlagReportContent(const auth::UserContext &user, long long reportId)
|
||||
{
|
||||
if (!auth::canOperateReports(user))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto result = drogon::app().getDbClient()->execSqlSync(
|
||||
"SELECT id FROM reports WHERE id = ? AND assigned_operator_id = ?",
|
||||
reportId,
|
||||
user.id);
|
||||
return !result.empty();
|
||||
}
|
||||
|
||||
bool ownsReport(const auth::UserContext &user, long long reportId)
|
||||
{
|
||||
const auto result = drogon::app().getDbClient()->execSqlSync(
|
||||
@@ -616,7 +630,7 @@ Json::Value ReportService::updateStatus(const auth::UserContext &user,
|
||||
Json::Value ReportService::takeReport(const auth::UserContext &user,
|
||||
long long reportId)
|
||||
{
|
||||
if (!auth::canManageReports(user))
|
||||
if (!auth::canOperateReports(user))
|
||||
{
|
||||
return failure("forbidden");
|
||||
}
|
||||
@@ -737,6 +751,10 @@ Json::Value ReportService::assignTeam(const auth::UserContext &user,
|
||||
return failure("team_not_found");
|
||||
}
|
||||
|
||||
db->execSqlSync(
|
||||
"UPDATE assignments SET ended_at = NOW() "
|
||||
"WHERE report_id = ? AND ended_at IS NULL",
|
||||
reportId);
|
||||
db->execSqlSync(
|
||||
"INSERT INTO assignments (report_id, team_id, assigned_by_user_id) "
|
||||
"VALUES (?, ?, ?)",
|
||||
@@ -814,16 +832,7 @@ Json::Value ReportService::flagReport(const auth::UserContext &user,
|
||||
return failure("invalid_flag");
|
||||
}
|
||||
|
||||
const auto owner = reportOwner(reportId);
|
||||
if (!owner)
|
||||
{
|
||||
return failure("report_not_found");
|
||||
}
|
||||
if (*owner == user.id)
|
||||
{
|
||||
return failure("cannot_flag_own_report");
|
||||
}
|
||||
if (!canAccessReport(user, reportId))
|
||||
if (!canFlagReportContent(user, reportId))
|
||||
{
|
||||
return failure("forbidden");
|
||||
}
|
||||
@@ -853,7 +862,7 @@ Json::Value ReportService::flagMessage(const auth::UserContext &user,
|
||||
{
|
||||
return failure("invalid_flag");
|
||||
}
|
||||
if (!canInteractWithReport(user, reportId))
|
||||
if (!canFlagReportContent(user, reportId))
|
||||
{
|
||||
return failure("forbidden");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user