chore: move source to repo root and standardize config
Phase 1 — Structural overhaul: - Move all source from headlamp-sealed-secrets/ subdirectory to repo root - Delete 23 AI-generated docs, 8 pre-built tarballs, release snapshots dir - Remove all working-directory refs from CI/release workflows - Update install-plugin.sh and typedoc.json paths Phase 2 — Config standardization: - Create .eslintrc.js and .prettierrc.js (standard Headlamp configs) - Remove inline eslintConfig/prettier from package.json (drop jsx-a11y, prettier extends) - Rewrite tsconfig.json (package name extend, add compilerOptions.types) - Create vitest.config.mts and vitest.setup.ts (standard from polaris) - Replace headlamp-plugin CLI scripts with direct tool invocation - Rewrite .gitignore with standard baseline Phase 3 — MCP & Claude settings: - Create .mcp.json with github/kubernetes/flux/playwright servers - Create .claude/settings.local.json - Remove 7 specialized agents, keep 3 meta-orchestration agents Phase 4 — Documentation: - Rewrite CLAUDE.md (remove subdirectory refs, standard format) - Add ArtifactHub badge, Architecture section, standardized install methods to README.md - Create CONTRIBUTING.md and SECURITY.md - Fix pre-existing test bugs in validators.test.ts (isValidNamespace returns boolean, not ValidationResult; error message string mismatches) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* 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 };
|
||||
}
|
||||
Reference in New Issue
Block a user