Spaces:
Sleeping
Sleeping
File size: 8,049 Bytes
57a889c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | // ---------------------------------------------------------------------------
// OAuth 2.1 scope definitions for TREK MCP
// ---------------------------------------------------------------------------
export const SCOPES = {
TRIPS_READ: 'trips:read',
TRIPS_WRITE: 'trips:write',
TRIPS_DELETE: 'trips:delete',
TRIPS_SHARE: 'trips:share',
PLACES_READ: 'places:read',
PLACES_WRITE: 'places:write',
ATLAS_READ: 'atlas:read',
ATLAS_WRITE: 'atlas:write',
PACKING_READ: 'packing:read',
PACKING_WRITE: 'packing:write',
TODOS_READ: 'todos:read',
TODOS_WRITE: 'todos:write',
BUDGET_READ: 'budget:read',
BUDGET_WRITE: 'budget:write',
RESERVATIONS_READ: 'reservations:read',
RESERVATIONS_WRITE: 'reservations:write',
COLLAB_READ: 'collab:read',
COLLAB_WRITE: 'collab:write',
NOTIFICATIONS_READ: 'notifications:read',
NOTIFICATIONS_WRITE: 'notifications:write',
VACAY_READ: 'vacay:read',
VACAY_WRITE: 'vacay:write',
GEO_READ: 'geo:read',
WEATHER_READ: 'weather:read',
JOURNEY_READ: 'journey:read',
JOURNEY_WRITE: 'journey:write',
JOURNEY_SHARE: 'journey:share',
} as const;
export type Scope = typeof SCOPES[keyof typeof SCOPES];
export const ALL_SCOPES: Scope[] = Object.values(SCOPES) as Scope[];
export interface ScopeInfo {
label: string;
description: string;
group: string;
}
export const SCOPE_INFO: Record<Scope, ScopeInfo> = {
'trips:read': { label: 'View trips & itineraries', description: 'Read trips, days, day notes, and members', group: 'Trips' },
'trips:write': { label: 'Edit trips & itineraries', description: 'Create and update trips, days, notes, and manage members', group: 'Trips' },
'trips:delete': { label: 'Delete trips', description: 'Permanently delete entire trips — this action is irreversible', group: 'Trips' },
'trips:share': { label: 'Manage share links', description: 'Create, update, and revoke public share links for trips', group: 'Trips' },
'places:read': { label: 'View places & map data', description: 'Read places, day assignments, tags, and categories', group: 'Places' },
'places:write': { label: 'Manage places', description: 'Create, update, and delete places, assignments, and tags', group: 'Places' },
'atlas:read': { label: 'View Atlas', description: 'Read visited countries, regions, and bucket list', group: 'Atlas' },
'atlas:write': { label: 'Manage Atlas', description: 'Mark countries and regions visited, manage bucket list', group: 'Atlas' },
'packing:read': { label: 'View packing lists', description: 'Read packing items, bags, and category assignees', group: 'Packing' },
'packing:write': { label: 'Manage packing lists', description: 'Add, update, delete, toggle, and reorder packing items and bags', group: 'Packing' },
'todos:read': { label: 'View to-do lists', description: 'Read trip to-do items and category assignees', group: 'To-dos' },
'todos:write': { label: 'Manage to-do lists', description: 'Create, update, toggle, delete, and reorder to-do items', group: 'To-dos' },
'budget:read': { label: 'View budget', description: 'Read budget items and expense breakdown', group: 'Budget' },
'budget:write': { label: 'Manage budget', description: 'Create, update, and delete budget items', group: 'Budget' },
'reservations:read': { label: 'View reservations', description: 'Read reservations and accommodation details', group: 'Reservations' },
'reservations:write': { label: 'Manage reservations', description: 'Create, update, delete, and reorder reservations', group: 'Reservations' },
'collab:read': { label: 'View collaboration', description: 'Read collab notes, polls, and messages', group: 'Collaboration' },
'collab:write': { label: 'Manage collaboration', description: 'Create, update, and delete collab notes, polls, and messages', group: 'Collaboration' },
'notifications:read': { label: 'View notifications', description: 'Read in-app notifications and unread counts', group: 'Notifications' },
'notifications:write': { label: 'Manage notifications', description: 'Mark notifications as read and respond to them', group: 'Notifications' },
'vacay:read': { label: 'View vacation plans', description: 'Read vacation planning data, entries, and stats', group: 'Vacation' },
'vacay:write': { label: 'Manage vacation plans', description: 'Create and manage vacation entries, holidays, and team plans', group: 'Vacation' },
'geo:read': { label: 'Maps & geocoding', description: 'Search locations, resolve map URLs, and reverse geocode coordinates', group: 'Geo' },
'weather:read': { label: 'Weather forecasts', description: 'Fetch weather forecasts for trip locations and dates', group: 'Weather' },
'journey:read': { label: 'View journeys', description: 'Read journeys, entries, and contributor list', group: 'Journey' },
'journey:write': { label: 'Manage journeys', description: 'Create, update, and delete journeys and their entries', group: 'Journey' },
'journey:share': { label: 'Manage journey links', description: 'Create, update, and revoke public share links for journeys', group: 'Journey' },
};
// ---------------------------------------------------------------------------
// Scope enforcement helpers
// null scopes = static trek_ token = full access
// ---------------------------------------------------------------------------
/** trips:read OR trips:write OR trips:delete OR trips:share all grant read access to trips */
export function canReadTrips(scopes: string[] | null): boolean {
if (!scopes) return true;
return scopes.some(s => s === 'trips:read' || s === 'trips:write' || s === 'trips:delete' || s === 'trips:share');
}
/** group:write grants write access; for trips canReadTrips handles read */
export function canWrite(scopes: string[] | null, group: string): boolean {
if (!scopes) return true;
return scopes.includes(`${group}:write`);
}
/** group:read OR group:write grant read access */
export function canRead(scopes: string[] | null, group: string): boolean {
if (!scopes) return true;
return scopes.some(s => s === `${group}:read` || s === `${group}:write`);
}
/** trips:delete is a separate scope from trips:write */
export function canDeleteTrips(scopes: string[] | null): boolean {
if (!scopes) return true;
return scopes.includes('trips:delete');
}
/** trips:share is a separate scope for managing public share links */
export function canShareTrips(scopes: string[] | null): boolean {
if (!scopes) return true;
return scopes.includes('trips:share');
}
/** journey:share is a separate scope for managing public share links for journeys */
export function canShareJourneys(scopes: string[] | null): boolean {
if (!scopes) return true;
return scopes.includes('journey:share');
}
export function validateScopes(requestedScopes: string[]): { valid: boolean; invalid: string[] } {
const invalid = requestedScopes.filter(s => !ALL_SCOPES.includes(s as Scope));
return { valid: invalid.length === 0, invalid };
}
|