feat: replace hardcoded permission map with direct OAuth2 scope checks
Remove role-to-permission mapping (usePermissions, RequirePermission) and replace with direct scope reads from the Logto access token JWT. OrgResolver decodes the scope claim after /api/me resolves and stores scopes in Zustand. RequireScope and useScopes replace the old hooks/components across all pages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,9 +32,9 @@ import {
|
||||
useLogs,
|
||||
useCreateApp,
|
||||
} from '../api/hooks';
|
||||
import { RequirePermission } from '../components/RequirePermission';
|
||||
import { RequireScope } from '../components/RequireScope';
|
||||
import { DeploymentStatusBadge } from '../components/DeploymentStatusBadge';
|
||||
import { usePermissions } from '../hooks/usePermissions';
|
||||
import { useScopes } from '../auth/useScopes';
|
||||
import type { DeploymentResponse } from '../types/api';
|
||||
|
||||
// ─── Types ───────────────────────────────────────────────────────────────────
|
||||
@@ -119,7 +119,9 @@ export function AppDetailPage() {
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const { tenantId } = useAuth();
|
||||
const { canManageApps, canDeploy } = usePermissions();
|
||||
const scopes = useScopes();
|
||||
const canManageApps = scopes.has('apps:manage');
|
||||
const canDeploy = scopes.has('apps:deploy');
|
||||
|
||||
// Active tab
|
||||
const [activeTab, setActiveTab] = useState('overview');
|
||||
@@ -399,7 +401,7 @@ export function AppDetailPage() {
|
||||
{/* Action bar */}
|
||||
<Card title="Actions">
|
||||
<div className="flex flex-wrap gap-2 py-2">
|
||||
<RequirePermission permission="apps:deploy">
|
||||
<RequireScope scope="apps:deploy">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
@@ -408,9 +410,9 @@ export function AppDetailPage() {
|
||||
>
|
||||
Deploy
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
|
||||
<RequirePermission permission="apps:deploy">
|
||||
<RequireScope scope="apps:deploy">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
@@ -420,9 +422,9 @@ export function AppDetailPage() {
|
||||
>
|
||||
Restart
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
|
||||
<RequirePermission permission="apps:deploy">
|
||||
<RequireScope scope="apps:deploy">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
@@ -431,9 +433,9 @@ export function AppDetailPage() {
|
||||
>
|
||||
Stop
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
|
||||
<RequirePermission permission="apps:deploy">
|
||||
<RequireScope scope="apps:deploy">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
@@ -445,9 +447,9 @@ export function AppDetailPage() {
|
||||
>
|
||||
Re-upload JAR
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
|
||||
<RequirePermission permission="apps:manage">
|
||||
<RequireScope scope="apps:manage">
|
||||
<Button
|
||||
variant="danger"
|
||||
size="sm"
|
||||
@@ -455,7 +457,7 @@ export function AppDetailPage() {
|
||||
>
|
||||
Delete App
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -552,11 +554,11 @@ export function AppDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<RequirePermission permission="apps:manage">
|
||||
<RequireScope scope="apps:manage">
|
||||
<Button variant="secondary" size="sm" onClick={openRoutingModal}>
|
||||
Edit Routing
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@cameleer/design-system';
|
||||
import { useAuth } from '../auth/useAuth';
|
||||
import { useTenant, useEnvironments, useApps } from '../api/hooks';
|
||||
import { RequirePermission } from '../components/RequirePermission';
|
||||
import { RequireScope } from '../components/RequireScope';
|
||||
import type { EnvironmentResponse, AppResponse } from '../types/api';
|
||||
|
||||
// Helper: fetches apps for one environment and reports data upward via effect
|
||||
@@ -126,7 +126,7 @@ export function DashboardPage() {
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RequirePermission permission="apps:manage">
|
||||
<RequireScope scope="apps:manage">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
@@ -134,7 +134,7 @@ export function DashboardPage() {
|
||||
>
|
||||
New Environment
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
@@ -193,11 +193,11 @@ export function DashboardPage() {
|
||||
title="No environments yet"
|
||||
description="Create your first environment to get started deploying Camel applications."
|
||||
action={
|
||||
<RequirePermission permission="apps:manage">
|
||||
<RequireScope scope="apps:manage">
|
||||
<Button variant="primary" onClick={() => navigate('/environments/new')}>
|
||||
Create Environment
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
useApps,
|
||||
useCreateApp,
|
||||
} from '../api/hooks';
|
||||
import { RequirePermission } from '../components/RequirePermission';
|
||||
import { RequireScope } from '../components/RequireScope';
|
||||
import { DeploymentStatusBadge } from '../components/DeploymentStatusBadge';
|
||||
import type { AppResponse } from '../types/api';
|
||||
|
||||
@@ -188,7 +188,7 @@ export function EnvironmentDetailPage() {
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<RequirePermission
|
||||
<RequireScope
|
||||
permission="apps:manage"
|
||||
fallback={
|
||||
<h1 className="text-2xl font-semibold text-white">
|
||||
@@ -201,7 +201,7 @@ export function EnvironmentDetailPage() {
|
||||
onSave={handleRename}
|
||||
placeholder="Environment name"
|
||||
/>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
<Badge label={environment.slug} color="primary" variant="outlined" />
|
||||
<Badge
|
||||
label={environment.status}
|
||||
@@ -209,12 +209,12 @@ export function EnvironmentDetailPage() {
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RequirePermission permission="apps:deploy">
|
||||
<RequireScope scope="apps:deploy">
|
||||
<Button variant="primary" size="sm" onClick={openNewApp}>
|
||||
New App
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
<RequirePermission permission="apps:manage">
|
||||
</RequireScope>
|
||||
<RequireScope scope="apps:manage">
|
||||
<Button
|
||||
variant="danger"
|
||||
size="sm"
|
||||
@@ -224,7 +224,7 @@ export function EnvironmentDetailPage() {
|
||||
>
|
||||
Delete Environment
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -234,11 +234,11 @@ export function EnvironmentDetailPage() {
|
||||
title="No apps yet"
|
||||
description="Deploy your first Camel application to this environment."
|
||||
action={
|
||||
<RequirePermission permission="apps:deploy">
|
||||
<RequireScope scope="apps:deploy">
|
||||
<Button variant="primary" onClick={openNewApp}>
|
||||
New App
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import type { Column } from '@cameleer/design-system';
|
||||
import { useAuth } from '../auth/useAuth';
|
||||
import { useEnvironments, useCreateEnvironment } from '../api/hooks';
|
||||
import { RequirePermission } from '../components/RequirePermission';
|
||||
import { RequireScope } from '../components/RequireScope';
|
||||
import type { EnvironmentResponse } from '../types/api';
|
||||
|
||||
interface TableRow {
|
||||
@@ -120,11 +120,11 @@ export function EnvironmentsPage() {
|
||||
{/* Page header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-semibold text-white">Environments</h1>
|
||||
<RequirePermission permission="apps:manage">
|
||||
<RequireScope scope="apps:manage">
|
||||
<Button variant="primary" size="sm" onClick={openModal}>
|
||||
Create Environment
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
</div>
|
||||
|
||||
{/* Table / empty state */}
|
||||
@@ -133,11 +133,11 @@ export function EnvironmentsPage() {
|
||||
title="No environments yet"
|
||||
description="Create your first environment to start deploying Camel applications."
|
||||
action={
|
||||
<RequirePermission permission="apps:manage">
|
||||
<RequireScope scope="apps:manage">
|
||||
<Button variant="primary" onClick={openModal}>
|
||||
Create Environment
|
||||
</Button>
|
||||
</RequirePermission>
|
||||
</RequireScope>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user