Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion components/sessioncard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ interface SessionModalProps {
onUpdate?: () => void;
workspaceMembers: any[];
canManage: boolean;
canCancel: boolean;
canAddNotes?: boolean;
sessionColors?: SessionColors;
colorsReady?: boolean | undefined;
Expand All @@ -155,6 +156,7 @@ const SessionModal: React.FC<SessionModalProps> = ({
onUpdate,
workspaceMembers,
canManage,
canCancel,
canAddNotes,
sessionColors,
colorsReady,
Expand Down Expand Up @@ -726,7 +728,7 @@ const SessionModal: React.FC<SessionModalProps> = ({
</SessionInset>
)}

{canManage && !session.cancelled && (
{(canManage || canCancel) && !session.cancelled && (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'sessions/.*/cancel|canCancelSession|sessions_[^"]*_cancel' .

Repository: PlanetaryOrbit/orbit

Length of output: 917


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== components/sessioncard.tsx (relevant ranges) =="
sed -n '180,280p' components/sessioncard.tsx
echo
sed -n '700,770p' components/sessioncard.tsx
echo
echo "== utils/sessionPermissions.ts =="
cat -n utils/sessionPermissions.ts
echo
echo "== API route search =="
rg -n 'cancelled|uncancel|restore|canCancelSession|canManageSession|authorize|permission' pages components utils api -g '!**/node_modules/**'

Repository: PlanetaryOrbit/orbit

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== pages/api/workspace/[id]/sessions/[sid]/cancel.ts (full) =="
cat -n pages/api/workspace/[id]/sessions/[sid]/cancel.ts

echo
echo "== permission wrapper and session permission helpers =="
sed -n '1,260p' utils/permissionsManager.ts 2>/dev/null || true
sed -n '1,220p' utils/sessionPermissions.ts

Repository: PlanetaryOrbit/orbit

Length of output: 14145


Hide the Uncancel action behind manage access. The DELETE route already requires sessions_*_manage, but the button is still rendered for any cancelled session. That leaves users with cancel-only access at a dead end. Either gate this control on canManage or add a separate uncancel permission and enforce it server-side too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/sessioncard.tsx` at line 731, Update the session action condition
near the canManage/canCancel check so the Uncancel control is rendered only when
canManage is true, while preserving the existing cancelled-state guard and
cancel-only behavior.

<SessionInset>
{!isCancelExpanded ? (
<button
Expand Down
1 change: 1 addition & 0 deletions components/settings/permissions/roles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const RolesManager: FC<Props> = ({ roles, setRoles, grouproles }) => {
[`Create Scheduled ${typeCapitalized}`]: `sessions_${type}_scheduled`,
[`Manage ${typeCapitalized} Sessions`]: `sessions_${type}_manage`,
[`Add Notes to ${typeCapitalized} Sessions`]: `sessions_${type}_notes`,
[`Cancel ${typeCapitalized} Sessions`]: `sessions_${type}_cancel`,
};
});

Expand Down
3 changes: 2 additions & 1 deletion components/topbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const Topbar: NextPage = () => {
try {
const res = await axios.get("/api/user/sessions");
setSessions(res.data.sessions || []);
toast.success("Sessions refreshed")
} catch {
toast.error("Failed to load sessions");
} finally {
Expand Down Expand Up @@ -307,7 +308,7 @@ const Topbar: NextPage = () => {
Active sessions
</p>
<button
onClick={() => { fetchSessions(); toast.success("Sessions refreshed"); }}
onClick={fetchSessions}
className="rounded-lg p-1 text-zinc-400 transition-colors hover:bg-zinc-100 dark:hover:bg-zinc-800"
>
<IconRefresh className="h-4 w-4" />
Expand Down
3 changes: 2 additions & 1 deletion pages/workspace/[id]/sessions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { withPermissionCheckSsr } from "@/utils/permissionsManager";
import toast from "react-hot-toast";
import SessionTemplate from "@/components/sessioncard";
import PatternEditDialog from "@/components/sessionpatterns";
import { canCreateAnySession, canAddNotes, canManageSession } from "@/utils/sessionPermissions";
import { canCreateAnySession, canAddNotes, canManageSession, canCancelSession } from "@/utils/sessionPermissions";
import { AuthenticatedRequest } from "@/lib/withAuth";
import clsx from "clsx";
import {
Expand Down Expand Up @@ -1187,6 +1187,7 @@ const Home: pageWithLayout<pageProps> = (props) => {
}}
workspaceMembers={workspaceMembers}
canManage={canManageSession(workspace.yourPermission || [], selectedSession?.type)}
canCancel={canCancelSession(workspace.yourPermission || [], selectedSession?.type)}
canAddNotes={canAddNotes(workspace.yourPermission || [], selectedSession?.type)}
sessionColors={sessionColors}
colorsReady={!colorsLoading}
Expand Down
6 changes: 5 additions & 1 deletion utils/sessionPermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ export type SessionType = 'shift' | 'training' | 'event' | 'other';
export function hasSessionPermission(
permissions: string[],
sessionType: string | null | undefined,
action: 'see' | 'assign' | 'claim' | 'host' | 'unscheduled' | 'scheduled' | 'manage' | 'notes'
action: 'see' | 'assign' | 'claim' | 'host' | 'unscheduled' | 'scheduled' | 'manage' | 'notes' | 'cancel'
): boolean {
if (permissions.includes('admin')) return true;

Expand Down Expand Up @@ -41,6 +41,10 @@ export function canManageSession(permissions: string[], sessionType: string | nu
return hasSessionPermission(permissions, sessionType, 'manage');
}

export function canCancelSession(permissions: string[], sessionType: string | null | undefined): boolean {
return hasSessionPermission(permissions, sessionType, 'cancel');
}

export function canAddNotes(permissions: string[], sessionType: string | null | undefined): boolean {
return hasSessionPermission(permissions, sessionType, 'notes');
}
Expand Down
Loading