Added comprehensive authentication and authorization system: Authentication System: - Better Auth integration with session management - Login/logout pages and API routes - Middleware for route protection - Auth utilities and client libraries User Management: - User list, detail, and invite pages - User API endpoints (CRUD operations) - Session management for users - Profile settings page Role-Based Access Control: - Role management pages (list, create, edit) - Permission system with granular controls - Role assignment to users - Role API endpoints Admin Features: - Audit log page for tracking system events - Admin settings page - Audit service for logging user actions Additional Features: - Quotes management pages and components - SalesBldr API integration - Email service for notifications Configuration & Documentation: - Updated docker-compose.yml - MCP server configuration (mcp.json) - CVE-2025-55182 security review documentation - Standards guide and PRD documents - Re-enabling authentication documentation Database Migrations: - 012: Auth tables (users, sessions, accounts, verifications) - 013: Role tables (roles, permissions, role_permissions, user_roles) - 014: Admin settings table UI Updates: - Updated dashboard layout - Enhanced app layout with auth integration
109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import { Label } from "@/components/ui/label";
|
|
import { statement } from "@/lib/permissions";
|
|
|
|
interface PermissionPickerProps {
|
|
value: Record<string, string[]>;
|
|
onChange: (permissions: Record<string, string[]>) => void;
|
|
disabled?: boolean;
|
|
}
|
|
|
|
const resourceLabels: Record<string, string> = {
|
|
tickets: "Tickets",
|
|
configItems: "Configuration Items",
|
|
admin: "Admin Panel",
|
|
users: "User Management",
|
|
roles: "Role Management",
|
|
auditLog: "Audit Log",
|
|
settings: "Settings",
|
|
};
|
|
|
|
const actionLabels: Record<string, string> = {
|
|
create: "Create",
|
|
read: "Read",
|
|
update: "Update",
|
|
delete: "Delete",
|
|
access: "Access",
|
|
invite: "Invite",
|
|
ban: "Ban",
|
|
};
|
|
|
|
export function PermissionPicker({ value, onChange, disabled }: PermissionPickerProps) {
|
|
const handleToggle = (resource: string, action: string, checked: boolean) => {
|
|
const currentActions = value[resource] || [];
|
|
let newActions: string[];
|
|
|
|
if (checked) {
|
|
newActions = [...currentActions, action];
|
|
} else {
|
|
newActions = currentActions.filter((a) => a !== action);
|
|
}
|
|
|
|
onChange({
|
|
...value,
|
|
[resource]: newActions,
|
|
});
|
|
};
|
|
|
|
const handleToggleAll = (resource: string, checked: boolean) => {
|
|
const allActions = statement[resource as keyof typeof statement] as readonly string[];
|
|
onChange({
|
|
...value,
|
|
[resource]: checked ? [...allActions] : [],
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{Object.entries(statement).map(([resource, actions]) => {
|
|
const currentActions = value[resource] || [];
|
|
const allChecked = actions.every((a) => currentActions.includes(a));
|
|
const someChecked = actions.some((a) => currentActions.includes(a));
|
|
|
|
return (
|
|
<div key={resource} className="space-y-3">
|
|
<div className="flex items-center gap-2">
|
|
<Checkbox
|
|
id={`${resource}-all`}
|
|
checked={allChecked}
|
|
onCheckedChange={(checked) =>
|
|
handleToggleAll(resource, checked as boolean)
|
|
}
|
|
disabled={disabled}
|
|
className={someChecked && !allChecked ? "data-[state=checked]:bg-muted" : ""}
|
|
/>
|
|
<Label
|
|
htmlFor={`${resource}-all`}
|
|
className="font-semibold cursor-pointer"
|
|
>
|
|
{resourceLabels[resource] || resource}
|
|
</Label>
|
|
</div>
|
|
<div className="ml-6 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3">
|
|
{(actions as readonly string[]).map((action) => (
|
|
<div key={action} className="flex items-center gap-2">
|
|
<Checkbox
|
|
id={`${resource}-${action}`}
|
|
checked={currentActions.includes(action)}
|
|
onCheckedChange={(checked) =>
|
|
handleToggle(resource, action, checked as boolean)
|
|
}
|
|
disabled={disabled}
|
|
/>
|
|
<Label
|
|
htmlFor={`${resource}-${action}`}
|
|
className="text-sm cursor-pointer"
|
|
>
|
|
{actionLabels[action] || action}
|
|
</Label>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|