Skip to content

Commit

Permalink
Add tag based permissions ui
Browse files Browse the repository at this point in the history
  • Loading branch information
kyle-ssg committed Nov 5, 2024
1 parent dcef5ae commit 82519f2
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 14 deletions.
6 changes: 6 additions & 0 deletions frontend/common/services/useRole.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export const roleService = service
query: (query: Req['getRole']) => ({
url: `organisations/${query.organisation_id}/roles/${query.role_id}/`,
}),
transformResponse: async (res: Role) => {
return {
...res,
tag_based: true,
}
},
}),
getRoles: builder.query<Res['roles'], Req['getRoles']>({
providesTags: [{ id: 'LIST', type: 'Role' }],
Expand Down
36 changes: 31 additions & 5 deletions frontend/web/components/EditPermissions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import React, { FC, forwardRef, useCallback, useEffect, useState } from 'react'
import React, {
FC,
forwardRef,
useCallback,
useEffect,
useMemo,
useState,
} from 'react'
import { find } from 'lodash'
import { close as closeIcon } from 'ionicons/icons'
import { IonIcon } from '@ionic/react'
Expand Down Expand Up @@ -61,6 +68,7 @@ import classNames from 'classnames'
import OrganisationProvider from 'common/providers/OrganisationProvider'
import { useHasPermission } from 'common/providers/Permission'
import PlanBasedAccess from './PlanBasedAccess'
import { useGetTagsQuery } from 'common/services/useTag'

const Project = require('common/project')

Expand Down Expand Up @@ -166,6 +174,14 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = withAdminPermissions(
? props.parentId
: undefined

const { data: tags, isLoading: tagsLoading } = useGetTagsQuery(
{ projectId: `${projectId}` },
{ skip: !role?.tag_based || !projectId },
)
const hasTags = useMemo(() => {
return tags?.find((v) => role?.tags.includes(v.id))
}, [tags, role?.tags])

const [permissionWasCreated, setPermissionWasCreated] =
useState<boolean>(false)
const [rolesSelected, setRolesSelected] = useState<
Expand Down Expand Up @@ -673,8 +689,16 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = withAdminPermissions(
}

const rolesAdded = getRoles(roles, rolesSelected || [])

const isAdmin = admin()
const levelUpperCase = level.toUpperCase()

if (role?.tag_based && !hasTags) {
return (
<div className='text-center py-2'>
Please add at least one tag to set permissions
</div>
)
}

return !permissions || !entityPermissions ? (
<div className='modal-body text-center'>
Expand All @@ -684,7 +708,7 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = withAdminPermissions(
<PlanBasedAccess className='px-4 pt-4' feature={'RBAC'} theme={'page'}>
<div>
<div className={classNames('modal-body', className || 'px-4 mt-4')}>
{level !== 'organisation' && (
{level !== 'organisation' && !role?.tag_based && (
<div className='mb-2'>
<Row className={role ? 'py-2' : ''}>
<Flex>
Expand All @@ -710,9 +734,11 @@ const _EditPermissionsModal: FC<EditPermissionModalType> = withAdminPermissions(
}}
title='Permissions'
className='no-pad mb-2'
items={permissions}
items={permissions?.filter((item) => {
if (item.key === `VIEW_${levelUpperCase}`) return true
return !(role?.tag_based && !item.supports_tag)
})}
renderRow={(p: AvailablePermission) => {
const levelUpperCase = level.toUpperCase()
const disabled =
level !== 'organisation' &&
p.key !== `VIEW_${levelUpperCase}` &&
Expand Down
7 changes: 4 additions & 3 deletions frontend/web/components/PermissionsTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ const PermissionsTabs: FC<PermissionsTabsType> = ({
theme='pill m-0'
isRoles={true}
>
{role?.tag_based !== false && (
{role?.tag_based !== true && (
<TabItem
tabLabel={
<Row className='justify-content-center'>Organisation</Row>
Expand Down Expand Up @@ -155,8 +155,9 @@ const PermissionsTabs: FC<PermissionsTabsType> = ({
value={project}
/>
</div>

<TagBasedPermissions projectId={project} role={role} />
<div className='my-4'>
<TagBasedPermissions projectId={project} role={role} />
</div>
<div className='mt-2'>
{environments.length > 0 && (
<RolePermissionsList
Expand Down
8 changes: 6 additions & 2 deletions frontend/web/components/RolePermissionsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React, {
forwardRef,
Ref,
useImperativeHandle,
useMemo,
useState,
} from 'react'
import Icon from './Icon'
Expand All @@ -16,6 +17,8 @@ import { Role, User, UserGroup, UserGroupSummary } from 'common/types/responses'
import PanelSearch from './PanelSearch'
import PermissionsSummaryList from './PermissionsSummaryList'
import TagBasedPermissions from './TagBasedPermissions'
import { useGetTagsQuery } from 'common/services/useTag'
import classNames from 'classnames'

type NameAndId = {
name: string
Expand All @@ -30,6 +33,7 @@ type RolePermissionsListProps = {
level: PermissionLevel
filter: string
orgId?: string
projectId: string
user?: User
group?: UserGroupSummary
}
Expand Down Expand Up @@ -82,7 +86,7 @@ const PermissionsSummary: FC<PermissionsSummaryType> = ({
}

const RolePermissionsList: React.FC<RolePermissionsListProps> = forwardRef(
({ filter, group, level, mainItems, orgId, role, user }, ref) => {
({ filter, group, level, mainItems, orgId, projectId, role, user }, ref) => {
const [expandedItems, setExpandedItems] = useState<(string | number)[]>([])

const mainItemsFiltered =
Expand Down Expand Up @@ -171,7 +175,7 @@ const RolePermissionsList: React.FC<RolePermissionsListProps> = forwardRef(
</div>
)}
items={mainItemsFiltered || []}
className='no-pad'
className='no-pad overflow-visible'
/>
)
},
Expand Down
5 changes: 3 additions & 2 deletions frontend/web/components/TagBasedPermissions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ const TagBasedPermissions: FC<TaggedPermissionsType> = ({
{ skip: !projectId || !role },
)
const showTagBasedPermissions = projectId && tag_based_permissions && !!role
const [tagBasedPermissionsEnabled, setTagBasedPermissionsEnabled] =
useState<boolean>(!!role?.tags?.length)
const matchingTags = useMemo(() => {
if (!role?.tags || !tags?.length) return []
return role.tags.filter((id) => tags?.find((tag) => tag.id === id))
Expand All @@ -37,6 +35,9 @@ const TagBasedPermissions: FC<TaggedPermissionsType> = ({
<>
{role?.tag_based && (
<div className='mb-2'>
<div className='mt-2 text-body fw-bold'>
Enable permissions for the following tags:
</div>
<AddEditTags
projectId={projectId}
value={matchingTags}
Expand Down
28 changes: 27 additions & 1 deletion frontend/web/components/modals/CreateRole.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import Input from 'components/base/forms/Input'
import SettingsButton from 'components/SettingsButton'
import PermissionsTabs from 'components/PermissionsTabs'
import AccountStore from 'common/stores/account-store'
import Switch from 'components/Switch'

type TabRef = {
onClosing: () => Promise<void>
Expand Down Expand Up @@ -87,6 +88,8 @@ const CreateRole: FC<CreateRoleType> = ({
user: number
}[]
>([])

const [tagBased, setTagBased] = useState(!!role?.tag_based)
const [groupSelected, setGroupSelected] = useState<
{
group: number
Expand Down Expand Up @@ -309,6 +312,7 @@ const CreateRole: FC<CreateRoleType> = ({
)
useEffect(() => {
if (!isLoading && isEdit && roleData) {
setTagBased(roleData.tag_based)
setRoleName(roleData.name)
setRoleDesc(roleData.description || '')
}
Expand All @@ -331,7 +335,7 @@ const CreateRole: FC<CreateRoleType> = ({
if (!organisationId) return
if (isEdit && role) {
editRole({
body: { description: roleDesc, name: roleName },
body: { description: roleDesc, name: roleName, tag_based: tagBased },
organisation_id: role.organisation,
role_id: role.id,
})
Expand All @@ -340,6 +344,7 @@ const CreateRole: FC<CreateRoleType> = ({
description: roleDesc,
name: roleName,
organisation_id: organisationId,
tag_based: tagBased,
})
}
}
Expand All @@ -365,6 +370,27 @@ const CreateRole: FC<CreateRoleType> = ({
id='roleName'
placeholder='E.g. Viewers'
/>
<InputGroup
title='Use tag-based permissions'
tooltip={
'Tag-based roles will grant permissions to a selected set of feature tags'
}
value={tagBased}
component={
<Switch
disabled={!!role}
checked={tagBased}
onChange={setTagBased}
/>
}
unsaved={isEdit && roleDescChanged}
onChange={(event: InputEvent) => {
setRoleDescChanged(true)
setRoleDesc(Utils.safeParseEventValue(event))
}}
id='description'
placeholder='E.g. Some role description'
/>
<InputGroup
title='Description'
inputProps={{
Expand Down
4 changes: 3 additions & 1 deletion frontend/web/styles/components/_panel.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@
}
}
}

.overflow-visible>.panel-content {
overflow: visible;
}
.dark {
.panel {
.icon {
Expand Down

0 comments on commit 82519f2

Please sign in to comment.