Merge branch 'hi/feat-project-level-role' into feat/project-level-permissions

This commit is contained in:
Joshen Lim committed 2024-06-06 17:14:11 +07:00
commit 453d294951
3 files changed
+88 -17

No files matched your search

+84 -15
View File
@@ -1,4 +1,4 @@
import { useIsLoggedIn } from 'common'
import { useIsLoggedIn, useParams } from 'common'
import jsonLogic from 'json-logic-js'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
@@ -6,38 +6,76 @@ import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { IS_PLATFORM } from 'lib/constants'
import type { Permission } from 'types'
import { useSelectedOrganization } from './useSelectedOrganization'
import { useSelectedProject } from './useSelectedProject'
import { useProjectDetailQuery } from 'data/projects/project-detail-query'
const toRegexpString = (actionOrResource: string) =>
`^${actionOrResource.replace('.', '\\.').replace('%', '.*')}$`
function doPermissionConditionCheck(permissions: Permission[], data?: object) {
const isRestricted = permissions
.filter((permission) => permission.restrictive)
.some(
({ condition }: { condition: jsonLogic.RulesLogic }) =>
condition === null || jsonLogic.apply(condition, data)
)
if (isRestricted) return false
return permissions
.filter((permission) => !permission.restrictive)
.some(
({ condition }: { condition: jsonLogic.RulesLogic }) =>
condition === null || jsonLogic.apply(condition, data)
)
}
export function doPermissionsCheck(
permissions: Permission[] | undefined,
action: string,
resource: string,
data?: object,
organizationId?: number
organizationId?: number,
projectId?: number
) {
if (!permissions || !Array.isArray(permissions)) {
return false
}
return permissions
.filter(
if (projectId) {
const projectPermissions = permissions.filter(
(permission) =>
permission.organization_id === organizationId &&
permission.actions.some((act) => (action ? action.match(toRegexpString(act)) : null)) &&
permission.resources.some((res) => resource.match(toRegexpString(res)))
)
.some(
({ condition }: { condition: jsonLogic.RulesLogic }) =>
condition === null || jsonLogic.apply(condition, { resource_name: resource, ...data })
permission.resources.some((res) => resource.match(toRegexpString(res))) &&
permission.project_ids?.includes(projectId)
)
if (projectPermissions.length > 0) {
return doPermissionConditionCheck(projectPermissions, { resource_name: resource, ...data })
}
}
const orgPermissions = permissions.filter(
(permission) =>
permission.organization_id === organizationId &&
permission.actions.some((act) => (action ? action.match(toRegexpString(act)) : null)) &&
permission.resources.some((res) => resource.match(toRegexpString(res)))
)
return doPermissionConditionCheck(orgPermissions, { resource_name: resource, ...data })
}
export function useGetPermissions(
permissionsOverride?: Permission[],
organizationIdOverride?: number,
enabled = true
) {
return useGetProjectPermissions(permissionsOverride, organizationIdOverride, undefined, enabled)
}
export function useGetProjectPermissions(
permissionsOverride?: Permission[],
organizationIdOverride?: number,
projectIdOverride?: number,
enabled = true
) {
const permissionsResult = usePermissionsQuery({
enabled: permissionsOverride === undefined && enabled,
@@ -54,9 +92,17 @@ export function useGetPermissions(
organizationIdOverride === undefined ? organizationResult : { id: organizationIdOverride }
const organizationId = organization?.id
const projectResult = useSelectedProject({
enabled: projectIdOverride === undefined && enabled,
})
const project = projectIdOverride === undefined ? projectResult : { id: projectIdOverride }
const projectId = project?.id
return {
permissions,
organizationId,
projectId,
}
}
@@ -68,19 +114,32 @@ export function useCheckPermissions(
// e.g If you want to use useCheckPermissions in a loop like organization settings
organizationId?: number,
permissions?: Permission[]
) {
return useCheckProjectPermissions(action, resource, data, organizationId, undefined, permissions)
}
export function useCheckProjectPermissions(
action: string,
resource: string,
data?: object,
// [Joshen] Pass the variables if you want to avoid hooks in this
// e.g If you want to use useCheckPermissions in a loop like organization settings
organizationId?: number,
projectId?: number,
permissions?: Permission[]
) {
const isLoggedIn = useIsLoggedIn()
const { permissions: allPermissions, organizationId: orgId } = useGetPermissions(
permissions,
organizationId,
isLoggedIn
)
const {
permissions: allPermissions,
organizationId: orgId,
projectId: _projectId,
} = useGetProjectPermissions(permissions, organizationId, projectId, isLoggedIn)
if (!isLoggedIn) return false
if (!IS_PLATFORM) return true
return doPermissionsCheck(allPermissions, action, resource, data, orgId)
return doPermissionsCheck(allPermissions, action, resource, data, orgId, _projectId)
}
export function usePermissionsLoaded() {
@@ -88,7 +147,17 @@ export function usePermissionsLoaded() {
const { isFetched: isPermissionsFetched } = usePermissionsQuery({ enabled: isLoggedIn })
const { isFetched: isOrganizationsFetched } = useOrganizationsQuery({ enabled: isLoggedIn })
const { ref } = useParams()
const { isFetched: isProjectDetailFetched } = useProjectDetailQuery(
{ ref },
{ enabled: !!ref && isLoggedIn }
)
if (!IS_PLATFORM) return true
if (ref) {
return isLoggedIn && isPermissionsFetched && isOrganizationsFetched && isProjectDetailFetched
}
return isLoggedIn && isPermissionsFetched && isOrganizationsFetched
}
+2 -2
View File
@@ -3,9 +3,9 @@ import { useProjectDetailQuery } from 'data/projects/project-detail-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useMemo } from 'react'
export function useSelectedProject() {
export function useSelectedProject({ enabled = true } = {}) {
const { ref } = useParams()
const { data } = useProjectDetailQuery({ ref })
const { data } = useProjectDetailQuery({ ref }, { enabled })
return useMemo(
() => data && { ...data, parentRef: data?.parent_project_ref ?? data?.ref },
+2
View File
@@ -81,6 +81,8 @@ export interface Permission {
condition: jsonLogic.RulesLogic
organization_id: number
resources: string[]
restrictive?: boolean
project_ids?: number[]
}
export interface ResponseFailure {