vue-nuxt-permission
Version:
A unified permission system for Vue 3 and Nuxt 3 / Nuxt 4 with directives, guards, and async support.
1 lines • 29.9 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["../src/crypto.ts","../src/guards/globalGuard.ts","../src/guards/createGuard.ts","../src/composables/usePermission.ts"],"sourcesContent":["import CryptoJS from \"crypto-js\";\n\nexport interface EncryptedPayload {\n iv: string;\n data: string;\n}\n\nexport interface PermissionCrypto {\n encrypt<T>(value: T): EncryptedPayload;\n decrypt<T = unknown>(payload: unknown): T | null;\n decryptAuthPayload<T extends Record<string, any>>(\n payload: T,\n fields?: readonly string[],\n ): T;\n}\n\nfunction normalizeSecret(rawSecret: string): string {\n if (!rawSecret || typeof rawSecret !== \"string\") {\n throw new Error(\"A crypto secret is required\");\n }\n\n const secret = rawSecret.trim();\n if (secret.startsWith('\"') && secret.endsWith('\"')) {\n return secret.slice(1, -1);\n }\n\n return secret;\n}\n\nfunction createKey(rawSecret: string): CryptoJS.lib.WordArray {\n const key = CryptoJS.enc.Utf8.parse(normalizeSecret(rawSecret));\n key.sigBytes = 32;\n key.words.length = 8;\n return key;\n}\n\nfunction parseEncryptedPayload(payload: unknown): EncryptedPayload | null {\n if (payload && typeof payload === \"object\") {\n const candidate = payload as Partial<EncryptedPayload>;\n if (typeof candidate.iv === \"string\" && typeof candidate.data === \"string\") {\n return candidate as EncryptedPayload;\n }\n }\n\n if (typeof payload === \"string\") {\n try {\n return parseEncryptedPayload(JSON.parse(payload));\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Creates an AES-CBC helper compatible with the encrypted permission payloads\n * returned by Jervis authentication APIs.\n */\nexport function createPermissionCrypto(rawSecret: string): PermissionCrypto {\n const key = createKey(rawSecret);\n\n const encrypt = <T>(value: T): EncryptedPayload => {\n const iv = CryptoJS.lib.WordArray.random(16);\n const encrypted = CryptoJS.AES.encrypt(JSON.stringify(value), key, {\n iv,\n mode: CryptoJS.mode.CBC,\n padding: CryptoJS.pad.Pkcs7,\n });\n\n return {\n iv: CryptoJS.enc.Base64.stringify(iv),\n data: CryptoJS.enc.Base64.stringify(encrypted.ciphertext),\n };\n };\n\n const decrypt = <T = unknown>(payload: unknown): T | null => {\n try {\n const encryptedPayload = parseEncryptedPayload(payload);\n if (!encryptedPayload) return null;\n\n const decrypted = CryptoJS.AES.decrypt(\n { ciphertext: CryptoJS.enc.Base64.parse(encryptedPayload.data) } as CryptoJS.lib.CipherParams,\n key,\n {\n iv: CryptoJS.enc.Base64.parse(encryptedPayload.iv),\n mode: CryptoJS.mode.CBC,\n padding: CryptoJS.pad.Pkcs7,\n },\n );\n const text = decrypted.toString(CryptoJS.enc.Utf8);\n return text ? (JSON.parse(text) as T) : null;\n } catch {\n return null;\n }\n };\n\n const decryptAuthPayload = <T extends Record<string, any>>(\n payload: T,\n fields: readonly string[] = [\"roles\", \"permissions\"],\n ): T => {\n const decryptedPayload = { ...payload };\n\n for (const field of fields) {\n const value = payload[field];\n if (Array.isArray(value)) continue;\n decryptedPayload[field as keyof T] = (decrypt(value) ?? []) as T[keyof T];\n }\n\n return decryptedPayload;\n };\n\n return { encrypt, decrypt, decryptAuthPayload };\n}\n\nexport function createPermissionDecryptor(secret: string) {\n const { decrypt } = createPermissionCrypto(secret);\n return (payload: unknown): string[] => {\n const permissions = decrypt<unknown>(payload);\n return Array.isArray(permissions) ? permissions : [];\n };\n}\n","import { configurePermission, getCurrentPermissions } from \"@/core/config\";\r\nimport { hasPermission } from \"@/core/evaluator\";\r\nimport type { GuardOptions, PermissionRoute, PermissionValue } from \"@/types\";\r\nimport type { NavigationGuardNext, RouteLocationNormalized } from \"vue-router\";\r\n\r\nconst MAX_ROUTE_DEPTH = 50;\r\n\r\n/**\r\n * Find a matching node in protectedRoutes for the given target path.\r\n * Matches either by raw concatenated path (parent + child) or by direct\r\n * path equality at any nesting depth.\r\n */\r\nfunction findProtectedRouteMatch(\r\n routes: PermissionRoute[],\r\n targetPath: string,\r\n basePath = \"\",\r\n depth = 0,\r\n): PermissionRoute | null {\r\n if (depth > MAX_ROUTE_DEPTH) return null;\r\n for (const route of routes) {\r\n const fullPath = basePath + route.path;\r\n if (fullPath === targetPath || route.path === targetPath) return route;\r\n if (route.children?.length) {\r\n const child = findProtectedRouteMatch(\r\n route.children,\r\n targetPath,\r\n fullPath,\r\n depth + 1,\r\n );\r\n if (child) return child;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * Global Permission Guard\r\n * ------------------------\r\n * A ready-to-use guard for Vue/Nuxt router.\r\n *\r\n * Example:\r\n * router.beforeEach((to, from, next) => globalGuard(to, from, next, {\r\n * authRoutes,\r\n * protectedRoutes,\r\n * getAuthState: () => authStore.state,\r\n * loginPath: '/login',\r\n * homePath: '/'\r\n * }))\r\n */\r\nexport const globalGuard = async (\r\n to: RouteLocationNormalized,\r\n from: RouteLocationNormalized,\r\n next: NavigationGuardNext,\r\n options: GuardOptions = {},\r\n) => {\r\n const {\r\n authRoutes = [],\r\n protectedRoutes = [] as PermissionRoute[],\r\n getAuthState,\r\n loginPath = \"/login\",\r\n homePath = \"/\",\r\n onDenied,\r\n onAllowed,\r\n } = options;\r\n\r\n try {\r\n const authState = getAuthState?.() ?? { isAuthenticated: false };\r\n const isAuthenticated = authState.isAuthenticated;\r\n\r\n // Resolve user permissions with full fallback chain.\r\n // Many consumer projects expose them via authState.user.permissions and\r\n // never call setPermissions/configurePermission explicitly.\r\n const userPermissions =\r\n authState.permissions ??\r\n authState.user?.permissions ??\r\n getCurrentPermissions();\r\n\r\n // If we discovered permissions on the auth payload that the package\r\n // didn't know about yet, sync them so the directive/composable see them.\r\n if (\r\n Array.isArray(userPermissions) &&\r\n userPermissions.length > 0 &&\r\n getCurrentPermissions().length === 0\r\n ) {\r\n configurePermission(userPermissions);\r\n }\r\n\r\n const isAuthRoute = authRoutes.some((r) => r.path === to.path);\r\n\r\n // Membership in protectedRoutes implies auth + permission requirements,\r\n // even when the route's meta doesn't set requiresAuth/checkPermission.\r\n const matchedProtected = findProtectedRouteMatch(protectedRoutes, to.path);\r\n\r\n const requiresAuth =\r\n (to.meta?.requiresAuth ?? false) || matchedProtected !== null;\r\n\r\n const effectivePermissions: PermissionValue | undefined =\r\n to.meta?.permissions ?? matchedProtected?.meta?.permissions;\r\n\r\n const checkPermission =\r\n (to.meta?.checkPermission ?? false) ||\r\n (matchedProtected !== null && effectivePermissions !== undefined);\r\n\r\n if (!isAuthenticated && requiresAuth) {\r\n onDenied?.(to, from);\r\n return next(loginPath);\r\n }\r\n\r\n if (isAuthenticated && isAuthRoute) {\r\n onAllowed?.(to, from);\r\n return next(homePath);\r\n }\r\n\r\n if (checkPermission && effectivePermissions !== undefined) {\r\n const allowed = await hasPermission(\r\n effectivePermissions,\r\n userPermissions,\r\n );\r\n if (!allowed) {\r\n onDenied?.(to, from);\r\n const fallback = await findAccessibleRoute(\r\n protectedRoutes,\r\n userPermissions,\r\n );\r\n return next(fallback || loginPath);\r\n }\r\n }\r\n\r\n onAllowed?.(to, from);\r\n next();\r\n } catch (e) {\r\n console.error(\"[v-permission:globalGuard] Unexpected error:\", e);\r\n next(loginPath);\r\n }\r\n};\r\n\r\n/**\r\n * Helper to find the first accessible route from protectedRoutes.\r\n * Includes depth protection against circular/deeply nested routes.\r\n */\r\nasync function findAccessibleRoute(\r\n routes: PermissionRoute[],\r\n userPermissions: string[],\r\n basePath = \"\",\r\n depth = 0,\r\n): Promise<string | null> {\r\n if (depth > MAX_ROUTE_DEPTH) {\r\n console.warn(\"[v-permission:globalGuard] Route nesting exceeds max depth\");\r\n return null;\r\n }\r\n\r\n for (const route of routes) {\r\n const fullPath = basePath + route.path;\r\n const requiredPermissions = route.meta?.permissions ?? \"*\";\r\n\r\n if (await hasPermission(requiredPermissions, userPermissions)) {\r\n return fullPath;\r\n }\r\n\r\n if (route.children?.length) {\r\n const child = await findAccessibleRoute(\r\n route.children,\r\n userPermissions,\r\n fullPath,\r\n depth + 1,\r\n );\r\n if (child) return child;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\nexport default globalGuard;\r\n","import {\r\n configurePermission,\r\n getCurrentPermissions,\r\n getDecryptHook,\r\n} from \"@/core/config\";\r\nimport { hasPermission } from \"@/core/evaluator\";\r\nimport type { GuardOptions, PermissionRoute, PermissionValue } from \"@/types\";\r\nimport type { NavigationGuardNext, RouteLocationNormalized } from \"vue-router\";\r\n\r\nconst MAX_ROUTE_DEPTH = 50;\r\n\r\nfunction joinPath(base: string, segment: string): string {\r\n if (!base) return segment;\r\n if (segment.startsWith(\"/\")) return segment;\r\n return base.endsWith(\"/\") ? base + segment : base + \"/\" + segment;\r\n}\r\n\r\nfunction findProtectedRouteMatch(\r\n routes: PermissionRoute[],\r\n targetPath: string,\r\n basePath = \"\",\r\n depth = 0,\r\n): PermissionRoute | null {\r\n if (depth > MAX_ROUTE_DEPTH) return null;\r\n for (const route of routes) {\r\n const fullPath = joinPath(basePath, route.path);\r\n if (fullPath === targetPath) return route;\r\n if (route.children?.length) {\r\n const child = findProtectedRouteMatch(\r\n route.children,\r\n targetPath,\r\n fullPath,\r\n depth + 1,\r\n );\r\n if (child) return child;\r\n }\r\n }\r\n return null;\r\n}\r\n\r\n/**\r\n * createPermissionGuard\r\n * ---------------------\r\n * Generates a dynamic permission guard for any Vue or Nuxt router.\r\n *\r\n * Example:\r\n * router.beforeEach(createPermissionGuard({\r\n * loginPath: '/login',\r\n * homePath: '/',\r\n * onDenied: (to) => console.warn('Denied:', to.path)\r\n * }))\r\n */\r\nexport const createPermissionGuard = (options: GuardOptions = {}) => {\r\n const {\r\n authRoutes = [],\r\n protectedRoutes = [] as PermissionRoute[],\r\n getAuthState,\r\n loginPath = \"/login\",\r\n homePath = \"/\",\r\n onDenied,\r\n onAllowed,\r\n } = options;\r\n\r\n return async (\r\n to: RouteLocationNormalized,\r\n from: RouteLocationNormalized,\r\n next: NavigationGuardNext,\r\n ) => {\r\n try {\r\n const authState = getAuthState?.() ?? { isAuthenticated: false };\r\n const isAuthenticated = authState.isAuthenticated;\r\n\r\n const decryptFn =\r\n options.decrypt ?? options.transform ?? getDecryptHook();\r\n\r\n const rawPermissions =\r\n authState.permissions ??\r\n authState.user?.permissions ??\r\n getCurrentPermissions();\r\n\r\n let userPermissions: string[] = [];\r\n if (decryptFn && rawPermissions && !Array.isArray(rawPermissions)) {\r\n const decrypted = await decryptFn(rawPermissions);\r\n userPermissions = Array.isArray(decrypted) ? decrypted : [];\r\n } else if (Array.isArray(rawPermissions)) {\r\n userPermissions = rawPermissions;\r\n } else {\r\n userPermissions = getCurrentPermissions();\r\n }\r\n\r\n if (\r\n Array.isArray(userPermissions) &&\r\n userPermissions.length > 0 &&\r\n getCurrentPermissions().length === 0\r\n ) {\r\n configurePermission(userPermissions, {\r\n decrypt: decryptFn,\r\n });\r\n }\r\n\r\n const isAuthRoute = authRoutes.some((r) => r.path === to.path);\r\n\r\n const matchedProtected = findProtectedRouteMatch(\r\n protectedRoutes,\r\n to.path,\r\n );\r\n\r\n const requiresAuth =\r\n (to.meta?.requiresAuth ?? false) || matchedProtected !== null;\r\n\r\n const effectivePermissions: PermissionValue | undefined =\r\n to.meta?.permissions ?? matchedProtected?.meta?.permissions;\r\n\r\n const checkPermission =\r\n (to.meta?.checkPermission ?? false) ||\r\n (matchedProtected !== null && effectivePermissions !== undefined);\r\n\r\n if (!isAuthenticated && requiresAuth) {\r\n onDenied?.(to, from);\r\n return next(loginPath);\r\n }\r\n\r\n if (isAuthenticated && isAuthRoute) {\r\n onAllowed?.(to, from);\r\n return next(homePath);\r\n }\r\n\r\n if (checkPermission && effectivePermissions !== undefined) {\r\n const allowed = await hasPermission(\r\n effectivePermissions,\r\n userPermissions,\r\n );\r\n if (!allowed) {\r\n onDenied?.(to, from);\r\n\r\n const fallback = await findAccessibleRoute(\r\n protectedRoutes,\r\n userPermissions,\r\n );\r\n return next(fallback || loginPath);\r\n }\r\n }\r\n\r\n onAllowed?.(to, from);\r\n next();\r\n } catch (e) {\r\n console.error(\"[v-permission:guard] Unexpected error:\", e);\r\n next(loginPath);\r\n }\r\n };\r\n};\r\n\r\n/**\r\n * Recursively finds a route that the user has permission to access.\r\n * Includes depth protection against circular/deeply nested routes.\r\n */\r\nasync function findAccessibleRoute(\r\n routes: PermissionRoute[],\r\n userPermissions: string[],\r\n basePath = \"\",\r\n depth = 0,\r\n): Promise<string | null> {\r\n if (depth > MAX_ROUTE_DEPTH) {\r\n console.warn(\"[v-permission:guard] Route nesting exceeds max depth\");\r\n return null;\r\n }\r\n\r\n for (const route of routes) {\r\n const fullPath = basePath + route.path;\r\n const requiredPermissions = route.meta?.permissions ?? \"*\";\r\n\r\n if (await hasPermission(requiredPermissions, userPermissions)) {\r\n return fullPath;\r\n }\r\n\r\n if (route.children?.length) {\r\n const child = await findAccessibleRoute(\r\n route.children,\r\n userPermissions,\r\n fullPath,\r\n depth + 1,\r\n );\r\n if (child) return child;\r\n }\r\n }\r\n return null;\r\n}\r\n","import { clearPermissionCache } from \"@/core/cache\";\r\nimport {\r\n configurePermission,\r\n getCurrentPermissions,\r\n getReactivePermissions,\r\n} from \"@/core/config\";\r\nimport { hasPermission } from \"@/core/evaluator\";\r\nimport type { PermissionValue } from \"@/types\";\r\nimport {\r\n getPermissionsFromStorage,\r\n savePermissionsToStorage,\r\n} from \"@/utils/storage\";\r\nimport { computed } from \"vue\";\r\n\r\n/**\r\n * usePermission Composable\r\n * Reactive + Async + Cached Permission Checker\r\n *\r\n * The returned `permissions` reflects the global reactive state, so any call\r\n * to `configurePermission` or `setPermissions` (from anywhere in the app)\r\n * will be observed automatically.\r\n */\r\nexport function usePermission() {\r\n // Bind to the global reactive permissions ref. This way the composable\r\n // always reflects the current state, even if it was called at module\r\n // top level (a pattern some consumers rely on).\r\n const permissionsRef = getReactivePermissions();\r\n\r\n // If the global ref is empty but we have a persisted snapshot in storage,\r\n // hydrate the global state once. This lets newly-mounted apps pick up\r\n // permissions saved during the previous session without an explicit call.\r\n if (\r\n (!permissionsRef.value || permissionsRef.value.length === 0) &&\r\n getCurrentPermissions().length === 0\r\n ) {\r\n const stored = getPermissionsFromStorage();\r\n if (Array.isArray(stored) && stored.length > 0) {\r\n // Don't re-persist (storage already has it); just sync runtime state.\r\n configurePermission(stored, { persist: false });\r\n }\r\n }\r\n\r\n /**\r\n * can(rule)\r\n * Async permission evaluator\r\n */\r\n const can = async (rule: PermissionValue) => {\r\n return await hasPermission(rule, permissionsRef.value);\r\n };\r\n\r\n /**\r\n * canSync(rule)\r\n * Instant local check — simplified fallback\r\n */\r\n const canSync = (rule: PermissionValue) => {\r\n const current = permissionsRef.value;\r\n if (rule === \"*\") return true;\r\n if (typeof rule === \"string\") return current.includes(rule);\r\n if (Array.isArray(rule)) {\r\n if (rule.includes(\"*\")) return true;\r\n return rule.some((r) => current.includes(r));\r\n }\r\n if (typeof rule === \"object\" && rule.permissions && rule.mode) {\r\n const { permissions: p, mode } = rule;\r\n if (!Array.isArray(p) || p.length === 0) return false;\r\n if (p.includes(\"*\")) return true;\r\n if (mode === \"and\") return p.every((v) => current.includes(v));\r\n if (mode === \"or\") return p.some((v) => current.includes(v));\r\n if (mode === \"not\") return !p.some((v) => current.includes(v));\r\n if (mode === \"startWith\")\r\n return p.some((pat) => current.some((u) => u.startsWith(pat)));\r\n if (mode === \"endWith\")\r\n return p.some((pat) => current.some((u) => u.endsWith(pat)));\r\n if (mode === \"regex\")\r\n return p.some((pat) => {\r\n try {\r\n const r = new RegExp(pat);\r\n return current.some((u) => r.test(u));\r\n } catch {\r\n return false;\r\n }\r\n });\r\n }\r\n return false;\r\n };\r\n\r\n /**\r\n * refresh()\r\n * Clear cache and re-read the current permissions from storage so any\r\n * out-of-band updates (e.g., another tab or a direct storage write) are\r\n * picked up. If storage is empty, leaves the current global state alone.\r\n */\r\n const refresh = () => {\r\n clearPermissionCache();\r\n const stored = getPermissionsFromStorage();\r\n if (Array.isArray(stored) && stored.length > 0) {\r\n configurePermission(stored, { persist: false });\r\n }\r\n };\r\n\r\n /**\r\n * setPermissions()\r\n */\r\n const setPermissions = (newPerms: string[]) => {\r\n if (!Array.isArray(newPerms)) {\r\n console.error(\"[v-permission] Permissions must be an array\");\r\n return;\r\n }\r\n clearPermissionCache();\r\n // configurePermission will update the reactive ref AND persist to storage\r\n // (persist defaults to true).\r\n configurePermission(newPerms);\r\n savePermissionsToStorage(newPerms);\r\n };\r\n\r\n /**\r\n * hasAll()\r\n */\r\n const hasAll = async (perms: string[]) => {\r\n // Empty array means no requirements, so always true\r\n if (!Array.isArray(perms) || perms.length === 0) return true;\r\n return await can({ permissions: perms, mode: \"and\" });\r\n };\r\n\r\n /**\r\n * hasAny()\r\n */\r\n const hasAny = async (perms: string[]) => {\r\n // Empty array or non-array should return false\r\n if (!Array.isArray(perms) || perms.length === 0) return false;\r\n return await can({ permissions: perms, mode: \"or\" });\r\n };\r\n\r\n return {\r\n permissions: computed(() => permissionsRef.value),\r\n can,\r\n hasPermission: can, // Alias for can() — used in docs & templates\r\n canSync,\r\n refresh,\r\n setPermissions,\r\n hasAll,\r\n hasAny,\r\n };\r\n}\r\n"],"names":["normalizeSecret","rawSecret","secret","createKey","key","CryptoJS","parseEncryptedPayload","payload","candidate","createPermissionCrypto","encrypt","value","iv","encrypted","decrypt","encryptedPayload","text","fields","decryptedPayload","field","createPermissionDecryptor","permissions","MAX_ROUTE_DEPTH","findProtectedRouteMatch","routes","targetPath","basePath","depth","route","fullPath","_a","child","globalGuard","to","from","next","options","authRoutes","protectedRoutes","getAuthState","loginPath","homePath","onDenied","onAllowed","authState","isAuthenticated","userPermissions","getCurrentPermissions","configurePermission","isAuthRoute","r","matchedProtected","requiresAuth","_b","effectivePermissions","_c","_d","checkPermission","_e","hasPermission","fallback","findAccessibleRoute","e","requiredPermissions","joinPath","base","segment","createPermissionGuard","decryptFn","getDecryptHook","rawPermissions","decrypted","usePermission","permissionsRef","getReactivePermissions","stored","getPermissionsFromStorage","can","rule","canSync","current","p","mode","v","pat","u","refresh","clearPermissionCache","setPermissions","newPerms","savePermissionsToStorage","hasAll","perms","hasAny","computed"],"mappings":"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"}