Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: tag based permissions validation #4815

Open
wants to merge 1 commit into
base: feat/tag-based-permissions
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions frontend/common/providers/Permission.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, { FC, ReactNode } from 'react'
import React, { FC, ReactNode, useMemo } from 'react'
import { useGetPermissionQuery } from 'common/services/usePermission'
import { PermissionLevel } from 'common/types/requests'
import AccountStore from 'common/stores/account-store' // we need this to make JSX compile
import AccountStore from 'common/stores/account-store'
import intersection from 'lodash/intersection' // we need this to make JSX compile

type PermissionType = {
id: any
permission: string
tags?: number[]
level: PermissionLevel
children: (data: { permission: boolean; isLoading: boolean }) => ReactNode
}
Expand All @@ -14,11 +16,23 @@ export const useHasPermission = ({
id,
level,
permission,
tags,
}: Omit<PermissionType, 'children'>) => {
const { data, isLoading, isSuccess } = useGetPermissionQuery(
{ id: `${id}`, level },
{ skip: !id || !level },
)
const {
data: permissionsData,
isLoading,
isSuccess,
} = useGetPermissionQuery({ id: `${id}`, level }, { skip: !id || !level })
const data = useMemo(() => {
if (!tags?.length || !permissionsData?.tag_based_permissions) return permissionsData
const addedPermissions = permissionsData
permissionsData.tag_based_permissions.forEach((tagBasedPermission) => {
if (intersection(tagBasedPermission.tags, tags).length) {
addedPermissions[tagBasedPermission.key] = true
}
})
return addedPermissions
}, [permissionsData, tags])
const hasPermission = !!data?.[permission] || !!data?.ADMIN
return {
isLoading,
Expand All @@ -32,11 +46,13 @@ const Permission: FC<PermissionType> = ({
id,
level,
permission,
tags,
}) => {
const { isLoading, permission: hasPermission } = useHasPermission({
id,
level,
permission,
tags,
})
return (
<>
Expand Down
17 changes: 13 additions & 4 deletions frontend/common/services/usePermission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,25 @@ export const permissionService = service
query: ({ id, level }: Req['getPermission']) => ({
url: `${level}s/${id}/my-permissions/`,
}),
transformResponse(baseQueryReturnValue: {
admin: boolean
permissions: string[]
}) {
transformResponse(
baseQueryReturnValue: {
admin: boolean
permissions: string[]
tag_based_permissions?: Res['permission']['tag_based_permissions']
},
_,
) {
const res: Res['permission'] = {
ADMIN: baseQueryReturnValue.admin,
}
if (baseQueryReturnValue.tag_based_permissions) {
res.tag_based_permissions =
baseQueryReturnValue.tag_based_permissions
}
baseQueryReturnValue.permissions.forEach((v) => {
res[v] = true
})

return res
},
}),
Expand Down
5 changes: 4 additions & 1 deletion frontend/common/types/responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,10 @@ export type Res = {
}
identity: { id: string } //todo: we don't consider this until we migrate identity-store
identities: EdgePagedResponse<Identity>
permission: Record<string, boolean>
permission: Record<string, boolean> & {
ADMIN: boolean
tag_based_permissions?: { key: string; tags: number[] }[]
}
availablePermissions: AvailablePermission[]
tag: Tag
tags: Tag[]
Expand Down
2 changes: 2 additions & 0 deletions frontend/web/components/CompareEnvironments.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ class CompareEnvironments extends Component {
</div>
<Permission
level='environment'
tags={p.projectFlagLeft.tags}
permission={Utils.getManageFeaturePermission(
Utils.changeRequestsEnabled(
environmentLeft.minimum_change_request_approvals,
Expand Down Expand Up @@ -270,6 +271,7 @@ class CompareEnvironments extends Component {
</Permission>
<Permission
level='environment'
tags={p.projectFlagLeft.tags}
permission={Utils.getManageFeaturePermission(
Utils.changeRequestsEnabled(
environmentRight.minimum_change_request_approvals,
Expand Down
1 change: 1 addition & 0 deletions frontend/web/components/CompareFeatures.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ class CompareEnvironments extends Component {
)
return (
<Permission
tags={this.state.flag?.tags}
level='environment'
permission={Utils.getManageFeaturePermission(
changeRequestsEnabled,
Expand Down
3 changes: 3 additions & 0 deletions frontend/web/components/FeatureAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ interface FeatureActionProps {
projectId: string
featureIndex: number
readOnly: boolean
tags: number[]
protectedTags: Tag[] | undefined
hideAudit: boolean
hideHistory: boolean
Expand Down Expand Up @@ -54,6 +55,7 @@ export const FeatureAction: FC<FeatureActionProps> = ({
projectId,
protectedTags,
readOnly,
tags,
}) => {
const [isOpen, setIsOpen] = useState<boolean>(false)

Expand Down Expand Up @@ -147,6 +149,7 @@ export const FeatureAction: FC<FeatureActionProps> = ({
<Permission
level='project'
permission='DELETE_FEATURE'
tags={tags}
id={projectId}
>
{({ permission: removeFeaturePermission }) =>
Expand Down
29 changes: 15 additions & 14 deletions frontend/web/components/FeatureRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,20 +141,20 @@ class TheComponent extends Component {
const changeRequestsEnabled = Utils.changeRequestsEnabled(
environment && environment.minimum_change_request_approvals,
)
const onChange = ()=> {
if(disableControls) {
return;
}
if (
projectFlag?.multivariate_options?.length ||
Utils.changeRequestsEnabled(
environment.minimum_change_request_approvals,
)
) {
this.editFeature(projectFlag, environmentFlags[id])
return
}
this.confirmToggle()
const onChange = () => {
if (disableControls) {
return
}
if (
projectFlag?.multivariate_options?.length ||
Utils.changeRequestsEnabled(
environment.minimum_change_request_approvals,
)
) {
this.editFeature(projectFlag, environmentFlags[id])
return
}
this.confirmToggle()
}
const isCompact = getViewMode() === 'compact'
if (this.props.condensed) {
Expand Down Expand Up @@ -381,6 +381,7 @@ class TheComponent extends Component {
featureIndex={this.props.index}
readOnly={readOnly}
protectedTags={protectedTags}
tags={projectFlag.tags}
isCompact={isCompact}
hideAudit={
AccountStore.getOrganisationRole() !== 'ADMIN' ||
Expand Down
Loading