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
2 changes: 2 additions & 0 deletions apps/backend/lambdas/projects/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ Lambda for managing projects.
| GET | /health | Health check |
| GET | /dashboard | |
| GET | /projects/{id}/members | |
| GET | /projects/assignable-staff | |
| GET | /projects | |
| GET | /projects/{id}/overview | |
| GET | /projects/{id}/donors | |
| GET | /projects/{id} | |
| PUT | /projects/{id} | |
Expand Down
32 changes: 32 additions & 0 deletions apps/backend/lambdas/projects/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,38 @@ export async function canEditProject(
}
}

/**
* Gates the staff picker's user list.
*
* Creating a project is admin-only, but editing one is open to its Directors,
* so a Director must be able to read the roster to assign staff. This is
* deliberately narrower than `GET /users` (ADMIN-only) and returns only the
* fields the picker renders — not the full user row.
*/
export async function canListAssignableStaff(userId: number): Promise<boolean> {
try {
const user = await db
.selectFrom('branch.users')
.where('user_id', '=', userId)
.select('is_admin')
.executeTakeFirst();

if (user?.is_admin) return true;

const membership = await db
.selectFrom('branch.project_memberships')
.where('user_id', '=', userId)
.where('role', 'in', ['Director', 'Admin'])
.select('membership_id')
.executeTakeFirst();

return !!membership;
} catch (error) {
console.error('Error checking staff-list access:', error);
return false;
}
}

export async function canCreateProject(userId: number): Promise<boolean> {
try {
const user = await db
Expand Down
Loading
Loading