mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 09:55:06 +03:00
Implement ABAC in greater granularity for roles management in org team members
This commit is contained in:
1 parent
18fcc1d1ec
commit
ee3e598423
7 files changed
+232
-136
No files matched your search
@@ -11,7 +11,9 @@ export function isInviteExpired(timestamp: string) {
|
||||
return true
|
||||
}
|
||||
|
||||
export const getUserDisplayName = (user: Member) => {
|
||||
export const getUserDisplayName = (user?: Member) => {
|
||||
if (!user) return ''
|
||||
|
||||
const { username, primary_email, invited_id } = user || {}
|
||||
return invited_id !== undefined ? primary_email : username || ''
|
||||
}
|
||||
+68
-47
@@ -4,17 +4,19 @@ import { FC, useEffect, useState } from 'react'
|
||||
import { object, string } from 'yup'
|
||||
import { Button, Form, IconMail, Input, Modal, Select } from '@supabase/ui'
|
||||
|
||||
import { Member, User } from 'types'
|
||||
import { useFlag, useOrganizationDetail, useOrganizationRoles, useStore } from 'hooks'
|
||||
import { Member, User, Role } from 'types'
|
||||
import { useFlag, useOrganizationDetail, useStore } from 'hooks'
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
|
||||
interface Props {
|
||||
members: Member[]
|
||||
user: User
|
||||
members: Member[]
|
||||
roles: Role[]
|
||||
rolesAddable: Number[]
|
||||
}
|
||||
|
||||
const InviteMemberModal: FC<Props> = ({ members = [], user }) => {
|
||||
const InviteMemberButton: FC<Props> = ({ user, members = [], roles = [], rolesAddable = [] }) => {
|
||||
const { ui } = useStore()
|
||||
const router = useRouter()
|
||||
const { slug } = router.query
|
||||
@@ -22,7 +24,6 @@ const InviteMemberModal: FC<Props> = ({ members = [], user }) => {
|
||||
const enablePermissions = useFlag('enablePermissions')
|
||||
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const { roles } = useOrganizationRoles((slug as string) || '')
|
||||
const { mutateOrgMembers } = useOrganizationDetail((slug as string) || '')
|
||||
|
||||
const initialValues = { email: '', role: '' }
|
||||
@@ -92,66 +93,86 @@ const InviteMemberModal: FC<Props> = ({ members = [], user }) => {
|
||||
<>
|
||||
<Button onClick={() => setIsOpen(true)}>Invite</Button>
|
||||
<Modal
|
||||
size="small"
|
||||
hideFooter
|
||||
size="medium"
|
||||
layout="vertical"
|
||||
className="!overflow-visible"
|
||||
visible={isOpen}
|
||||
onCancel={() => setIsOpen(false)}
|
||||
header="Invite a member to this organization"
|
||||
description="Members you'd like to invite must already be registered on Supabase"
|
||||
layout="vertical"
|
||||
hideFooter
|
||||
>
|
||||
<Modal.Content>
|
||||
<Form validationSchema={schema} initialValues={initialValues} onSubmit={onInviteMember}>
|
||||
{({ isSubmitting, resetForm }: any) => {
|
||||
// Catches 'roles' when its available and then adds a default value for role select
|
||||
useEffect(() => {
|
||||
if (roles) {
|
||||
resetForm({
|
||||
values: { ...initialValues, role: roles[0].id },
|
||||
initialValues: { ...initialValues, role: roles[0].id },
|
||||
})
|
||||
}
|
||||
}, [roles])
|
||||
<Form validationSchema={schema} initialValues={initialValues} onSubmit={onInviteMember}>
|
||||
{({ values, isSubmitting, resetForm }: any) => {
|
||||
// Catches 'roles' when its available and then adds a default value for role select
|
||||
useEffect(() => {
|
||||
if (roles) {
|
||||
resetForm({
|
||||
values: { ...initialValues, role: roles[0].id },
|
||||
initialValues: { ...initialValues, role: roles[0].id },
|
||||
})
|
||||
}
|
||||
}, [roles])
|
||||
|
||||
return (
|
||||
<div className="w-full py-4">
|
||||
<div className="space-y-4">
|
||||
{roles && enablePermissions && (
|
||||
<Select name="role" label="Member role">
|
||||
{roles.map((role: any) => (
|
||||
<Select.Option key={role.id} value={role.id}>
|
||||
{role.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
const selectedRole = roles.find((role) => role.id === Number(values.role))
|
||||
const invalidRoleSelected = enablePermissions
|
||||
? values.role && !rolesAddable.includes(Number(values.role))
|
||||
: false
|
||||
|
||||
<Input
|
||||
autoFocus
|
||||
id="email"
|
||||
icon={<IconMail />}
|
||||
placeholder="Enter email address"
|
||||
label="Email address"
|
||||
/>
|
||||
return (
|
||||
<>
|
||||
<Modal.Content>
|
||||
<div className="w-full py-4">
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
{roles && enablePermissions && (
|
||||
<Select
|
||||
name="role"
|
||||
label="Member role"
|
||||
error={
|
||||
invalidRoleSelected
|
||||
? `You need additional permissions to assign users the role of ${selectedRole?.name}`
|
||||
: ''
|
||||
}
|
||||
>
|
||||
{roles.map((role: any) => (
|
||||
<Select.Option key={role.id} value={role.id}>
|
||||
{role.name}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Input
|
||||
autoFocus
|
||||
id="email"
|
||||
icon={<IconMail />}
|
||||
placeholder="Enter email address"
|
||||
label="Email address"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Modal.Content>
|
||||
<Modal.Seperator />
|
||||
<Modal.Content>
|
||||
<div className="pt-2 pb-3">
|
||||
<Button
|
||||
block
|
||||
size="medium"
|
||||
htmlType="submit"
|
||||
disabled={isSubmitting}
|
||||
disabled={isSubmitting || invalidRoleSelected}
|
||||
loading={isSubmitting}
|
||||
>
|
||||
Invite new member
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</Form>
|
||||
</Modal.Content>
|
||||
</Modal.Content>
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default InviteMemberModal
|
||||
export default InviteMemberButton
|
||||
+68
-20
@@ -1,9 +1,11 @@
|
||||
import { FC, useState, useContext } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { Button, Dropdown, IconTrash, IconMoreHorizontal } from '@supabase/ui'
|
||||
|
||||
import { Member, Role } from 'types'
|
||||
import { useStore, useOrganizationDetail, useFlag } from 'hooks'
|
||||
import { useStore, useOrganizationDetail, useFlag, checkPermissions } from 'hooks'
|
||||
import { delete_, post, patch } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import TextConfirmModal from 'components/ui/Modals/TextConfirmModal'
|
||||
@@ -18,8 +20,9 @@ interface Props {
|
||||
roles: Role[]
|
||||
}
|
||||
|
||||
const OwnerDropdown: FC<Props> = ({ members, member, roles }) => {
|
||||
const MemberActions: FC<Props> = ({ members, member, roles }) => {
|
||||
const PageState: any = useContext(PageContext)
|
||||
const { rolesRemovable } = PageState
|
||||
const { slug, name: orgName } = PageState.organization
|
||||
|
||||
const { ui } = useStore()
|
||||
@@ -29,6 +32,20 @@ const OwnerDropdown: FC<Props> = ({ members, member, roles }) => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [ownerTransferIsVisible, setOwnerTransferIsVisible] = useState(false)
|
||||
|
||||
const isPendingInviteAcceptance = member.invited_id
|
||||
|
||||
const canRemoveMember = enablePermissions
|
||||
? rolesRemovable.includes((member?.role_ids ?? [-1])[0])
|
||||
: true
|
||||
const canResendInvite = checkPermissions(
|
||||
PermissionAction.SQL_INSERT,
|
||||
'postgres.auth.user_invites'
|
||||
)
|
||||
const canRevokeInvite = checkPermissions(
|
||||
PermissionAction.SQL_DELETE,
|
||||
'postgres.auth.user_invites'
|
||||
)
|
||||
|
||||
const handleMemberDelete = async () => {
|
||||
confirmAlert({
|
||||
title: 'Confirm to remove',
|
||||
@@ -144,6 +161,31 @@ const OwnerDropdown: FC<Props> = ({ members, member, roles }) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!canRemoveMember || (isPendingInviteAcceptance && !canResendInvite && !canRevokeInvite)) {
|
||||
return (
|
||||
<div className="flex items-center justify-end">
|
||||
<Tooltip.Root delayDuration={0}>
|
||||
<Tooltip.Trigger>
|
||||
<Button as="span" type="text" icon={<IconMoreHorizontal />} />
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side="bottom">
|
||||
<Tooltip.Arrow className="radix-tooltip-arrow" />
|
||||
<div
|
||||
className={[
|
||||
'bg-scale-100 rounded py-1 px-2 leading-none shadow', // background
|
||||
'border-scale-200 border ', //border
|
||||
].join(' ')}
|
||||
>
|
||||
<span className="text-scale-1200 text-xs">
|
||||
You need additional permissions to manage this team member
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-end">
|
||||
<Dropdown
|
||||
@@ -151,7 +193,7 @@ const OwnerDropdown: FC<Props> = ({ members, member, roles }) => {
|
||||
align="end"
|
||||
overlay={
|
||||
<>
|
||||
{!enablePermissions && !member.invited_at && (
|
||||
{!enablePermissions && !isPendingInviteAcceptance && (
|
||||
<>
|
||||
<Dropdown.Item onClick={() => setOwnerTransferIsVisible(!ownerTransferIsVisible)}>
|
||||
<div className="flex flex-col">
|
||||
@@ -162,25 +204,31 @@ const OwnerDropdown: FC<Props> = ({ members, member, roles }) => {
|
||||
<Dropdown.Seperator />
|
||||
</>
|
||||
)}
|
||||
{member.invited_at ? (
|
||||
{isPendingInviteAcceptance ? (
|
||||
<>
|
||||
<Dropdown.Item onClick={() => handleRevokeInvitation(member)}>
|
||||
<div className="flex flex-col">
|
||||
<p>Cancel invitation</p>
|
||||
<p className="block opacity-50">Revoke this invitation.</p>
|
||||
</div>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Seperator />
|
||||
<Dropdown.Item onClick={() => handleResendInvite(member)}>
|
||||
<div className="flex flex-col">
|
||||
<p>Resend invitation</p>
|
||||
<p className="block opacity-50">Invites expire after 24hrs.</p>
|
||||
</div>
|
||||
</Dropdown.Item>
|
||||
{canResendInvite && (
|
||||
<Dropdown.Item onClick={() => handleRevokeInvitation(member)}>
|
||||
<div className="flex flex-col">
|
||||
<p>Cancel invitation</p>
|
||||
<p className="block opacity-50">Revoke this invitation.</p>
|
||||
</div>
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
{canRevokeInvite && (
|
||||
<>
|
||||
<Dropdown.Seperator />
|
||||
<Dropdown.Item onClick={() => handleResendInvite(member)}>
|
||||
<div className="flex flex-col">
|
||||
<p>Resend invitation</p>
|
||||
<p className="block opacity-50">Invites expire after 24hrs.</p>
|
||||
</div>
|
||||
</Dropdown.Item>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Dropdown.Item icon={<IconTrash size="tiny" />} onClick={handleMemberDelete}>
|
||||
Remove member
|
||||
<Dropdown.Item icon={<IconTrash size={16} />} onClick={handleMemberDelete}>
|
||||
<p>Remove member</p>
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
</>
|
||||
@@ -217,4 +265,4 @@ const OwnerDropdown: FC<Props> = ({ members, member, roles }) => {
|
||||
)
|
||||
}
|
||||
|
||||
export default observer(OwnerDropdown)
|
||||
export default observer(MemberActions)
|
||||
@@ -3,24 +3,16 @@ import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { useState, useContext, Fragment } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Badge, Button, Loading, Listbox, IconUser, Modal } from '@supabase/ui'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
import { Member } from 'types'
|
||||
import {
|
||||
checkPermissions,
|
||||
useStore,
|
||||
useFlag,
|
||||
useOrganizationDetail,
|
||||
useOrganizationRoles,
|
||||
} from 'hooks'
|
||||
import { Member, Role } from 'types'
|
||||
import { useStore, useFlag, useOrganizationDetail } from 'hooks'
|
||||
import { patch } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { isInviteExpired, getUserDisplayName } from '../Organization.utils'
|
||||
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import OwnerDropdown from './OwnerDropdown'
|
||||
import MemberActions from './MemberActions'
|
||||
import { PageContext } from 'pages/org/[slug]/settings'
|
||||
import { patch } from 'lib/common/fetch'
|
||||
import { getRolesManagementPermissions } from './TeamSettings.utils'
|
||||
|
||||
interface SelectedMember extends Member {
|
||||
oldRoleId: number
|
||||
@@ -29,23 +21,23 @@ interface SelectedMember extends Member {
|
||||
|
||||
const MembersView = () => {
|
||||
const PageState: any = useContext(PageContext)
|
||||
const {
|
||||
roles,
|
||||
rolesAddable,
|
||||
rolesRemovable,
|
||||
}: { roles: Role[]; rolesAddable: Number[]; rolesRemovable: Number[] } = PageState
|
||||
|
||||
const { ui } = useStore()
|
||||
const slug = ui.selectedOrganization?.slug || ''
|
||||
|
||||
const enablePermissions = useFlag('enablePermissions')
|
||||
const { roles } = useOrganizationRoles(slug)
|
||||
const { mutateOrgMembers } = useOrganizationDetail(slug)
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [selectedMember, setSelectedMember] = useState<SelectedMember>()
|
||||
const [userRoleChangeModalVisible, setUserRoleChangeModalVisible] = useState(false)
|
||||
|
||||
const rolesPermissions = getRolesManagementPermissions(roles)
|
||||
const canEditMemberRoles = checkPermissions(
|
||||
PermissionAction.SQL_INSERT,
|
||||
'postgres.auth.subject_roles'
|
||||
)
|
||||
const hasAccessToMemberActions = enablePermissions ? true : PageState.isOrgOwner
|
||||
|
||||
const getRoleNameById = (id: number | undefined) => {
|
||||
if (!roles) return id
|
||||
@@ -102,28 +94,27 @@ const MembersView = () => {
|
||||
const role = (roles || []).find((role) => role.id === memberRoleId)
|
||||
const memberIsUser = x.primary_email == PageState.user.primary_email
|
||||
const memberIsPendingInvite = !!x.invited_id
|
||||
const disableRoleEdit = !canEditMemberRoles || memberIsUser || memberIsPendingInvite
|
||||
const canRemoveRole = rolesRemovable.includes(memberRoleId)
|
||||
const disableRoleEdit = !canRemoveRole || memberIsUser || memberIsPendingInvite
|
||||
|
||||
const validateSelectedRoleToChange = (roleId: any) => {
|
||||
if (!role) return
|
||||
|
||||
const selectedRole = (roles || []).find((role) => role.id === roleId)
|
||||
const rolePermission = rolesPermissions[roleId]
|
||||
if (!rolePermission || !rolePermission.canChangeTo) {
|
||||
const canAddRole = rolesAddable.includes(selectedRole?.id ?? -1)
|
||||
|
||||
if (!canAddRole) {
|
||||
return ui.setNotification({
|
||||
category: 'error',
|
||||
message: `You do not have permission to update members to ${
|
||||
duration: 4000,
|
||||
message: `You do not have permission to update this team member to ${
|
||||
selectedRole!.name
|
||||
}`,
|
||||
})
|
||||
}
|
||||
|
||||
setUserRoleChangeModalVisible(true)
|
||||
setSelectedMember({
|
||||
...x,
|
||||
oldRoleId: role.id,
|
||||
newRoleId: roleId,
|
||||
})
|
||||
setSelectedMember({ ...x, oldRoleId: role.id, newRoleId: roleId })
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -203,7 +194,7 @@ const MembersView = () => {
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
) : !canEditMemberRoles ? (
|
||||
) : !canRemoveRole ? (
|
||||
<Tooltip.Content side="bottom">
|
||||
<Tooltip.Arrow className="radix-tooltip-arrow" />
|
||||
<div
|
||||
@@ -213,8 +204,8 @@ const MembersView = () => {
|
||||
].join(' ')}
|
||||
>
|
||||
<span className="text-scale-1200 text-xs">
|
||||
You need additional permissions to update the role of other
|
||||
members in this organization
|
||||
You need additional permissions to update the role of this
|
||||
team member
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip.Content>
|
||||
@@ -227,8 +218,8 @@ const MembersView = () => {
|
||||
)}
|
||||
</Table.td>
|
||||
<Table.td>
|
||||
{PageState.isOrgOwner && !memberIsUser && (
|
||||
<OwnerDropdown members={PageState.members} member={x} roles={roles} />
|
||||
{hasAccessToMemberActions && !memberIsUser && (
|
||||
<MemberActions members={PageState.members} member={x} roles={roles} />
|
||||
)}
|
||||
</Table.td>
|
||||
</Table.tr>
|
||||
@@ -253,23 +244,24 @@ const MembersView = () => {
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
visible={userRoleChangeModalVisible}
|
||||
hideFooter
|
||||
size="medium"
|
||||
visible={userRoleChangeModalVisible}
|
||||
onCancel={() => setUserRoleChangeModalVisible(false)}
|
||||
header="Change role of member"
|
||||
size="small"
|
||||
>
|
||||
<div className="flex flex-col gap-2 my-3">
|
||||
<div className="flex flex-col gap-2 py-4">
|
||||
<Modal.Content>
|
||||
<p className="text-sm text-scale-1200 mb-3">
|
||||
By changing the role of this member their permissions will change.
|
||||
</p>
|
||||
<p className="text-sm text-scale-1100">
|
||||
You are going to change the role of {selectedMember?.primary_email} from{' '}
|
||||
You are changing the role of{' '}
|
||||
<span className="text-scale-1200">{getUserDisplayName(selectedMember)}</span> from{' '}
|
||||
<span className="text-scale-1200">{getRoleNameById(selectedMember?.oldRoleId)}</span>{' '}
|
||||
to{' '}
|
||||
<span className="text-scale-1200">{getRoleNameById(selectedMember?.newRoleId)}</span>
|
||||
</p>
|
||||
<p className="text-sm text-scale-1200 mt-3">
|
||||
By changing the role of this member their permissions will change.
|
||||
</p>
|
||||
</Modal.Content>
|
||||
<Modal.Seperator />
|
||||
<Modal.Content>
|
||||
|
||||
@@ -2,27 +2,27 @@ import { useState, useContext } from 'react'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { Button, Input, IconSearch } from '@supabase/ui'
|
||||
|
||||
import { checkPermissions, useFlag, useStore } from 'hooks'
|
||||
import { useFlag, useStore } from 'hooks'
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { API_URL } from 'lib/constants'
|
||||
import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal'
|
||||
import InviteMemberModal from './InviteMemberModal'
|
||||
import InviteMemberModal from './InviteMemberButton'
|
||||
import MembersView from './MembersView'
|
||||
import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal'
|
||||
|
||||
import { PageContext } from 'pages/org/[slug]/settings'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
const TeamSettings = observer(() => {
|
||||
const PageState: any = useContext(PageContext)
|
||||
const { ui } = useStore()
|
||||
const [isLeaving, setIsLeaving] = useState(false)
|
||||
const enablePermissions = useFlag('enablePermissions')
|
||||
const { user, members, roles, rolesAddable } = PageState
|
||||
|
||||
const slug = ui.selectedOrganization?.slug
|
||||
const { ui } = useStore()
|
||||
const slug = ui.selectedOrganization?.slug ?? ''
|
||||
const isOrgOwner = ui.selectedOrganization?.is_owner
|
||||
const canAddMembers = enablePermissions
|
||||
? checkPermissions(PermissionAction.SQL_INSERT, 'postgres.auth.subject_roles')
|
||||
: isOrgOwner
|
||||
|
||||
const enablePermissions = useFlag('enablePermissions')
|
||||
const [isLeaving, setIsLeaving] = useState(false)
|
||||
|
||||
const canAddMembers = enablePermissions ? rolesAddable.length > 0 : isOrgOwner
|
||||
|
||||
function onFilterMemberChange(e: any) {
|
||||
PageState.membersFilterString = e.target.value
|
||||
@@ -69,7 +69,12 @@ const TeamSettings = observer(() => {
|
||||
<div className="flex items-center space-x-4">
|
||||
{canAddMembers && (
|
||||
<div>
|
||||
<InviteMemberModal members={PageState.members} user={PageState.user} />
|
||||
<InviteMemberModal
|
||||
user={user}
|
||||
members={members}
|
||||
roles={roles}
|
||||
rolesAddable={rolesAddable}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!isOrgOwner && (
|
||||
|
||||
@@ -2,28 +2,48 @@ import { Role } from 'types'
|
||||
import { checkPermissions } from 'hooks'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
|
||||
export const getRolesManagementPermissions = (roles: Role[]) => {
|
||||
if (!roles) return roles
|
||||
export const getRolesManagementPermissions = (
|
||||
roles: Role[]
|
||||
): { rolesAddable: Number[]; rolesRemovable: Number[] } => {
|
||||
const rolesAddable: Number[] = []
|
||||
const rolesRemovable: Number[] = []
|
||||
if (!roles) return { rolesAddable, rolesRemovable }
|
||||
|
||||
const rolesPermissions: any = {}
|
||||
const availableRoles = ['Owner', 'Administrator', 'Developer']
|
||||
|
||||
// [Joshen TODO] Hardcoded logic until BE is ready
|
||||
availableRoles.forEach((role: string) => {
|
||||
const selectedRole = roles.find((r) => r.name === role)
|
||||
if (!selectedRole) return
|
||||
|
||||
const canChangeTo = checkPermissions(
|
||||
PermissionAction.SQL_INSERT,
|
||||
'postgres.auth.subject_roles',
|
||||
{ resource: { role_id: selectedRole!.id } }
|
||||
)
|
||||
const canChangeFrom = checkPermissions(
|
||||
PermissionAction.SQL_DELETE,
|
||||
'postgres.auth.subject_roles',
|
||||
{ resource: { role_id: selectedRole!.id } }
|
||||
)
|
||||
rolesPermissions[selectedRole!.id] = { canChangeTo, canChangeFrom }
|
||||
// This is if the logged in user is an Owner
|
||||
rolesAddable.push(selectedRole.id)
|
||||
rolesRemovable.push(selectedRole.id)
|
||||
|
||||
// This is if the logged in user is an Admin
|
||||
// if (role !== 'Owner') {
|
||||
// rolesAddable.push(selectedRole.id)
|
||||
// rolesRemovable.push(selectedRole.id)
|
||||
// }
|
||||
|
||||
// Just comment out everything if doing for developer
|
||||
})
|
||||
|
||||
return rolesPermissions
|
||||
// [Joshen TODO] This is the correct logic but BE is not ready
|
||||
// availableRoles.forEach((role: string) => {
|
||||
// const selectedRole = roles.find((r) => r.name === role)
|
||||
// if (!selectedRole) return
|
||||
|
||||
// const canAdd = checkPermissions(PermissionAction.SQL_INSERT, 'postgres.auth.subject_roles', {
|
||||
// resource: { role_id: selectedRole!.id },
|
||||
// })
|
||||
// if (canAdd) rolesAddable.push(selectedRole.id)
|
||||
|
||||
// const canRemove = checkPermissions(PermissionAction.SQL_DELETE, 'postgres.auth.subject_roles', {
|
||||
// resource: { role_id: selectedRole!.id },
|
||||
// })
|
||||
// if (canRemove) rolesRemovable.push(selectedRole.id)
|
||||
// })
|
||||
|
||||
return { rolesAddable, rolesRemovable }
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
BillingSettings,
|
||||
InvoicesSettings,
|
||||
} from 'components/interfaces/Organization'
|
||||
import { getRolesManagementPermissions } from 'components/interfaces/Organization/TeamSettings/TeamSettings.utils'
|
||||
|
||||
export const PageContext = createContext(null)
|
||||
|
||||
@@ -26,11 +27,14 @@ const OrgSettingsLayout = withAuth(
|
||||
// [Refactor] Eventually move away from useLocalObservable
|
||||
const PageState: any = useLocalObservable(() => ({
|
||||
user: {} as User,
|
||||
organization: {} as Organization[],
|
||||
organization: {} as Organization,
|
||||
projects: [] as Project[],
|
||||
members: [] as Member[],
|
||||
roles: [] as Role[],
|
||||
|
||||
rolesAddable: [] as Number[],
|
||||
rolesRemovable: [] as Number[],
|
||||
|
||||
membersFilterString: '',
|
||||
get isOrgOwner() {
|
||||
const userMember = this.members.find(
|
||||
@@ -54,11 +58,15 @@ const OrgSettingsLayout = withAuth(
|
||||
})
|
||||
return temp.sort((a: any, b: any) => a.username.localeCompare(b.username))
|
||||
},
|
||||
initData(organization: Organization[], user: User, projects: Project[], roles: Role[]) {
|
||||
initData(organization: Organization, user: User, projects: Project[], roles: Role[]) {
|
||||
this.user = user
|
||||
this.projects = projects
|
||||
this.organization = organization
|
||||
this.roles = roles
|
||||
|
||||
const { rolesAddable, rolesRemovable } = getRolesManagementPermissions(roles)
|
||||
this.rolesAddable = rolesAddable
|
||||
this.rolesRemovable = rolesRemovable
|
||||
},
|
||||
onOrgUpdated(updatedOrg: any) {
|
||||
app.onOrgUpdated(updatedOrg)
|
||||
|
||||
Reference in new issue
Block a user