Commit eae4ed0f authored by Paulo Avila's avatar Paulo Avila
Browse files

Inclusão de travas de seguraça para pastas

parent f5fa1ac0
...@@ -103,17 +103,21 @@ export default async function MemberFolderPage({ ...@@ -103,17 +103,21 @@ export default async function MemberFolderPage({
</p> </p>
</div> </div>
</div> </div>
<TaskDialog {(role === "admin" || !member || member.UserId === user.id) && (
workspaceId={workspaceId} <TaskDialog
members={members} workspaceId={workspaceId}
defaultAssigneeId={member?.UserId} members={members}
trigger={ defaultAssigneeId={member?.UserId}
<Button> currentUserId={user.id}
<Plus className="mr-2 h-4 w-4" /> isAdmin={role === "admin"}
Nova tarefa trigger={
</Button> <Button>
} <Plus className="mr-2 h-4 w-4" />
/> Nova tarefa
</Button>
}
/>
)}
</div> </div>
<StatusSummary tasks={tasks} /> <StatusSummary tasks={tasks} />
......
...@@ -81,6 +81,8 @@ export default async function WorkspacePage({ ...@@ -81,6 +81,8 @@ export default async function WorkspacePage({
<TaskDialog <TaskDialog
workspaceId={workspaceId} workspaceId={workspaceId}
members={members} members={members}
currentUserId={user.id}
isAdmin={isAdmin}
trigger={ trigger={
<Button> <Button>
<Plus className="mr-2 h-4 w-4" /> <Plus className="mr-2 h-4 w-4" />
...@@ -104,6 +106,8 @@ export default async function WorkspacePage({ ...@@ -104,6 +106,8 @@ export default async function WorkspacePage({
tasks={tasks} tasks={tasks}
members={members} members={members}
workspaceId={workspaceId} workspaceId={workspaceId}
currentUserId={user.id}
isAdmin={isAdmin}
/> />
</TabsContent> </TabsContent>
<TabsContent value="list" className="mt-4"> <TabsContent value="list" className="mt-4">
......
...@@ -65,6 +65,8 @@ export function TaskBoard({ ...@@ -65,6 +65,8 @@ export function TaskBoard({
members={members} members={members}
task={toFormValues(task)} task={toFormValues(task)}
subtasks={subtasksByTask[task.Id] ?? []} subtasks={subtasksByTask[task.Id] ?? []}
currentUserId={currentUserId}
isAdmin={isAdmin}
trigger={ trigger={
<Button <Button
variant="ghost" variant="ghost"
......
...@@ -48,10 +48,14 @@ export function TaskByPerson({ ...@@ -48,10 +48,14 @@ export function TaskByPerson({
tasks, tasks,
members, members,
workspaceId, workspaceId,
currentUserId,
isAdmin,
}: { }: {
tasks: Task[]; tasks: Task[];
members: WorkspaceMember[]; members: WorkspaceMember[];
workspaceId: number; workspaceId: number;
currentUserId: number;
isAdmin: boolean;
subtasksByTask?: Record<number, Subtask[]>; subtasksByTask?: Record<number, Subtask[]>;
}) { }) {
const groups: PersonGroup[] = members.map((member) => ({ const groups: PersonGroup[] = members.map((member) => ({
...@@ -155,11 +159,14 @@ export function TaskByPerson({ ...@@ -155,11 +159,14 @@ export function TaskByPerson({
<ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground" /> <ChevronRight className="h-4 w-4 shrink-0 text-muted-foreground" />
</Link> </Link>
{group.userId !== null && ( {group.userId !== null &&
(isAdmin || group.userId === currentUserId) && (
<TaskDialog <TaskDialog
workspaceId={workspaceId} workspaceId={workspaceId}
members={members} members={members}
defaultAssigneeId={group.userId} defaultAssigneeId={group.userId}
currentUserId={currentUserId}
isAdmin={isAdmin}
trigger={ trigger={
<Button <Button
variant="ghost" variant="ghost"
......
...@@ -66,6 +66,8 @@ export function TaskDialog({ ...@@ -66,6 +66,8 @@ export function TaskDialog({
task, task,
subtasks, subtasks,
defaultAssigneeId, defaultAssigneeId,
currentUserId,
isAdmin,
trigger, trigger,
}: { }: {
workspaceId: number; workspaceId: number;
...@@ -73,8 +75,17 @@ export function TaskDialog({ ...@@ -73,8 +75,17 @@ export function TaskDialog({
task?: TaskFormValues; task?: TaskFormValues;
subtasks?: Subtask[]; subtasks?: Subtask[];
defaultAssigneeId?: number; defaultAssigneeId?: number;
currentUserId: number;
isAdmin: boolean;
trigger: ReactNode; trigger: ReactNode;
}) { }) {
// Membros comuns só podem atribuir tarefas a si mesmos; mantém o
// responsável atual visível em modo edição.
const selectableMembers = isAdmin
? members
: members.filter(
(m) => m.UserId === currentUserId || m.UserId === task?.assigneeId
);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const action = task const action = task
? updateTaskAction.bind(null, task.id) ? updateTaskAction.bind(null, task.id)
...@@ -175,7 +186,7 @@ export function TaskDialog({ ...@@ -175,7 +186,7 @@ export function TaskDialog({
<SelectValue placeholder="Sem responsável" /> <SelectValue placeholder="Sem responsável" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{members.map((m) => ( {selectableMembers.map((m) => (
<SelectItem key={m.UserId} value={String(m.UserId)}> <SelectItem key={m.UserId} value={String(m.UserId)}>
{m.Name} {m.Name}
</SelectItem> </SelectItem>
......
...@@ -135,6 +135,8 @@ export function TaskTable({ ...@@ -135,6 +135,8 @@ export function TaskTable({
members={members} members={members}
task={toFormValues(task)} task={toFormValues(task)}
subtasks={subtasksByTask[task.Id] ?? []} subtasks={subtasksByTask[task.Id] ?? []}
currentUserId={currentUserId}
isAdmin={isAdmin}
trigger={ trigger={
<Button <Button
variant="ghost" variant="ghost"
......
...@@ -12,7 +12,7 @@ import { ...@@ -12,7 +12,7 @@ import {
import { import {
assertCanEditTask, assertCanEditTask,
assertWorkspaceAdmin, assertWorkspaceAdmin,
assertWorkspaceMember, getWorkspaceRole,
requireUser, requireUser,
} from "./authz"; } from "./authz";
import * as data from "./data"; import * as data from "./data";
...@@ -265,7 +265,20 @@ export async function createTaskAction( ...@@ -265,7 +265,20 @@ export async function createTaskAction(
if (!parsed.success) return { error: firstError(parsed.error) }; if (!parsed.success) return { error: firstError(parsed.error) };
try { try {
await assertWorkspaceMember(parsed.data.workspaceId, user); const role = await getWorkspaceRole(parsed.data.workspaceId, user.id);
if (!role) {
return { error: "Você não tem acesso a este espaço de trabalho." };
}
// Membros comuns só criam tarefas para si mesmos (ou sem responsável).
if (
role !== "admin" &&
parsed.data.assigneeId !== undefined &&
parsed.data.assigneeId !== user.id
) {
return {
error: "Apenas admins podem criar tarefas para outra pessoa.",
};
}
await data.createTask( await data.createTask(
{ {
workspaceId: parsed.data.workspaceId, workspaceId: parsed.data.workspaceId,
...@@ -303,6 +316,18 @@ export async function updateTaskAction( ...@@ -303,6 +316,18 @@ export async function updateTaskAction(
const task = await data.getTask(taskId); const task = await data.getTask(taskId);
if (!task) return { error: "Tarefa não encontrada." }; if (!task) return { error: "Tarefa não encontrada." };
await assertCanEditTask(task, user); await assertCanEditTask(task, user);
// Membros comuns não podem transferir a tarefa para outra pessoa.
const role = await getWorkspaceRole(task.WorkspaceId, user.id);
if (
role !== "admin" &&
parsed.data.assigneeId !== undefined &&
parsed.data.assigneeId !== user.id &&
parsed.data.assigneeId !== task.AssigneeId
) {
return {
error: "Apenas admins podem atribuir tarefas a outra pessoa.",
};
}
await data.updateTask(taskId, { await data.updateTask(taskId, {
workspaceId: task.WorkspaceId, workspaceId: task.WorkspaceId,
title: parsed.data.title, title: parsed.data.title,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment