This repository has been archived on 2026-06-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
headlamp-sealed-secrets-plugin/headlamp-sealed-secrets/src/hooks/usePermissions.ts
T
Chris Farhood 839fdd4819 feat: implement RBAC permissions helper (Phase 2.3)
Add comprehensive RBAC permission checking using Kubernetes
SelfSubjectAccessReview API. Hide/disable UI elements based on
user permissions for better security and UX.

Features:
- RBAC module with permission checking utilities
- React hooks for permission management (usePermissions, usePermission, etc.)
- Permission-aware UI (hide create/delete/re-encrypt buttons)
- Decrypt button disabled if no Secret access
- Multi-namespace permission support
- Fail-safe design (returns false on error)

Technical details:
- Uses Kubernetes authorization.k8s.io/v1 SelfSubjectAccessReview API
- Concurrent permission checks with Promise.all
- Automatic loading states and error handling
- React cleanup on unmount prevents memory leaks
- Type-safe with Result<T, E> types

Files:
- src/lib/rbac.ts: NEW RBAC checking module (+168 lines)
- src/hooks/usePermissions.ts: NEW React hooks (+138 lines)
- src/components/SealedSecretList.tsx: Hide create button if no permission
- src/components/SealedSecretDetail.tsx: Hide re-encrypt/delete/decrypt based on permissions
- PHASE_2.3_COMPLETE.md: Implementation documentation
- .claude/agents/: Add 5 new specialized agents (test, accessibility, docs, orchestration)

Bundle size: 348.46 kB (96.05 kB gzipped), +1.81 kB (+0.5%)
Build time: 3.93s
Zero TypeScript/lint errors

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
2026-02-11 21:51:05 -05:00

138 lines
3.6 KiB
TypeScript

/**
* React Hooks for RBAC Permission Checking
*
* Provides React hooks for checking and caching user permissions
* for SealedSecrets and related resources.
*/
import React from 'react';
import { checkSealedSecretPermissions, ResourcePermissions } from '../lib/rbac';
/**
* Hook to check SealedSecret permissions for a namespace
*
* Automatically fetches permissions on mount and when namespace changes.
* Returns loading state and permissions.
*
* @param namespace Optional namespace to check (cluster-wide if omitted)
* @returns Object with loading state, permissions, and error
*
* @example
* const { loading, permissions, error } = usePermissions('default');
* if (!loading && permissions?.canCreate) {
* // Show create button
* }
*/
export function usePermissions(namespace?: string) {
const [loading, setLoading] = React.useState(true);
const [permissions, setPermissions] = React.useState<ResourcePermissions | null>(null);
const [error, setError] = React.useState<string | null>(null);
React.useEffect(() => {
let mounted = true;
async function fetchPermissions() {
setLoading(true);
setError(null);
const result = await checkSealedSecretPermissions(namespace);
if (!mounted) return;
if (result.ok) {
setPermissions(result.value);
setError(null);
} else if (result.ok === false) {
setPermissions(null);
setError(result.error);
}
setLoading(false);
}
fetchPermissions();
return () => {
mounted = false;
};
}, [namespace]);
return { loading, permissions, error };
}
/**
* Hook to check a specific permission
*
* Useful when you only need to check one permission (e.g., canCreate)
* instead of fetching all permissions.
*
* @param namespace Optional namespace to check
* @param permission Permission key to check
* @returns Object with loading state and allowed flag
*
* @example
* const { loading, allowed } = usePermission('default', 'canCreate');
* if (allowed) {
* // Show create button
* }
*/
export function usePermission(
namespace: string | undefined,
permission: keyof ResourcePermissions
) {
const { loading, permissions } = usePermissions(namespace);
const allowed = permissions?.[permission] ?? false;
return { loading, allowed };
}
/**
* Hook to check if user has any write permissions
*
* Returns true if user can create, update, or delete.
* Useful for showing/hiding entire sections of UI.
*
* @param namespace Optional namespace to check
* @returns Object with loading state and hasWriteAccess flag
*
* @example
* const { loading, hasWriteAccess } = useHasWriteAccess('default');
* if (hasWriteAccess) {
* // Show management UI
* }
*/
export function useHasWriteAccess(namespace?: string) {
const { loading, permissions } = usePermissions(namespace);
const hasWriteAccess =
permissions?.canCreate || permissions?.canUpdate || permissions?.canDelete || false;
return { loading, hasWriteAccess };
}
/**
* Hook to check if user has read-only access
*
* Returns true if user can read/list but cannot create/update/delete.
*
* @param namespace Optional namespace to check
* @returns Object with loading state and isReadOnly flag
*
* @example
* const { loading, isReadOnly } = useIsReadOnly('default');
* if (isReadOnly) {
* // Show read-only warning
* }
*/
export function useIsReadOnly(namespace?: string) {
const { loading, permissions } = usePermissions(namespace);
const isReadOnly =
(permissions?.canRead || permissions?.canList) &&
!permissions?.canCreate &&
!permissions?.canUpdate &&
!permissions?.canDelete;
return { loading, isReadOnly };
}