From 2d92b9aa46390fca6c97560d3a7478edc78ac882 Mon Sep 17 00:00:00 2001 From: Denis Bykhov Date: Tue, 14 May 2024 14:29:05 +0500 Subject: [PATCH] Security improvments (#5595) Signed-off-by: Denis Bykhov --- models/contact/src/index.ts | 4 +- models/hr/src/index.ts | 4 + models/hr/src/plugin.ts | 3 +- models/notification/src/index.ts | 9 +-- models/view/src/index.ts | 52 +++++-------- models/view/src/utils.ts | 70 ++++++++++++++++++ packages/model/src/dsl.ts | 6 +- .../src/components/ObjectPopup.svelte | 12 +-- packages/presentation/src/utils.ts | 12 +-- .../src/components/AccountArrayEditor.svelte | 30 +++++++- .../components/EditOrganizationPanel.svelte | 9 +-- .../src/components/SpaceMembersEditor.svelte | 45 +++++++++++ .../src/components/UserBoxList.svelte | 70 +++++++++--------- .../src/components/UsersPopup.svelte | 12 ++- plugins/contact-resources/src/index.ts | 4 +- plugins/contact/src/index.ts | 3 +- .../components/DepartmentRefPresenter.svelte | 33 +++++++++ plugins/hr-resources/src/index.ts | 4 +- .../src/components/EditDoc.svelte | 3 +- .../src/components/ViewletSetting.svelte | 6 +- .../src/components/filter/ArrayFilter.svelte | 2 +- plugins/view-resources/src/utils.ts | 74 ++++++++++++------- plugins/view/src/index.ts | 4 +- plugins/view/src/types.ts | 17 +++++ 24 files changed, 344 insertions(+), 144 deletions(-) create mode 100644 models/view/src/utils.ts create mode 100644 plugins/contact-resources/src/components/SpaceMembersEditor.svelte create mode 100644 plugins/hr-resources/src/components/DepartmentRefPresenter.svelte diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index c0f4f1b760..3cb79241ee 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -66,7 +66,7 @@ import core, { TAccount, TAttachedDoc, TDoc } from '@hcengineering/model-core' import { createPublicLinkAction } from '@hcengineering/model-guest' import { generateClassNotificationTypes } from '@hcengineering/model-notification' import presentation from '@hcengineering/model-presentation' -import view, { createAction, type Viewlet } from '@hcengineering/model-view' +import view, { createAction, createAttributePresenter, type Viewlet } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' import notification from '@hcengineering/notification' import type { Asset, IntlString, Resource } from '@hcengineering/platform' @@ -1142,4 +1142,6 @@ export function createModel (builder: Builder): void { domain: DOMAIN_CONTACT, disabled: [{ attachedToClass: 1 }, { modifiedBy: 1 }, { createdBy: 1 }, { createdOn: -1 }, { attachedTo: 1 }] }) + + createAttributePresenter(builder, contact.component.SpaceMembersEditor, core.class.Space, 'members', 'array') } diff --git a/models/hr/src/index.ts b/models/hr/src/index.ts index 062943e8c4..914cf818d4 100644 --- a/models/hr/src/index.ts +++ b/models/hr/src/index.ts @@ -211,6 +211,10 @@ export function createModel (builder: Builder): void { inlineEditor: hr.component.DepartmentEditor }) + builder.mixin(hr.class.Department, core.class.Class, view.mixin.AttributePresenter, { + presenter: hr.component.DepartmentRefPresenter + }) + builder.mixin(hr.class.Department, core.class.Class, view.mixin.ObjectEditor, { editor: hr.component.EditDepartment }) diff --git a/models/hr/src/plugin.ts b/models/hr/src/plugin.ts index 44ad9f39c4..748299fbea 100644 --- a/models/hr/src/plugin.ts +++ b/models/hr/src/plugin.ts @@ -50,7 +50,8 @@ export default mergeIds(hrId, hr, { TzDatePresenter: '' as AnyComponent, TzDateEditor: '' as AnyComponent, RequestPresenter: '' as AnyComponent, - DepartmentPresenter: '' as AnyComponent + DepartmentPresenter: '' as AnyComponent, + DepartmentRefPresenter: '' as AnyComponent }, category: { HR: '' as Ref diff --git a/models/notification/src/index.ts b/models/notification/src/index.ts index 69ca6588f0..bf46c218ff 100644 --- a/models/notification/src/index.ts +++ b/models/notification/src/index.ts @@ -18,9 +18,7 @@ import activity, { type ActivityMessage } from '@hcengineering/activity' import chunter from '@hcengineering/chunter' import { DOMAIN_MODEL, - Hierarchy, IndexKind, - type Space, type Account, type AttachedDoc, type Class, @@ -31,6 +29,7 @@ import { type Domain, type Markup, type Ref, + type Space, type Timestamp, type Tx } from '@hcengineering/core' @@ -700,11 +699,7 @@ export function generateClassNotificationTypes ( ignoreKeys: string[] = [], defaultEnabled: string[] = [] ): void { - const txes = builder.getTxes() - const hierarchy = new Hierarchy() - for (const tx of txes) { - hierarchy.tx(tx) - } + const hierarchy = builder.hierarchy const attributes = hierarchy.getAllAttributes( _class, hierarchy.isDerived(_class, core.class.AttachedDoc) ? core.class.AttachedDoc : core.class.Doc diff --git a/models/view/src/index.ts b/models/view/src/index.ts index b690ba2538..c9efd027be 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -23,7 +23,8 @@ import { type DocumentQuery, type Domain, type Ref, - type Space + type Space, + type AnyAttribute } from '@hcengineering/core' import { type Builder, Mixin, Model, UX } from '@hcengineering/model' import core, { TClass, TDoc } from '@hcengineering/model-core' @@ -87,10 +88,13 @@ import { type ViewletPreference, type ObjectIdentifier, type ObjectIcon, - type ObjectTooltip + type ObjectTooltip, + type AttrPresenter, + type AttributeCategory } from '@hcengineering/view' import view from './plugin' +import { classPresenter, createAction } from './utils' export { viewId } from '@hcengineering/view' export { viewOperation } from './migration' @@ -98,38 +102,7 @@ export type { ViewAction, Viewlet } export const DOMAIN_VIEW = 'view' as Domain -export function createAction> ( - builder: Builder, - data: Data>, - id?: Ref> -): void { - const { label, ...adata } = data - builder.createDoc>(view.class.Action, core.space.Model, { label, ...adata }, id) -} - -export function classPresenter ( - builder: Builder, - _class: Ref>, - presenter: AnyComponent, - editor?: AnyComponent, - popup?: AnyComponent, - activity?: AnyComponent -): void { - builder.mixin(_class, core.class.Class, view.mixin.AttributePresenter, { - presenter - }) - if (editor !== undefined) { - builder.mixin(_class, core.class.Class, view.mixin.AttributeEditor, { - inlineEditor: editor, - popup - }) - } - if (activity !== undefined) { - builder.mixin(_class, core.class.Class, view.mixin.ActivityAttributePresenter, { - presenter: activity - }) - } -} +export * from './utils' @Model(view.class.FilteredView, core.class.Doc, DOMAIN_VIEW) @UX(view.string.FilteredViews) @@ -383,6 +356,14 @@ export class TObjectPanel extends TClass implements ObjectPanel { component!: AnyComponent } +@Model(view.class.AttrPresenter, core.class.Doc, DOMAIN_MODEL) +export class TAttrPresenter extends TDoc implements AttrPresenter { + category!: AttributeCategory + objectClass!: Ref>> + attribute!: Ref + component!: AnyComponent +} + export type ActionTemplate = Partial> /** @@ -467,7 +448,8 @@ export function createModel (builder: Builder): void { TGroupping, TObjectIdentifier, TObjectTooltip, - TObjectIcon + TObjectIcon, + TAttrPresenter ) classPresenter( diff --git a/models/view/src/utils.ts b/models/view/src/utils.ts new file mode 100644 index 0000000000..f0c2f599b7 --- /dev/null +++ b/models/view/src/utils.ts @@ -0,0 +1,70 @@ +// +// 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. +// + +import { type Class, type Data, type Doc, type Ref } from '@hcengineering/core' +import { type Builder } from '@hcengineering/model' +import core from '@hcengineering/model-core' +import { type AnyComponent } from '@hcengineering/ui' +import { type Action, type AttributeCategory } from '@hcengineering/view' +import view from '.' + +export function createAction> ( + builder: Builder, + data: Data>, + id?: Ref> +): void { + const { label, ...adata } = data + builder.createDoc>(view.class.Action, core.space.Model, { label, ...adata }, id) +} + +export function classPresenter ( + builder: Builder, + _class: Ref>, + presenter: AnyComponent, + editor?: AnyComponent, + popup?: AnyComponent, + activity?: AnyComponent +): void { + builder.mixin(_class, core.class.Class, view.mixin.AttributePresenter, { + presenter + }) + if (editor !== undefined) { + builder.mixin(_class, core.class.Class, view.mixin.AttributeEditor, { + inlineEditor: editor, + popup + }) + } + if (activity !== undefined) { + builder.mixin(_class, core.class.Class, view.mixin.ActivityAttributePresenter, { + presenter: activity + }) + } +} + +export function createAttributePresenter ( + builder: Builder, + component: AnyComponent, + _class: Ref>, + key: keyof T, + category: AttributeCategory +): void { + const attr = builder.hierarchy.getAttribute(_class, key as string) + builder.createDoc(view.class.AttrPresenter, core.space.Model, { + component, + attribute: attr._id, + objectClass: _class, + category + }) +} diff --git a/packages/model/src/dsl.ts b/packages/model/src/dsl.ts index 9cb302deb6..fee18a702c 100644 --- a/packages/model/src/dsl.ts +++ b/packages/model/src/dsl.ts @@ -27,6 +27,7 @@ import core, { Domain, Enum, EnumOf, + Hierarchy, Hyperlink, Mixin as IMixin, IndexKind, @@ -290,6 +291,7 @@ function _generateTx (tx: ClassTxes): Tx[] { */ export class Builder { private readonly txes: Tx[] = [] + readonly hierarchy = new Hierarchy() onTx?: (tx: Tx) => void @@ -322,7 +324,7 @@ export class Builder { for (const tx of generated) { this.txes.push(tx) this.onTx?.(tx) - // this.hierarchy.tx(tx) + this.hierarchy.tx(tx) } } @@ -352,6 +354,7 @@ export class Builder { } this.txes.push(tx) this.onTx?.(tx) + this.hierarchy.tx(tx) return TxProcessor.createDoc2Doc(tx) } @@ -364,6 +367,7 @@ export class Builder { const tx = txFactory.createTxMixin(objectId, objectClass, core.space.Model, mixin, attributes) this.txes.push(tx) this.onTx?.(tx) + this.hierarchy.tx(tx) } getTxes (): Tx[] { diff --git a/packages/presentation/src/components/ObjectPopup.svelte b/packages/presentation/src/components/ObjectPopup.svelte index 7adf75858c..33eb3d9157 100644 --- a/packages/presentation/src/components/ObjectPopup.svelte +++ b/packages/presentation/src/components/ObjectPopup.svelte @@ -61,6 +61,12 @@ return true } + export let sort: (a: T, b: T) => number = (a, b) => { + const aval: string = `${getObjectValue(groupBy, a as any)}` + const bval: string = `${getObjectValue(groupBy, b as any)}` + return aval.localeCompare(bval) + } + const created: Doc[] = [] const dispatch = createEventDispatcher() @@ -84,11 +90,7 @@ _id: { $nin: ignoreObjects, ..._idExtra } }, (result) => { - result.sort((a, b) => { - const aval: string = `${getObjectValue(groupBy, a as any)}` - const bval: string = `${getObjectValue(groupBy, b as any)}` - return aval.localeCompare(bval) - }) + result.sort(sort) if (created.length > 0) { const cmap = new Set(created.map((it) => it._id)) objects = [...created, ...result.filter((d) => !cmap.has(d._id))].filter(filter) diff --git a/packages/presentation/src/utils.ts b/packages/presentation/src/utils.ts index 36b3e045e4..412450f99f 100644 --- a/packages/presentation/src/utils.ts +++ b/packages/presentation/src/utils.ts @@ -48,7 +48,7 @@ import core, { import { getMetadata, getResource } from '@hcengineering/platform' import { LiveQuery as LQ } from '@hcengineering/query' import { type AnyComponent, type AnySvelteComponent, type IconSize } from '@hcengineering/ui' -import view, { type AttributeEditor } from '@hcengineering/view' +import view, { type AttributeCategory, type AttributeEditor } from '@hcengineering/view' import { deepEqual } from 'fast-equals' import { onDestroy } from 'svelte' import { type KeyedAttribute } from '..' @@ -407,16 +407,6 @@ export async function copyTextToClipboard (text: string): Promise { } } -/** - * @public - */ -export type AttributeCategory = 'object' | 'attribute' | 'inplace' | 'collection' | 'array' - -/** - * @public - */ -export const AttributeCategoryOrder = { attribute: 0, inplace: 1, collection: 2, array: 2, object: 3 } - /** * @public */ diff --git a/plugins/contact-resources/src/components/AccountArrayEditor.svelte b/plugins/contact-resources/src/components/AccountArrayEditor.svelte index a1bd61746e..f6071f2b49 100644 --- a/plugins/contact-resources/src/components/AccountArrayEditor.svelte +++ b/plugins/contact-resources/src/components/AccountArrayEditor.svelte @@ -13,8 +13,8 @@ // limitations under the License. --> + + +{#if !joined && onChange !== undefined} +