mirror of
https://github.com/supabase/supabase.git
synced 2026-10-18 23:59:08 +03:00
Merge branch 'hi/feat-project-level-role' into feat/project-level-permissions
This commit is contained in:
commit
453d294951
3 files changed
+88
-17
No files matched your search
@@ -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
|
||||
}
|
||||
@@ -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 },
|
||||
|
||||
@@ -81,6 +81,8 @@ export interface Permission {
|
||||
condition: jsonLogic.RulesLogic
|
||||
organization_id: number
|
||||
resources: string[]
|
||||
restrictive?: boolean
|
||||
project_ids?: number[]
|
||||
}
|
||||
|
||||
export interface ResponseFailure {
|
||||
|
||||
Reference in new issue
Block a user