Skip to content

Instantly share code, notes, and snippets.

@CasperEngl
Last active December 14, 2024 17:42
Show Gist options
  • Select an option

  • Save CasperEngl/d65c90fffa92e0b8439b3a50e4524096 to your computer and use it in GitHub Desktop.

Select an option

Save CasperEngl/d65c90fffa92e0b8439b3a50e4524096 to your computer and use it in GitHub Desktop.
import type { RequireOneOrNone } from "type-fest"
import { isArray } from "lodash-es"
import type { UserModel } from "@/db/users/types"
import type { ResourceModelMap } from "./interfaces"
import type { PermissionResource, PermissionScope } from "./types"
import { getPermissions, validatePermissions } from "./utils"
type PermissionOptions<T extends PermissionResource> = RequireOneOrNone<{
resource: ResourceModelMap[T]
resources: ResourceModelMap[T][]
}, "resource" | "resources">
/**
* Checks if the user has the required permission for the given resource.
*
* @param {UserModel} user - The user to check the permission for.
* @param {PermissionResource} resourceName - The name of the resource.
* @param {PermissionScope} permissionOrPermissions - The permission to check.
* @param {object} [options] - The options to check the permission for.
* @param [options.resource] - The related resource to check the permission for.
* @param [options.resources] - The related resources to check the permission for.
* @returns A boolean indicating whether the user has the required permission.
*/
export function checkUserPermissions<T extends PermissionResource>(
user: UserModel,
resourceName: T,
permissionOrPermissions: PermissionScope | PermissionScope[],
options: PermissionOptions<T> = {},
) {
const requestedPermissions = isArray(permissionOrPermissions) ? permissionOrPermissions : [permissionOrPermissions]
const givenPermissions = getPermissions(user, resourceName, options.resource)
return validatePermissions(givenPermissions, requestedPermissions)
}
import { usePathname } from "next/navigation"
import * as React from "react"
import type { PermissionResource, PermissionScope } from "@/lib/auth/permissions/types"
import type { FeatureFlag } from "@/lib/flags/client"
import { checkUserPermissions } from "@/lib/auth/permissions"
import type { UserContextState } from "../components/user-context"
import { useUser } from "./use-user"
export interface NavigationPermission { resource: PermissionResource, scope: PermissionScope | PermissionScope[] }
export interface StaticNavigationItem {
name: string
href: string
featureFlag?: FeatureFlag
permissions?: NavigationPermission[]
}
export type WithCurrent<T extends StaticNavigationItem> = Omit<T, "featureFlag" | "permissions"> & { current: boolean }
export type BasicNavigationItem = WithCurrent<StaticNavigationItem>
export function useNavigationItems<T extends StaticNavigationItem>(navigationItems: T[]): WithCurrent<T>[] {
const filteredNavigationItems = useFilteredNavigationItems(navigationItems)
return useNavigationItemsWithCurrent(filteredNavigationItems)
}
function useNavigationItemsWithCurrent<T extends StaticNavigationItem>(navigationItems: T[]): WithCurrent<T>[] {
const pathname = usePathname()
return React.useMemo(
() =>
navigationItems.map(({ featureFlag: _featureFlag, permissions: _permissions, ...item }) => ({
...item,
current: pathname === item.href,
})),
[navigationItems, pathname],
)
}
function useFilteredNavigationItems<T extends StaticNavigationItem>(navigationItems: T[]) {
const userContext = useUser()
return React.useMemo(() => navigationItems.filter(item => checkNavigationItem(item, userContext)), [navigationItems, userContext])
}
function checkNavigationItem(item: StaticNavigationItem, userContext: UserContextState) {
return checkFeatureFlag(item, userContext) && checkPermissions(item, userContext)
}
function checkPermissions(item: StaticNavigationItem, userContext: UserContextState) {
if (!item.permissions)
return true
const { user } = userContext
if (!user)
return false
return item.permissions.every(({ resource, scope }) => checkUserPermissions(user, resource, scope))
}
function checkFeatureFlag(item: StaticNavigationItem, userContext: UserContextState) {
if (!item.featureFlag)
return true
return userContext.enabledFeatures[item.featureFlag]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment