mirror of
https://github.com/biersoeckli/QuickStack.git
synced 2026-02-10 21:49:19 -06:00
feat: small fixes for roles and users
This commit is contained in:
@@ -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 />
|
||||
|
||||
@@ -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} />
|
||||
|
||||
@@ -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 >
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
</>}
|
||||
/>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ class ProjectService {
|
||||
});
|
||||
} finally {
|
||||
revalidateTag(Tags.projects());
|
||||
revalidateTag(Tags.roles());
|
||||
revalidateTag(Tags.users());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user