feat: small fixes for roles and users

This commit is contained in:
biersoeckli
2025-03-08 16:03:52 +00:00
parent 48575b2e45
commit 999db2839b
9 changed files with 52 additions and 19 deletions

View File

@@ -25,15 +25,15 @@ export default async function ProjectPage() {
const session = await getAuthUserSession();
const data = await projectService.getAllProjects();
const relevantProjectsForUser = data.filter((project) =>
project.apps.some((app) => RoleUtils.sessionHasReadAccessForApp(session, app.id)));
RoleUtils.sessionHasReadAccessToProject(session, project));
return (
<div className="flex-1 space-y-4 pt-6">
<div className="flex gap-4">
<h2 className="text-3xl font-bold tracking-tight flex-1">Projects</h2>
<EditProjectDialog>
{RoleUtils.isAdmin(session) && <EditProjectDialog>
<Button><Plus /> Create Project</Button>
</EditProjectDialog>
</EditProjectDialog>}
</div>
<ProjectsTable data={relevantProjectsForUser} />
<ProjectsBreadcrumbs />

View File

@@ -20,7 +20,7 @@ import appService from "@/server/services/app.service";
export default async function UsersAndRolesPage() {
await getAdminUserSession();
const session = await getAdminUserSession();
const users = await userService.getAllUsers();
const roles = await roleService.getAll();
const allApps = await appService.getAll();
@@ -36,10 +36,10 @@ export default async function UsersAndRolesPage() {
<Tabs defaultValue="users" >
<TabsList className="">
<TabsTrigger className="px-8 gap-1.5" value="users"><CircleUser className="w-3.5 h-3.5" /> Users</TabsTrigger>
<TabsTrigger className="px-8 gap-1.5" value="roles"><UserRoundCog className="w-3.5 h-3.5"/> Roles</TabsTrigger>
<TabsTrigger className="px-8 gap-1.5" value="roles"><UserRoundCog className="w-3.5 h-3.5" /> Roles</TabsTrigger>
</TabsList>
<TabsContent value="users">
<UsersTable users={users} roles={roles} />
<UsersTable session={session} users={users} roles={roles} />
</TabsContent>
<TabsContent value="roles">
<RolesTable apps={allApps} roles={roles} />

View File

@@ -4,6 +4,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/u
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
@@ -158,14 +159,21 @@ export default function RoleEditOverlay({ children, role, apps }: {
control={form.control}
name="canCreateNewApps"
render={({ field }) => (
<FormItem className="flex gap-4">
<FormLabel className="pt-2">Can create new apps</FormLabel>
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
Can create new apps
</FormLabel>
<FormDescription>
If enabled, users with this role can create new apps. Projects can only be created by admins.
</FormDescription>
</div>
</FormItem>
)}
/>
@@ -174,14 +182,21 @@ export default function RoleEditOverlay({ children, role, apps }: {
control={form.control}
name="canAccessBackups"
render={({ field }) => (
<FormItem className="flex gap-4">
<FormLabel className="pt-2">Can access backups</FormLabel>
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md border p-4">
<FormControl>
<Checkbox
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<div className="space-y-1 leading-none">
<FormLabel>
Can access backups
</FormLabel>
<FormDescription>
If enabled, users can access the backups page and download backups from all apps.
</FormDescription>
</div>
</FormItem>
)}
/>
@@ -234,7 +249,7 @@ export default function RoleEditOverlay({ children, role, apps }: {
</div>
</ScrollArea>
</DialogContent>
</Dialog>
</Dialog >
</>
)
}

View File

@@ -12,10 +12,12 @@ import { UserExtended } from "@/shared/model/user-extended.model";
import UserEditOverlay from "./user-edit-overlay";
import { deleteUser } from "./actions";
import { RoleExtended } from "@/shared/model/role-extended.model.ts";
import { UserSession } from "@/shared/model/sim-session.model";
export default function UsersTable({ users, roles }: {
export default function UsersTable({ users, roles, session }: {
users: UserExtended[];
roles: RoleExtended[];
session: UserSession;
}) {
const { openConfirmDialog: openDialog } = useConfirmDialog();
@@ -44,12 +46,13 @@ export default function UsersTable({ users, roles }: {
<>
<div className="flex">
<div className="flex-1"></div>
<UserEditOverlay user={item} roles={roles}>
{session.email !== item.email && <><UserEditOverlay user={item} roles={roles}>
<Button variant="ghost"><EditIcon /></Button>
</UserEditOverlay>
<Button variant="ghost" onClick={() => asyncDeleteItem(item.id)}>
<TrashIcon />
</Button>
<Button variant="ghost" onClick={() => asyncDeleteItem(item.id)}>
<TrashIcon />
</Button>
</>}
</div>
</>}
/>

View File

@@ -165,11 +165,11 @@ export function SidebarCient({
<span>Projects</span>
</Link>
</SidebarMenuButton>
<EditProjectDialog>
{RoleUtils.isAdmin(session) && <EditProjectDialog>
<SidebarMenuAction>
<Plus />
</SidebarMenuAction>
</EditProjectDialog>
</EditProjectDialog>}
<SidebarMenu>
{projects.map((item) => (
<DropdownMenu key={item.id}>

View File

@@ -14,7 +14,7 @@ export async function AppSidebar() {
const projects = await projectService.getAllProjects();
const relevantProjectsForUser = projects.filter((project) =>
project.apps.some((app) => RoleUtils.sessionHasReadAccessForApp(session, app.id)));
RoleUtils.sessionHasReadAccessToProject(session, project));
for (const project of relevantProjectsForUser) {
project.apps = project.apps.filter((app) => RoleUtils.sessionHasReadAccessForApp(session, app.id));
}

View File

@@ -63,6 +63,8 @@ class AppService {
revalidateTag(Tags.apps(existingApp.projectId));
revalidateTag(Tags.app(existingApp.id));
revalidateTag(Tags.projects());
revalidateTag(Tags.roles());
revalidateTag(Tags.users());
}
}

View File

@@ -25,6 +25,8 @@ class ProjectService {
});
} finally {
revalidateTag(Tags.projects());
revalidateTag(Tags.roles());
revalidateTag(Tags.users());
}
}

View File

@@ -2,7 +2,18 @@ import { adminRoleName, RolePermissionEnum } from "@/shared/model/role-extended.
import { UserSession } from "@/shared/model/sim-session.model";
export class RoleUtils {
static sessionHasReadAccessToProject(session: UserSession, project: { apps: { id: string; }[] }) {
if (project.apps.length === 0 && RoleUtils.sessionCanCreateNewApps(session)) {
return true;
}
return project.apps.some((app) => RoleUtils.sessionHasReadAccessForApp(session, app.id))
}
static getRolePermissionForApp(session: UserSession, appId: string) {
if (this.isAdmin(session)) {
return RolePermissionEnum.READWRITE;
}
return (session.permissions?.find(app => app.appId === appId)?.permission ?? null) as RolePermissionEnum | null;
}