mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-27 03:54:57 +02:00
Table attribute editors should respect rbac (#10275)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { Card } from '@hcengineering/card'
|
||||
import { PermissionsStore } from '@hcengineering/contact'
|
||||
import { canChangeAttribute, permissionsStore } from '@hcengineering/contact-resources'
|
||||
import { permissionsStore } from '@hcengineering/contact-resources'
|
||||
import core, { AnyAttribute, Class, Doc, Ref, toRank, TypedSpace } from '@hcengineering/core'
|
||||
import {
|
||||
AttributeBarEditor,
|
||||
@@ -25,6 +25,7 @@
|
||||
isCollectionAttr,
|
||||
KeyedAttribute
|
||||
} from '@hcengineering/presentation'
|
||||
import { canChangeAttribute } from '@hcengineering/view-resources'
|
||||
|
||||
export let object: Card
|
||||
export let _class: Ref<Class<Doc>>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
getClient,
|
||||
ComponentExtensions
|
||||
} from '@hcengineering/presentation'
|
||||
import { showMenu } from '@hcengineering/view-resources'
|
||||
import { canChangeDoc, showMenu } from '@hcengineering/view-resources'
|
||||
import view from '@hcengineering/view'
|
||||
import { NotificationContext } from '@hcengineering/communication-types'
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
import ParentNamesPresenter from './ParentNamesPresenter.svelte'
|
||||
import { openCardInSidebar } from '../utils'
|
||||
import { afterUpdate } from 'svelte'
|
||||
import { canChangeDoc, permissionsStore } from '@hcengineering/contact-resources'
|
||||
import { permissionsStore } from '@hcengineering/contact-resources'
|
||||
|
||||
export let _id: Ref<Card>
|
||||
export let readonly: boolean = false
|
||||
|
||||
@@ -32,7 +32,13 @@
|
||||
navigate
|
||||
} from '@hcengineering/ui'
|
||||
import view from '@hcengineering/view'
|
||||
import { ParentsNavigator, RelationsEditor, getDocMixins, showMenu } from '@hcengineering/view-resources'
|
||||
import {
|
||||
ParentsNavigator,
|
||||
RelationsEditor,
|
||||
canChangeDoc,
|
||||
getDocMixins,
|
||||
showMenu
|
||||
} from '@hcengineering/view-resources'
|
||||
import { createEventDispatcher, onDestroy } from 'svelte'
|
||||
|
||||
import card from '../plugin'
|
||||
@@ -42,7 +48,7 @@
|
||||
import Childs from './Childs.svelte'
|
||||
import Content from './Content.svelte'
|
||||
import TagsEditor from './TagsEditor.svelte'
|
||||
import { canChangeDoc, permissionsStore } from '@hcengineering/contact-resources'
|
||||
import { permissionsStore } from '@hcengineering/contact-resources'
|
||||
|
||||
export let _id: Ref<Card>
|
||||
export let readonly: boolean = false
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
|
||||
import Content from '../Content.svelte'
|
||||
import { CardSectionAction } from '../../types'
|
||||
import { canChangeDoc, permissionsStore } from '@hcengineering/contact-resources'
|
||||
import { permissionsStore } from '@hcengineering/contact-resources'
|
||||
import { canChangeDoc } from '@hcengineering/view-resources'
|
||||
|
||||
export let readonly: boolean = false
|
||||
export let doc: Card
|
||||
|
||||
@@ -53,7 +53,6 @@ import {
|
||||
import core, {
|
||||
type AccountUuid,
|
||||
type AggregateValue,
|
||||
type AnyAttribute,
|
||||
type Class,
|
||||
type Client,
|
||||
type Doc,
|
||||
@@ -671,58 +670,6 @@ export function checkMyPermission (_id: Ref<Permission>, space: Ref<TypedSpace>,
|
||||
return (store.whitelist.has(space) || store.ps[space]?.has(_id)) ?? false
|
||||
}
|
||||
|
||||
export function canChangeAttribute (
|
||||
attr: AnyAttribute,
|
||||
space: Ref<TypedSpace>,
|
||||
store: PermissionsStore,
|
||||
_class?: Ref<Class<Doc>>
|
||||
): boolean {
|
||||
const arePermissionsDisabled = getMetadata(core.metadata.DisablePermissions) ?? false
|
||||
if (arePermissionsDisabled) return true
|
||||
if (store.whitelist.has(space)) return true
|
||||
const forbiddenId = `${attr._id}_forbidden` as Ref<Permission>
|
||||
const forbidden = store.ps[space]?.has(forbiddenId)
|
||||
if (forbidden) {
|
||||
return false
|
||||
}
|
||||
const allowedId = `${attr._id}_allowed` as Ref<Permission>
|
||||
const allowed = store.ps[space]?.has(allowedId)
|
||||
if (allowed) {
|
||||
return true
|
||||
}
|
||||
|
||||
return canChangeDoc(_class ?? attr.attributeOf, space, store)
|
||||
}
|
||||
|
||||
export function canChangeDoc (_class: Ref<Class<Doc>>, space: Ref<Space>, store: PermissionsStore): boolean {
|
||||
const arePermissionsDisabled = getMetadata(core.metadata.DisablePermissions) ?? false
|
||||
if (arePermissionsDisabled) return true
|
||||
if (store.whitelist.has(space)) return true
|
||||
if (store.ps[space] !== undefined) {
|
||||
const client = getClient()
|
||||
const h = client.getHierarchy()
|
||||
const ancestors = h.getAncestors(_class)
|
||||
const permissions = client
|
||||
.getModel()
|
||||
.findAllSync(core.class.Permission, { txClass: { $in: [core.class.TxUpdateDoc, core.class.TxMixin] } })
|
||||
for (const ancestor of ancestors) {
|
||||
const curr = permissions.filter(
|
||||
(p) =>
|
||||
p.objectClass === ancestor &&
|
||||
p.txMatch === undefined &&
|
||||
p.txClass === (h.isMixin(ancestor) ? core.class.TxMixin : core.class.TxUpdateDoc)
|
||||
)
|
||||
for (const permission of curr) {
|
||||
if (store.ps[space]?.has(permission._id)) {
|
||||
return permission.forbid !== true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !store.restrictedSpaces.has(space)
|
||||
}
|
||||
|
||||
export function getPermittedPersons (
|
||||
_id: Ref<Permission>,
|
||||
space: Ref<TypedSpace>,
|
||||
|
||||
@@ -13,15 +13,19 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import core, { AnyAttribute, Class, Doc, Ref, TxOperations, getObjectValue } from '@hcengineering/core'
|
||||
import contact, { PermissionsStore } from '@hcengineering/contact'
|
||||
import core, { AnyAttribute, Class, Doc, Ref, TxOperations, TypedSpace, getObjectValue } from '@hcengineering/core'
|
||||
import { getClient, reduceCalls, updateAttribute } from '@hcengineering/presentation'
|
||||
import { Label, Loading, mouseAttractor, resizeObserver } from '@hcengineering/ui'
|
||||
import { Label, Loading, mouseAttractor } from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey, BuildModelOptions, Viewlet } from '@hcengineering/view'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { showMenu } from '../actions'
|
||||
import { buildModel, restrictionStore } from '../utils'
|
||||
import view from '../plugin'
|
||||
import { buildModel, restrictionStore } from '../utils'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { Readable } from 'svelte/store'
|
||||
import { canChangeAttribute } from '../permissions'
|
||||
|
||||
export let objects: Doc[]
|
||||
export let config: Array<string | BuildModelKey>
|
||||
@@ -150,6 +154,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let permissionsStore: Readable<PermissionsStore> | undefined = undefined
|
||||
|
||||
onMount(async () => {
|
||||
permissionsStore = await getResource(contact.store.Permissions)
|
||||
})
|
||||
|
||||
function canChangeAttr (
|
||||
object: Doc,
|
||||
attr: AnyAttribute | undefined,
|
||||
permissionsStore: PermissionsStore | undefined
|
||||
): boolean {
|
||||
if (attr === undefined) return true
|
||||
if (permissionsStore === undefined) return true
|
||||
return canChangeAttribute(attr, object.space as Ref<TypedSpace>, permissionsStore)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !model || isBuildingModel}
|
||||
@@ -206,7 +226,13 @@
|
||||
onChange={getOnChange(object, attribute)}
|
||||
label={attribute.label}
|
||||
attribute={attribute.attribute}
|
||||
{...joinProps(attribute, object, readonly || $restrictionStore.readonly)}
|
||||
{...joinProps(
|
||||
attribute,
|
||||
object,
|
||||
readonly ||
|
||||
$restrictionStore.readonly ||
|
||||
!canChangeAttr(object, attribute.attribute, $permissionsStore)
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
Ref,
|
||||
SortingOrder,
|
||||
TxOperations,
|
||||
TypedSpace,
|
||||
getObjectValue,
|
||||
mergeQueries
|
||||
} from '@hcengineering/core'
|
||||
@@ -43,13 +44,17 @@
|
||||
} from '@hcengineering/ui'
|
||||
import { AttributeModel, BuildModelKey, BuildModelOptions, ViewOptionModel, ViewOptions } from '@hcengineering/view'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { showMenu } from '../actions'
|
||||
import view from '../plugin'
|
||||
import { LoadingProps, buildConfigAssociation, buildConfigLookup, buildModel, restrictionStore } from '../utils'
|
||||
import IconUpDown from './icons/UpDown.svelte'
|
||||
import { getResultOptions, getResultQuery } from '../viewOptions'
|
||||
import { canEditSpace } from '../visibilityTester'
|
||||
import contact, { PermissionsStore } from '@hcengineering/contact'
|
||||
import { Readable } from 'svelte/store'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { canChangeAttribute } from '../permissions'
|
||||
|
||||
export let _class: Ref<Class<Doc>>
|
||||
export let query: DocumentQuery<Doc>
|
||||
@@ -338,6 +343,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
let permissionsStore: Readable<PermissionsStore> | undefined = undefined
|
||||
|
||||
onMount(async () => {
|
||||
permissionsStore = await getResource(contact.store.Permissions)
|
||||
})
|
||||
|
||||
function canChangeAttr (
|
||||
object: Doc,
|
||||
attr: AnyAttribute | undefined,
|
||||
permissionsStore: PermissionsStore | undefined
|
||||
): boolean {
|
||||
if (permissionsStore === undefined) return true
|
||||
if (attr === undefined) return true
|
||||
return canChangeAttribute(attr, object.space as Ref<TypedSpace>, permissionsStore)
|
||||
}
|
||||
|
||||
async function canEdit (object: Doc): Promise<boolean> {
|
||||
if (client.getHierarchy().isDerived(object._class, core.class.Space)) {
|
||||
return await canEditSpace(object)
|
||||
@@ -474,7 +495,14 @@
|
||||
onChange={getOnChange(object, attribute)}
|
||||
label={attribute.label}
|
||||
attribute={attribute.attribute}
|
||||
{...joinProps(attribute, object, readonly || $restrictionStore.readonly, canEditObject)}
|
||||
{...joinProps(
|
||||
attribute,
|
||||
object,
|
||||
readonly ||
|
||||
$restrictionStore.readonly ||
|
||||
!canChangeAttr(object, attribute.attribute, $permissionsStore),
|
||||
canEditObject
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
@@ -484,7 +512,14 @@
|
||||
onChange={getOnChange(object, attribute)}
|
||||
label={attribute.label}
|
||||
attribute={attribute.attribute}
|
||||
{...joinProps(attribute, object, readonly || $restrictionStore.readonly, canEditObject)}
|
||||
{...joinProps(
|
||||
attribute,
|
||||
object,
|
||||
readonly ||
|
||||
$restrictionStore.readonly ||
|
||||
!canChangeAttr(object, attribute.attribute, $permissionsStore),
|
||||
canEditObject
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
</td>
|
||||
|
||||
@@ -13,10 +13,13 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import core, { Doc } from '@hcengineering/core'
|
||||
import contact, { PermissionsStore } from '@hcengineering/contact'
|
||||
import core, { AnyAttribute, Doc, Ref, TypedSpace } from '@hcengineering/core'
|
||||
import { getResource } from '@hcengineering/platform'
|
||||
import { AttributeModel } from '@hcengineering/view'
|
||||
import { FixedColumn, restrictionStore } from '../..'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { Readable } from 'svelte/store'
|
||||
import { canChangeAttribute, FixedColumn, restrictionStore } from '../..'
|
||||
import DividerPresenter from './DividerPresenter.svelte'
|
||||
|
||||
export let docObject: Doc
|
||||
@@ -51,6 +54,22 @@
|
||||
if (e.detail === undefined) return
|
||||
dispatch('resize', e.detail)
|
||||
}
|
||||
|
||||
let permissionsStore: Readable<PermissionsStore> | undefined = undefined
|
||||
|
||||
onMount(async () => {
|
||||
permissionsStore = await getResource(contact.store.Permissions)
|
||||
})
|
||||
|
||||
function canChangeAttr (
|
||||
object: Doc,
|
||||
attr: AnyAttribute | undefined,
|
||||
permissionsStore: PermissionsStore | undefined
|
||||
): boolean {
|
||||
if (attr === undefined) return true
|
||||
if (permissionsStore === undefined) return true
|
||||
return canChangeAttribute(attr, object.space as Ref<TypedSpace>, permissionsStore)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if dp?.dividerBefore === true && !hideDivider}
|
||||
@@ -66,7 +85,12 @@
|
||||
{compactMode}
|
||||
label={attributeModel.label}
|
||||
attribute={attributeModel.attribute}
|
||||
{...joinProps(attributeModel, docObject, props, $restrictionStore.readonly)}
|
||||
{...joinProps(
|
||||
attributeModel,
|
||||
docObject,
|
||||
props,
|
||||
$restrictionStore.readonly || !canChangeAttr(docObject, attributeModel.attribute, $permissionsStore)
|
||||
)}
|
||||
on:resize={translateSize}
|
||||
/>
|
||||
</FixedColumn>
|
||||
@@ -79,7 +103,12 @@
|
||||
label={attributeModel.label}
|
||||
{compactMode}
|
||||
attribute={attributeModel.attribute}
|
||||
{...joinProps(attributeModel, docObject, props, $restrictionStore.readonly)}
|
||||
{...joinProps(
|
||||
attributeModel,
|
||||
docObject,
|
||||
props,
|
||||
$restrictionStore.readonly || !canChangeAttr(docObject, attributeModel.attribute, $permissionsStore)
|
||||
)}
|
||||
on:resize={translateSize}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -199,6 +199,7 @@ export * from './objectIterator'
|
||||
export * from './selection'
|
||||
export * from './status'
|
||||
export * from './utils'
|
||||
export * from './permissions'
|
||||
export {
|
||||
buildModel,
|
||||
getActiveViewletId,
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { type PermissionsStore } from '@hcengineering/contact'
|
||||
import core, {
|
||||
type AnyAttribute,
|
||||
type Class,
|
||||
type Doc,
|
||||
type Permission,
|
||||
type Ref,
|
||||
type Space,
|
||||
type TypedSpace
|
||||
} from '@hcengineering/core'
|
||||
import { getMetadata } from '@hcengineering/platform'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
|
||||
export function canChangeAttribute (
|
||||
attr: AnyAttribute,
|
||||
space: Ref<TypedSpace>,
|
||||
store: PermissionsStore,
|
||||
_class?: Ref<Class<Doc>>
|
||||
): boolean {
|
||||
const arePermissionsDisabled = getMetadata(core.metadata.DisablePermissions) ?? false
|
||||
if (arePermissionsDisabled) return true
|
||||
if (store.whitelist.has(space)) return true
|
||||
const forbiddenId = `${attr._id}_forbidden` as Ref<Permission>
|
||||
const forbidden = store.ps[space]?.has(forbiddenId)
|
||||
if (forbidden) {
|
||||
return false
|
||||
}
|
||||
const allowedId = `${attr._id}_allowed` as Ref<Permission>
|
||||
const allowed = store.ps[space]?.has(allowedId)
|
||||
if (allowed) {
|
||||
return true
|
||||
}
|
||||
|
||||
return canChangeDoc(_class ?? attr.attributeOf, space, store)
|
||||
}
|
||||
|
||||
export function canChangeDoc (_class: Ref<Class<Doc>>, space: Ref<Space>, store: PermissionsStore): boolean {
|
||||
const arePermissionsDisabled = getMetadata(core.metadata.DisablePermissions) ?? false
|
||||
if (arePermissionsDisabled) return true
|
||||
if (store.whitelist.has(space)) return true
|
||||
if (store.ps[space] !== undefined) {
|
||||
const client = getClient()
|
||||
const h = client.getHierarchy()
|
||||
const ancestors = h.getAncestors(_class)
|
||||
const permissions = client
|
||||
.getModel()
|
||||
.findAllSync(core.class.Permission, { txClass: { $in: [core.class.TxUpdateDoc, core.class.TxMixin] } })
|
||||
for (const ancestor of ancestors) {
|
||||
const curr = permissions.filter(
|
||||
(p) =>
|
||||
p.objectClass === ancestor &&
|
||||
p.txMatch === undefined &&
|
||||
p.txClass === (h.isMixin(ancestor) ? core.class.TxMixin : core.class.TxUpdateDoc)
|
||||
)
|
||||
for (const permission of curr) {
|
||||
if (store.ps[space]?.has(permission._id)) {
|
||||
return permission.forbid !== true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !store.restrictedSpaces.has(space)
|
||||
}
|
||||
Reference in New Issue
Block a user