UNPKG

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.3 kB
{"version":3,"file":"index.cjs","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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"}