mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-10 11:47:42 +02:00
RBAC per class (#10314)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Vendored
+1
-1
@@ -97,7 +97,7 @@
|
||||
"MODEL_JSON": "${workspaceRoot}/models/all/bundle/model.json",
|
||||
// "SERVER_PROVIDER":"uweb"
|
||||
"SERVER_PROVIDER": "ws",
|
||||
"MODEL_VERSION": "0.7.312",
|
||||
"MODEL_VERSION": "0.7.320",
|
||||
// "version": "0.7.0",
|
||||
"COMMUNICATION_API_ENABLED": "true",
|
||||
"ELASTIC_INDEX_NAME": "local_storage_index",
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
//
|
||||
|
||||
import type { Asset, IntlString, Plugin } from '@hcengineering/platform'
|
||||
import { Tx } from '.'
|
||||
import type { DocumentQuery } from './storage'
|
||||
import { type WorkspaceDataId, type WorkspaceUuid } from './utils'
|
||||
import { Tx } from '.'
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -556,6 +556,10 @@ export interface AttributePermission extends Permission {
|
||||
attribute: Ref<AnyAttribute>
|
||||
}
|
||||
|
||||
export interface ClassPermission extends Permission {
|
||||
targetClass: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -15,18 +15,23 @@
|
||||
import type { Asset, IntlString, Metadata, Plugin, StatusCode } from '@hcengineering/platform'
|
||||
import { plugin } from '@hcengineering/platform'
|
||||
import type { BenchmarkDoc } from './benchmark'
|
||||
import { AccountRole, TxAccessLevel } from './classes'
|
||||
import type {
|
||||
Account,
|
||||
AccountUuid,
|
||||
AnyAttribute,
|
||||
ArrOf,
|
||||
Association,
|
||||
AttachedDoc,
|
||||
AttributePermission,
|
||||
Blob,
|
||||
Class,
|
||||
ClassCollaborators,
|
||||
ClassPermission,
|
||||
Collaborator,
|
||||
Collection,
|
||||
Configuration,
|
||||
ConfigurationElement,
|
||||
CustomSequence,
|
||||
Doc,
|
||||
DomainIndexConfiguration,
|
||||
Enum,
|
||||
@@ -49,7 +54,6 @@ import type {
|
||||
Relation,
|
||||
Role,
|
||||
Sequence,
|
||||
CustomSequence,
|
||||
Space,
|
||||
SpaceType,
|
||||
SpaceTypeDescriptor,
|
||||
@@ -60,12 +64,9 @@ import type {
|
||||
TypeAny,
|
||||
TypedSpace,
|
||||
UserStatus,
|
||||
Version,
|
||||
AccountUuid,
|
||||
ClassCollaborators,
|
||||
Collaborator,
|
||||
AttributePermission
|
||||
Version
|
||||
} from './classes'
|
||||
import { AccountRole, TxAccessLevel } from './classes'
|
||||
import { type Status, type StatusCategory } from './status'
|
||||
import type {
|
||||
Tx,
|
||||
@@ -131,6 +132,7 @@ export default plugin(coreId, {
|
||||
Role: '' as Ref<Class<Role>>,
|
||||
Permission: '' as Ref<Class<Permission>>,
|
||||
AttributePermission: '' as Ref<Class<AttributePermission>>,
|
||||
ClassPermission: '' as Ref<Class<ClassPermission>>,
|
||||
Type: '' as Ref<Class<Type<any>>>,
|
||||
TypeRelation: '' as Ref<Class<Type<string>>>,
|
||||
TypeString: '' as Ref<Class<Type<string>>>,
|
||||
|
||||
@@ -206,22 +206,28 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
const attachedDocAncestors = this.context.hierarchy.getAncestors(core.class.AttachedDoc)
|
||||
const ancestors = this.context.hierarchy.getAncestors(getTxObjectClass(tx))
|
||||
const targetAncestors = ancestors.filter((a) => !attachedDocAncestors.includes(a))
|
||||
const txClass = getTxClass(tx)
|
||||
|
||||
const permissions = this.context.modelDb.findAllSync(core.class.Permission, {
|
||||
objectClass: { $in: targetAncestors },
|
||||
txClass
|
||||
objectClass: { $in: targetAncestors }
|
||||
})
|
||||
const matched = permissions.filter((p) => {
|
||||
if (p.txMatch === undefined) return false
|
||||
const checkMatch = matchQuery([tx], p.txMatch, tx._class, this.context.hierarchy, true)
|
||||
return p.forbid !== true && checkMatch.length > 0
|
||||
})
|
||||
|
||||
if (matched.length > 0) return false
|
||||
|
||||
const withoutMatch = permissions.filter((p) => p.txMatch === undefined)
|
||||
return withoutMatch.length === 0
|
||||
for (const permission of permissions) {
|
||||
if (!isTxClassMatched(tx, permission)) continue
|
||||
if (
|
||||
permission.objectClass !== undefined &&
|
||||
!this.context.hierarchy.isDerived(getTxObjectClass(tx), permission.objectClass)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (permission.txMatch === undefined) {
|
||||
return false
|
||||
} else {
|
||||
const checkMatch = matchQuery([tx], permission.txMatch, tx._class, this.context.hierarchy, true)
|
||||
if (checkMatch.length === 0) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
@@ -463,17 +469,16 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
|
||||
}
|
||||
}
|
||||
|
||||
function getTxClass (tx: Tx): Ref<Class<Tx>> {
|
||||
let _class = tx._class
|
||||
if (tx._class === core.class.TxMixin && Object.keys((tx as TxMixin<Doc, Doc>).attributes).length > 0) {
|
||||
_class = core.class.TxUpdateDoc
|
||||
}
|
||||
return _class
|
||||
function isMixinUpdateTx (tx: Tx): boolean {
|
||||
return tx._class === core.class.TxMixin && Object.keys((tx as TxMixin<Doc, Doc>).attributes).length > 0
|
||||
}
|
||||
|
||||
function isTxClassMatched (tx: Tx, permission: Permission): boolean {
|
||||
const txClass = getTxClass(tx)
|
||||
return permission.txClass === txClass || permission.txClass === tx._class
|
||||
if (permission.txClass === tx._class) return true
|
||||
if (permission.txMatch === undefined && isMixinUpdateTx(tx)) {
|
||||
return permission.txClass === core.class.TxUpdateDoc
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function getTxObjectClass (tx: TxCUD<Doc>): Ref<Class<Doc>> {
|
||||
|
||||
@@ -23,7 +23,10 @@ export function definePermissions (builder: Builder): void {
|
||||
label: card.string.AddTagPermission,
|
||||
txClass: core.class.TxMixin,
|
||||
objectClass: card.class.Card,
|
||||
scope: 'space'
|
||||
scope: 'space',
|
||||
txMatch: {
|
||||
attributes: {}
|
||||
}
|
||||
},
|
||||
card.permission.AddTag
|
||||
)
|
||||
|
||||
+16
-14
@@ -33,9 +33,12 @@ import {
|
||||
TAttribute,
|
||||
TBlob,
|
||||
TClass,
|
||||
TClassCollaborators,
|
||||
TCollaborator,
|
||||
TCollection,
|
||||
TConfiguration,
|
||||
TConfigurationElement,
|
||||
TCustomSequence,
|
||||
TDoc,
|
||||
TDomainIndexConfiguration,
|
||||
TEnum,
|
||||
@@ -49,8 +52,10 @@ import {
|
||||
TPluginConfiguration,
|
||||
TRefTo,
|
||||
TRelation,
|
||||
TSequence,
|
||||
TTransientConfiguration,
|
||||
TType,
|
||||
TTypeAccountUuid,
|
||||
TTypeAny,
|
||||
TTypeBlob,
|
||||
TTypeBoolean,
|
||||
@@ -58,26 +63,22 @@ import {
|
||||
TTypeDate,
|
||||
TTypeFileSize,
|
||||
TTypeHyperlink,
|
||||
TTypeIdentifier,
|
||||
TTypeIntlString,
|
||||
TTypeMarkup,
|
||||
TTypePersonId,
|
||||
TTypeAccountUuid,
|
||||
TTypeNumber,
|
||||
TTypeIdentifier,
|
||||
TTypePersonId,
|
||||
TTypeRank,
|
||||
TTypeRecord,
|
||||
TTypeRelatedDocument,
|
||||
TTypeString,
|
||||
TTypeTimestamp,
|
||||
TVersion,
|
||||
TSequence,
|
||||
TCustomSequence,
|
||||
TClassCollaborators,
|
||||
TCollaborator
|
||||
TVersion
|
||||
} from './core'
|
||||
import { definePermissions } from './permissions'
|
||||
import {
|
||||
TAttributePermission,
|
||||
TClassPermission,
|
||||
TPermission,
|
||||
TRole,
|
||||
TSpace,
|
||||
@@ -95,15 +96,15 @@ export { coreId, DOMAIN_SPACE } from '@hcengineering/core'
|
||||
export * from './core'
|
||||
export {
|
||||
coreOperation,
|
||||
getSocialKeyByOldAccount,
|
||||
getAccountsFromTxes,
|
||||
getSocialKeyByOldEmail,
|
||||
getAccountUuidBySocialKey,
|
||||
getUniqueAccounts,
|
||||
getAccountUuidByOldAccount,
|
||||
getUniqueAccountsFromOldAccounts,
|
||||
getAccountUuidBySocialKey,
|
||||
getSocialIdBySocialKey,
|
||||
getSocialIdFromOldAccount
|
||||
getSocialIdFromOldAccount,
|
||||
getSocialKeyByOldAccount,
|
||||
getSocialKeyByOldEmail,
|
||||
getUniqueAccounts,
|
||||
getUniqueAccountsFromOldAccounts
|
||||
} from './migration'
|
||||
export * from './security'
|
||||
export * from './status'
|
||||
@@ -134,6 +135,7 @@ export function createModel (builder: Builder): void {
|
||||
TRole,
|
||||
TPermission,
|
||||
TAttributePermission,
|
||||
TClassPermission,
|
||||
TAttribute,
|
||||
TType,
|
||||
TEnumOf,
|
||||
|
||||
@@ -18,8 +18,13 @@ import {
|
||||
DOMAIN_SPACE,
|
||||
IndexKind,
|
||||
type AccountRole,
|
||||
type AccountUuid,
|
||||
type AnyAttribute,
|
||||
type AttributePermission,
|
||||
type Class,
|
||||
type ClassPermission,
|
||||
type CollectionSize,
|
||||
type Doc,
|
||||
type Permission,
|
||||
type Ref,
|
||||
type Role,
|
||||
@@ -27,13 +32,9 @@ import {
|
||||
type Space,
|
||||
type SpaceType,
|
||||
type SpaceTypeDescriptor,
|
||||
type TypedSpace,
|
||||
type AccountUuid,
|
||||
type TxAccessLevel,
|
||||
type Tx,
|
||||
type Doc,
|
||||
type AnyAttribute,
|
||||
type AttributePermission
|
||||
type TxAccessLevel,
|
||||
type TypedSpace
|
||||
} from '@hcengineering/core'
|
||||
import {
|
||||
ArrOf,
|
||||
@@ -43,15 +44,15 @@ import {
|
||||
Mixin,
|
||||
Model,
|
||||
Prop,
|
||||
TypeBoolean,
|
||||
TypeAccountUuid,
|
||||
TypeBoolean,
|
||||
TypeRef,
|
||||
TypeString,
|
||||
UX
|
||||
} from '@hcengineering/model'
|
||||
import { getEmbeddedLabel, type Asset, type IntlString } from '@hcengineering/platform'
|
||||
import core from './component'
|
||||
import { TAttachedDoc, TDoc, TClass } from './core'
|
||||
import { TAttachedDoc, TClass, TDoc } from './core'
|
||||
// S P A C E
|
||||
|
||||
@Model(core.class.Space, core.class.Doc, DOMAIN_SPACE)
|
||||
@@ -173,6 +174,12 @@ export class TAttributePermission extends TPermission implements AttributePermis
|
||||
attribute!: Ref<AnyAttribute>
|
||||
}
|
||||
|
||||
@Model(core.class.ClassPermission, core.class.Permission)
|
||||
@UX(core.string.Permission)
|
||||
export class TClassPermission extends TPermission implements ClassPermission {
|
||||
targetClass!: Ref<Class<Doc>>
|
||||
}
|
||||
|
||||
@Mixin(core.mixin.SpacesTypeData, core.class.Space)
|
||||
@UX(getEmbeddedLabel("All spaces' type")) // TODO: add icon?
|
||||
export class TSpacesTypeData extends TSpace implements RolesAssignment {
|
||||
|
||||
@@ -737,6 +737,10 @@ export function createModel (builder: Builder): void {
|
||||
presenter: setting.component.AttributePermissionPresenter
|
||||
})
|
||||
|
||||
builder.mixin(core.class.ClassPermission, core.class.Class, view.mixin.ObjectPresenter, {
|
||||
presenter: setting.component.ClassPermissionPresenter
|
||||
})
|
||||
|
||||
builder.createDoc(core.class.DomainIndexConfiguration, core.space.Model, {
|
||||
domain: DOMAIN_SETTING,
|
||||
disabled: [{ modifiedOn: 1 }, { modifiedBy: 1 }, { createdOn: 1 }, { space: 1 }]
|
||||
|
||||
@@ -73,4 +73,7 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 3C5 1.89543 5.89543 1 7 1H12C13.1046 1 14 1.89543 14 3V10C14 11.1046 13.1046 12 12 12H7C5.89543 12 5 11.1046 5 10V3ZM7 2C6.44772 2 6 2.44772 6 3V10C6 10.5523 6.44772 11 7 11H12C12.5523 11 13 10.5523 13 10V3C13 2.44772 12.5523 2 12 2H7Z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4 5C3.44772 5 3 5.44772 3 6V13C3 13.5523 3.44772 14 4 14H9C9.55228 14 10 13.5523 10 13V12H11V13C11 14.1046 10.1046 15 9 15H4C2.89543 15 2 14.1046 2 13V6C2 4.89543 2.89543 4 4 4H5V5H4Z" />
|
||||
</symbol>
|
||||
<symbol id="lock" viewBox="0 0 16 16">
|
||||
<path d="M4.69141 14.6338H11.3018C12.2178 14.6338 12.6689 14.1826 12.6689 13.1914V8.08496C12.6689 7.18945 12.293 6.72461 11.541 6.64941V4.96094C11.541 2.36328 9.81152 1.1123 7.99316 1.1123C6.18164 1.1123 4.45215 2.36328 4.45215 4.96094V6.67676C3.74805 6.78613 3.32422 7.2373 3.32422 8.08496V13.1914C3.32422 14.1826 3.77539 14.6338 4.69141 14.6338ZM5.75098 4.83105C5.75098 3.22461 6.76953 2.35645 7.99316 2.35645C9.2168 2.35645 10.2422 3.22461 10.2422 4.83105V6.64258L5.75098 6.64941V4.83105Z" />
|
||||
</symbol>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
@@ -29,5 +29,6 @@ loadMetadata(card.icon, {
|
||||
Expand: `${icons}#expand`,
|
||||
Feed: `${icons}#feed`,
|
||||
All: `${icons}#all`,
|
||||
Duplicate: `${icons}#duplicate`
|
||||
Duplicate: `${icons}#duplicate`,
|
||||
Lock: `${icons}#lock`
|
||||
})
|
||||
|
||||
@@ -62,11 +62,32 @@
|
||||
|
||||
const asMixin = hierarchy.as(space, core.mixin.SpacesTypeData)
|
||||
|
||||
return roles.reduce<RolesAssignment>((prev, { _id }) => {
|
||||
const res = roles.reduce<RolesAssignment>((prev, { _id }) => {
|
||||
prev[_id] = (asMixin as any)[_id] ?? []
|
||||
|
||||
return prev
|
||||
}, {})
|
||||
return res
|
||||
}
|
||||
|
||||
function getCurrentRolesAssignment (): RolesAssignment {
|
||||
if (space === undefined) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const asMixin = hierarchy.as(space, core.mixin.SpacesTypeData)
|
||||
const allRoles = client.getModel().findAllSync(card.class.Role, {})
|
||||
|
||||
const res: RolesAssignment = {}
|
||||
|
||||
for (const role of allRoles) {
|
||||
const curr = (asMixin as any)[role._id]
|
||||
if (curr !== undefined) {
|
||||
res[role._id] = curr
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
async function handleSave (): Promise<void> {
|
||||
@@ -100,7 +121,7 @@
|
||||
const data = getData()
|
||||
await client.diffUpdate(space, data)
|
||||
|
||||
if (rolesAssignment && !deepEqual(rolesAssignment, getRolesAssignment(roles))) {
|
||||
if (rolesAssignment && !deepEqual(rolesAssignment, getCurrentRolesAssignment())) {
|
||||
await client.updateMixin(space._id, space._class, core.space.Space, core.mixin.SpacesTypeData, rolesAssignment)
|
||||
}
|
||||
|
||||
@@ -112,7 +133,7 @@
|
||||
|
||||
const id = await client.createDoc(card.class.CardSpace, core.space.Space, data)
|
||||
|
||||
if (rolesAssignment && !deepEqual(rolesAssignment, getRolesAssignment(roles))) {
|
||||
if (rolesAssignment && !deepEqual(rolesAssignment, getCurrentRolesAssignment())) {
|
||||
await client.updateMixin(id, card.class.CardSpace, core.space.Space, core.mixin.SpacesTypeData, rolesAssignment)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,15 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import contact from '@hcengineering/contact'
|
||||
import core, { AnyAttribute, AttributePermission, Permission, Ref } from '@hcengineering/core'
|
||||
import core, {
|
||||
AnyAttribute,
|
||||
AttributePermission,
|
||||
Class,
|
||||
ClassPermission,
|
||||
Doc,
|
||||
Permission,
|
||||
Ref
|
||||
} from '@hcengineering/core'
|
||||
import { AttributeEditor, MessageBox, createQuery, getClient } from '@hcengineering/presentation'
|
||||
import {
|
||||
ButtonIcon,
|
||||
@@ -179,13 +187,20 @@
|
||||
return permission._class === core.class.AttributePermission
|
||||
}
|
||||
|
||||
function isClassPermission (permission: Permission): permission is ClassPermission {
|
||||
return permission._class === core.class.ClassPermission
|
||||
}
|
||||
|
||||
function getAttributePermissionLabel (permission: Permission): IntlString | undefined {
|
||||
const isAttribute = isAttributePermission(permission)
|
||||
if (!isAttribute) {
|
||||
return undefined
|
||||
if (isAttribute) {
|
||||
const attr = client.getModel().findObject(permission.attribute) as AnyAttribute
|
||||
return attr?.label
|
||||
}
|
||||
if (isClassPermission(permission)) {
|
||||
const _class = client.getModel().findObject(permission.targetClass) as Class<Doc>
|
||||
return _class?.label
|
||||
}
|
||||
const attr = client.getModel().findObject(permission.attribute) as AnyAttribute
|
||||
return attr?.label
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -15,9 +15,81 @@
|
||||
<script lang="ts">
|
||||
import { MasterTag } from '@hcengineering/card'
|
||||
import { ClassAttributes } from '@hcengineering/setting-resources'
|
||||
import setting from '@hcengineering/setting-resources/src/plugin'
|
||||
import { ButtonIcon, showPopup } from '@hcengineering/ui'
|
||||
import card from '../../plugin'
|
||||
import { getClient, MessageBox } from '@hcengineering/presentation'
|
||||
import core, { ClassPermission, Ref } from '@hcengineering/core'
|
||||
import view from '@hcengineering/view'
|
||||
|
||||
export let masterTag: MasterTag
|
||||
|
||||
const client = getClient()
|
||||
const hierarchy = client.getHierarchy()
|
||||
|
||||
let isRestricted: boolean =
|
||||
client.getModel().findObject(getPermissionRef(false)) !== undefined ||
|
||||
client.getModel().findObject(getPermissionRef(true)) !== undefined
|
||||
|
||||
function getPermissionRef (forbidden: boolean): Ref<ClassPermission> {
|
||||
return `${masterTag._id}_${forbidden ? 'forbidden' : 'allowed'}` as Ref<ClassPermission>
|
||||
}
|
||||
|
||||
function changeRestricted (): void {
|
||||
showPopup(
|
||||
MessageBox,
|
||||
{
|
||||
label: setting.string.Restricted,
|
||||
message: setting.string.RestrictedAttributeWarning,
|
||||
action: async () => {
|
||||
isRestricted = true
|
||||
const isMixin = hierarchy.isMixin(masterTag._id)
|
||||
const objectClass = hierarchy.getBaseClass(masterTag._id)
|
||||
const txClass = isMixin ? core.class.TxMixin : core.class.TxUpdateDoc
|
||||
await client.createDoc(
|
||||
core.class.ClassPermission,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass,
|
||||
txClass,
|
||||
txMatch: {
|
||||
[isMixin ? 'mixin' : 'objectClass']: masterTag._id
|
||||
},
|
||||
scope: 'space',
|
||||
forbid: false,
|
||||
label: view.string.AllowAttributeChanges,
|
||||
description: masterTag.label,
|
||||
targetClass: masterTag._id
|
||||
},
|
||||
getPermissionRef(false)
|
||||
)
|
||||
await client.createDoc(
|
||||
core.class.ClassPermission,
|
||||
core.space.Model,
|
||||
{
|
||||
objectClass,
|
||||
txClass,
|
||||
txMatch: {
|
||||
[isMixin ? 'mixin' : 'objectClass']: masterTag._id
|
||||
},
|
||||
scope: 'space',
|
||||
forbid: true,
|
||||
label: view.string.ForbidAttributeChanges,
|
||||
description: masterTag.label,
|
||||
targetClass: masterTag._id
|
||||
},
|
||||
getPermissionRef(true)
|
||||
)
|
||||
}
|
||||
},
|
||||
'top',
|
||||
(res) => {
|
||||
if (res !== undefined) {
|
||||
isRestricted = res
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ClassAttributes
|
||||
@@ -27,4 +99,17 @@
|
||||
showHeader={false}
|
||||
disabled={false}
|
||||
isCard
|
||||
/>
|
||||
>
|
||||
<div slot="header">
|
||||
<ButtonIcon
|
||||
kind={'secondary'}
|
||||
icon={card.icon.Lock}
|
||||
size={'small'}
|
||||
tooltip={{
|
||||
label: setting.string.Restricted
|
||||
}}
|
||||
disabled={isRestricted}
|
||||
on:click={changeRestricted}
|
||||
/>
|
||||
</div>
|
||||
</ClassAttributes>
|
||||
|
||||
@@ -170,7 +170,8 @@ const cardPlugin = plugin(cardId, {
|
||||
Expand: '' as Asset,
|
||||
Feed: '' as Asset,
|
||||
All: '' as Asset,
|
||||
Duplicate: '' as Asset
|
||||
Duplicate: '' as Asset,
|
||||
Lock: '' as Asset
|
||||
},
|
||||
extensions: {
|
||||
EditCardExtension: '' as ComponentExtensionId,
|
||||
|
||||
@@ -187,6 +187,8 @@
|
||||
<IconSettings size={'small'} />
|
||||
<span><Label label={settings.string.ClassProperties} /></span>
|
||||
{/if}
|
||||
{:else if $$slots.header}
|
||||
<slot name="header" />
|
||||
{:else}
|
||||
<div></div>
|
||||
{/if}
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
<!--
|
||||
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!--
|
||||
// Copyright © 2024 Hardcore Engineering Inc.
|
||||
//
|
||||
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License. You may
|
||||
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { Class, ClassPermission, Doc } from '@hcengineering/core'
|
||||
import { getClient } from '@hcengineering/presentation'
|
||||
import { Label } from '@hcengineering/ui'
|
||||
|
||||
export let value: ClassPermission
|
||||
export let inline: boolean = false
|
||||
|
||||
const client = getClient()
|
||||
$: _class = client.getModel().findObject(value?.targetClass) as Class<Doc>
|
||||
</script>
|
||||
|
||||
{#if value}
|
||||
<div class="flex-presenter" class:inline-presenter={inline}>
|
||||
<Label label={value.label} />
|
||||
{#if _class}
|
||||
<Label label={_class.label} />
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -38,6 +38,7 @@ import Configure from './components/Configure.svelte'
|
||||
import InviteSetting from './components/InviteSetting.svelte'
|
||||
import PermissionPresenter from './components/presenters/PermissionPresenter.svelte'
|
||||
import AttributePermissionPresenter from './components/presenters/AttributePermissionPresenter.svelte'
|
||||
import ClassPermissionPresenter from './components/presenters/ClassPermissionPresenter.svelte'
|
||||
import SpaceTypeDescriptorPresenter from './components/presenters/SpaceTypeDescriptorPresenter.svelte'
|
||||
import Spaces from './components/Spaces.svelte'
|
||||
import SpaceTypeGeneralSectionEditor from './components/spaceTypes/editor/SpaceTypeGeneralSectionEditor.svelte'
|
||||
@@ -144,6 +145,7 @@ export default async (): Promise<Resources> => ({
|
||||
ManageSpaceTypeContent,
|
||||
PermissionPresenter,
|
||||
AttributePermissionPresenter,
|
||||
ClassPermissionPresenter,
|
||||
SpaceTypeDescriptorPresenter,
|
||||
SpaceTypeGeneralSectionEditor,
|
||||
SpaceTypePropertiesSectionEditor,
|
||||
|
||||
@@ -200,6 +200,7 @@ export default plugin(settingId, {
|
||||
ClassSetting: '' as AnyComponent,
|
||||
PermissionPresenter: '' as AnyComponent,
|
||||
AttributePermissionPresenter: '' as AnyComponent,
|
||||
ClassPermissionPresenter: '' as AnyComponent,
|
||||
SpaceTypeDescriptorPresenter: '' as AnyComponent,
|
||||
SpaceTypeGeneralSectionEditor: '' as AnyComponent,
|
||||
SpaceTypePropertiesSectionEditor: '' as AnyComponent,
|
||||
|
||||
@@ -31,6 +31,18 @@ export function canChangeAttribute (
|
||||
return true
|
||||
}
|
||||
|
||||
const target = attr.attributeOf
|
||||
const forbiddenClId = `${target}_forbidden` as Ref<Permission>
|
||||
const forbiddenCl = store.ps[space]?.has(forbiddenClId)
|
||||
if (forbiddenCl) {
|
||||
return false
|
||||
}
|
||||
const allowedClId = `${target}_allowed` as Ref<Permission>
|
||||
const allowedCl = store.ps[space]?.has(allowedClId)
|
||||
if (allowedCl) {
|
||||
return true
|
||||
}
|
||||
|
||||
return canChangeDoc(_class ?? attr.attributeOf, space, store)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user