Files
supabase/apps/studio/hooks/misc/useCheckPermissions.ts
T
1127c4ba88 Project Level Permissions (#27347)
* fix: update Permission params

* fix: upgrade check permission hook to support project level role

* fix: usePermissionsLoaded

* fix: Permission params can be undefined

* Scaffold new access management UI

* Add validation

* Update roles view

* Add tooltip

* Add button to apply role to all projects

* Update UI to select projects first instead of roles

* Merge master update UI

* Midway trying to implementation project level perms API

* First pass implementating updating project level permissions

* Add client side validation for assigning/removing roles

* Midway implementing new invites

* Integrate most of the project level permissions functionality

* fix: filter out org-level permissions before checking

* Add relevant UI guards in org level pages for project role POV

* Minor refactors

* Small refactors

* More fixes

* Moar refactors

* More fixes

* More fixes

* Refactor update role logic and smack some test cases on it

* Fixes

* Fix type issue

* Fix type

* more fixes, refactors, adding checks...

* MORE fixes

* Add perms checking for replicas

* Add ButtonTooltip component and use them to prevent repetition of pointer events auto for buttons with tooltips

* Convert all buttons with tooltips to use ButtonTooltip

* refactor

* PRettier

* Small fix

* Remove commented out code in organization-invitation-accept-mutation

* fix: switch to use the platform oauth authorizations routes

* Add perms checking for org audit logs and org oauth apps

* PRettier

* Fix incorrect URL for oauth app flow

* Fix incorrect URL for oauth app flow

* Fix

* Add perms checking for warehouse related UI

* Update roles helper icon

* remove unused lib

* Update package lock... again

* Update package lock... again

* Smalllll update

* Update some checks

* Add gate for project level permissions

* Last fix

* update codegen

* Update warehouse endpoint routes

* Fix

---------

Co-authored-by: phamhieu <phamhieu1998@gmail.com>
Co-authored-by: Alaister Young <a@alaisteryoung.com>
2024-07-01 17:59:54 +08:00

172 lines
5.3 KiB
TypeScript

import { useIsLoggedIn, useParams } from 'common'
import jsonLogic from 'json-logic-js'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
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,
projectId?: number
) {
if (!permissions || !Array.isArray(permissions)) {
return false
}
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))) &&
permission.project_ids?.includes(projectId)
)
if (projectPermissions.length > 0) {
return doPermissionConditionCheck(projectPermissions, { resource_name: resource, ...data })
}
}
const orgPermissions = permissions
// filter out org-level permission
.filter((permission) => !permission.project_ids || permission.project_ids.length === 0)
.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,
})
const permissions =
permissionsOverride === undefined ? permissionsResult.data : permissionsOverride
const organizationResult = useSelectedOrganization({
enabled: organizationIdOverride === undefined && enabled,
})
const organization =
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,
}
}
export function useCheckPermissions(
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,
permissions?: Permission[]
) {
return useCheckProjectPermissions(action, resource, data, {
organizationId,
projectId: undefined,
permissions,
})
}
export function useCheckProjectPermissions(
action: string,
resource: string,
data?: object,
overrides?: {
organizationId?: number
projectId?: number
permissions?: Permission[]
}
) {
const isLoggedIn = useIsLoggedIn()
const { organizationId, projectId, permissions } = overrides ?? {}
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, _projectId)
}
export function usePermissionsLoaded() {
const isLoggedIn = useIsLoggedIn()
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
}