Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
"MODEL_JSON": "${workspaceRoot}/models/all/bundle/model.json",
// "SERVER_PROVIDER":"uweb"
"SERVER_PROVIDER": "ws",
"MODEL_VERSION": "0.7.301",
"MODEL_VERSION": "0.7.312",
// "version": "0.7.0",
"COMMUNICATION_API_ENABLED": "true",
"ELASTIC_INDEX_NAME": "local_storage_index",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "Umožňuje uživatelům archivovat prostor",
"AutoJoin": "Automatické připojení",
"AutoJoinDescr": "Automaticky připojit nové zaměstnance k tomuto prostoru",
"RBAC": "Řízení přístupu na základě rolí",
"RBACDescr": "Vyžadovat řízení přístupu na základě rolí pro provádění akcí v tomto prostoru",
"BlobSize": "Velikost",
"BlobContentType": "Typ obsahu",
"Relation": "Vztah",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "Gewährt Benutzern die Möglichkeit, den Arbeitsbereich zu archivieren",
"AutoJoin": "Automatisch beitreten",
"AutoJoinDescr": "Neue Mitarbeiter automatisch diesem Arbeitsbereich hinzufügen",
"RBAC": "Rollenbasierte Zugriffskontrolle",
"RBACDescr": "Erfordert rollenbasierten Zugriff, um Aktionen in diesem Arbeitsbereich auszuführen",
"BlobSize": "Größe",
"BlobContentType": "Inhaltstyp",
"Relation": "Beziehung",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "Grants users ability to archive the space",
"AutoJoin": "Auto join",
"AutoJoinDescr": "Automatically join new employees to this space",
"RBAC": "Role-based access control",
"RBACDescr": "Require role-based access to perform actions in this space",
"BlobSize": "Size",
"BlobContentType": "Content type",
"Relation": "Relation",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
"ArchiveSpaceDescription": "Concede a los usuarios la capacidad de archivar el espacio",
"AutoJoin": "Auto unirse",
"AutoJoinDescr": "Unirse automáticamente a los nuevos empleados a este espacio",
"RBAC": "Control de acceso basado en roles",
"RBACDescr": "Requerir acceso basado en roles para realizar acciones en este espacio",
"BlobSize": "Tamaño",
"BlobContentType": "Tipo de contenido",
"Relation": "Relación",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "Accorde aux utilisateurs la capacité d'archiver l'espace",
"AutoJoin": "Rejoindre automatiquement",
"AutoJoinDescr": "Ajouter automatiquement les nouveaux employés à cet espace",
"RBAC": "Contrôle d'accès basé sur les rôles",
"RBACDescr": "Exiger un accès basé sur les rôles pour effectuer des actions dans cet espace",
"BlobSize": "Taille",
"BlobContentType": "Type de contenu",
"Relation": "Relation",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "Concede agli utenti la possibilità di archiviare lo spazio",
"AutoJoin": "Partecipazione automatica",
"AutoJoinDescr": "Aggiungi automaticamente i nuovi dipendenti a questo spazio",
"RBAC": "Controllo accessi basato sui ruoli",
"RBACDescr": "Richiedi accesso basato sui ruoli per eseguire azioni in questo spazio",
"BlobSize": "Dimensione",
"BlobContentType": "Tipo di contenuto",
"Relation": "Relazione",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "ユーザーにスペースをアーカイブする権限を付与します",
"AutoJoin": "自動参加",
"AutoJoinDescr": "新しいユーザーを自動的にこのスペースに参加させます",
"RBAC": "ロールベースアクセス制御",
"RBACDescr": "このスペースでの操作を実行するためにロールベースのアクセスを要求します",
"BlobSize": "サイズ",
"BlobContentType": "コンテンツタイプ",
"Relation": "関係",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
"ArchiveSpaceDescription": "Concede aos usuários a capacidade de arquivar o espaço",
"AutoJoin": "Auto adesão",
"AutoJoinDescr": "Adesão automática de novos funcionários a este espaço",
"RBAC": "Controle de acesso baseado em funções",
"RBACDescr": "Exigir acesso baseado em funções para executar ações neste espaço",
"BlobSize": "Tamanho",
"BlobContentType": "Tipo de conteúdo",
"Relation": "Relação",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "Дает пользователям разрешение архивировать пространство",
"AutoJoin": "Автоприсоединение",
"AutoJoinDescr": "Автоматически присоединять новых сотрудников к этому пространству",
"RBAC": "Ролевой доступ",
"RBACDescr": "Требовать ролевой доступ для выполнения действий в этом пространстве",
"BlobSize": "Размер",
"BlobContentType": "Тип контента",
"Relation": "Связь",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "Kullanıcılara alanı arşivleme yetkisi verir",
"AutoJoin": "Otomatik katıl",
"AutoJoinDescr": "Yeni çalışanları bu alana otomatik olarak ekle",
"RBAC": "Rol tabanlı erişim kontrolü",
"RBACDescr": "Bu alanda işlemleri gerçekleştirmek için rol tabanlı erişim gerektir",
"BlobSize": "Boyut",
"BlobContentType": "İçerik türü",
"Relation": "İlişki",
Expand Down
2 changes: 2 additions & 0 deletions foundations/core/packages/core/lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
"ArchiveSpaceDescription": "授予用户归档空间的权限",
"AutoJoin": "自动加入",
"AutoJoinDescr": "自动将新员工加入此空间",
"RBAC": "基于角色的访问控制",
"RBACDescr": "要求基于角色的访问权限以在此空间中执行操作",
"BlobSize": "大小",
"BlobContentType": "內容類型",
"Relation": "关系",
Expand Down
1 change: 1 addition & 0 deletions foundations/core/packages/core/src/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,7 @@ export interface SystemSpace extends Space {}
*/
export interface TypedSpace extends Space {
type: Ref<SpaceType>
restricted?: boolean // if true user must have permission to any txes
}

/**
Expand Down
4 changes: 3 additions & 1 deletion foundations/core/packages/core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,9 @@ export default plugin(coreId, {
UpdateSpaceDescription: '' as IntlString,
ArchiveSpaceDescription: '' as IntlString,
AutoJoin: '' as IntlString,
AutoJoinDescr: '' as IntlString
AutoJoinDescr: '' as IntlString,
RBAC: '' as IntlString,
RBACDescr: '' as IntlString
},
descriptor: {
SpacesType: '' as Ref<SpaceTypeDescriptor>
Expand Down
50 changes: 41 additions & 9 deletions foundations/server/packages/middleware/src/spacePermissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { BaseMiddleware } from '@hcengineering/server-core'
*/
export class SpacePermissionsMiddleware extends BaseMiddleware implements Middleware {
private whitelistSpaces = new Set<Ref<Space>>()
private readonly restrictedSpaces = new Set<Ref<Space>>()
private assignmentBySpace: Record<Ref<Space>, RolesAssignment> = {}
private permissionsBySpace: Record<Ref<Space>, Record<AccountUuid, Set<Permission>>> = {}
private typeBySpace: Record<Ref<Space>, Ref<SpaceType>> = {}
Expand Down Expand Up @@ -121,6 +122,12 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
return
}

if (space.restricted === true) {
this.restrictedSpaces.add(space._id)
} else {
this.restrictedSpaces.delete(space._id)
}

this.typeBySpace[space._id] = space.type

const asMixin: RolesAssignment = this.context.hierarchy.as(
Expand Down Expand Up @@ -157,7 +164,12 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
*
* Checks if the required permission is present in the space for the given context
*/
private checkPermission (ctx: MeasureContext<SessionData>, space: Ref<TypedSpace>, tx: TxCUD<Doc>): boolean {
private checkPermission (
ctx: MeasureContext<SessionData>,
space: Ref<TypedSpace>,
tx: TxCUD<Doc>,
isSpace: boolean
): boolean {
const account = ctx.contextData.account
const permissions = this.permissionsBySpace[space]?.[account.uuid] ?? []
let withoutMatch: Permission | undefined
Expand Down Expand Up @@ -185,7 +197,7 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
return withoutMatch.forbid !== undefined ? !withoutMatch.forbid : true
}

return true
return isSpace || !this.restrictedSpaces.has(space)
}

private throwForbidden (): void {
Expand Down Expand Up @@ -273,6 +285,7 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
delete this.typeBySpace[tx.objectId]

this.whitelistSpaces.delete(tx.objectId)
this.restrictedSpaces.delete(tx.objectId)
}

private isSpaceTxCUD (tx: TxCUD<Doc>): tx is TxCUD<Space> {
Expand Down Expand Up @@ -337,9 +350,8 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
if (this.isSpaceTxCUD(tx)) {
if (tx._class === core.class.TxCreateDoc) {
this.handleCreate(tx)
// } else if (tx._class === core.class.TxUpdateDoc) {
// Roles assignment in spaces are managed through the space type mixin
// so nothing to handle here
} else if (tx._class === core.class.TxUpdateDoc) {
this.handleSpaceUpdate(tx)
} else if (tx._class === core.class.TxMixin) {
this.handleMixin(tx)
} else if (tx._class === core.class.TxRemoveDoc) {
Expand All @@ -350,6 +362,21 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
this.handlePermissionsUpdatesFromRoleTx(ctx, tx)
}

private handleSpaceUpdate (tx: TxCUD<Space>): void {
if (!this.isTypedSpaceClass(tx.objectClass)) {
return
}

const updateTx = tx as TxUpdateDoc<TypedSpace>
if (updateTx.operations.restricted !== undefined) {
if (updateTx.operations.restricted) {
this.restrictedSpaces.add(tx.objectId)
} else {
this.restrictedSpaces.delete(tx.objectId)
}
}
}

private processPermissionsUpdatesFromTx (ctx: MeasureContext, tx: Tx): void {
if (!TxProcessor.isExtendsCUD(tx._class)) {
return
Expand All @@ -362,8 +389,8 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
async tx (ctx: MeasureContext<SessionData>, txes: Tx[]): Promise<TxMiddlewareResult> {
await this.init(ctx)
for (const tx of txes) {
this.processPermissionsUpdatesFromTx(ctx, tx)
this.checkPermissions(ctx, tx)
this.processPermissionsUpdatesFromTx(ctx, tx)
}
const res = await this.provideTx(ctx, txes)
for (const txd of ctx.contextData.broadcast.txes) {
Expand All @@ -388,17 +415,22 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle

this.checkSpacePermissions(ctx, cudTx, cudTx.objectSpace)
if (isSpace) {
this.checkSpacePermissions(ctx, cudTx, cudTx.objectId as Ref<Space>)
this.checkSpacePermissions(ctx, cudTx, cudTx.objectId as Ref<Space>, true)
}
}

private checkSpacePermissions (ctx: MeasureContext, cudTx: TxCUD<Doc>, targetSpaceId: Ref<Space>): void {
private checkSpacePermissions (
ctx: MeasureContext,
cudTx: TxCUD<Doc>,
targetSpaceId: Ref<Space>,
isSpace: boolean = false
): void {
if (this.whitelistSpaces.has(targetSpaceId)) {
return
}
// NOTE: move this checking logic later to be defined in some server plugins?
// so they can contribute checks into the middleware for their custom permissions?
if (!this.checkPermission(ctx, targetSpaceId as Ref<TypedSpace>, cudTx)) {
if (!this.checkPermission(ctx, targetSpaceId as Ref<TypedSpace>, cudTx, isSpace)) {
this.throwForbidden()
}
}
Expand Down
22 changes: 16 additions & 6 deletions plugins/card-resources/src/components/navigator/CreateSpace.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@
let types: Ref<MasterTag>[] =
space?.types !== undefined ? hierarchy.clone(space.types) : topLevelTypes.map((it) => it._id)

let roles = client.getModel().findAllSync(card.class.Role, { type: { $in: types } })
$: roles = client.getModel().findAllSync(card.class.Role, { type: { $in: types } })
let roles = client.getModel().findAllSync(card.class.Role, { types: { $in: types } })
$: roles = client.getModel().findAllSync(card.class.Role, { types: { $in: types } })

let name: string = space?.name ?? ''

let isPrivate: boolean = space?.private ?? false
let restricted: boolean = space?.restricted ?? false
let members: AccountUuid[] =
space?.members !== undefined ? hierarchy.clone(space.members) : [getCurrentAccount().uuid]
let owners: AccountUuid[] = space?.owners !== undefined ? hierarchy.clone(space.owners) : [getCurrentAccount().uuid]
Expand Down Expand Up @@ -86,7 +87,8 @@
autoJoin,
archived: false,
type: card.spaceType.SpaceType,
types
types,
restricted
}
}

Expand All @@ -106,9 +108,9 @@
}

async function create (): Promise<void> {
const teamspaceData = getData()
const data = getData()

const id = await client.createDoc(card.class.CardSpace, core.space.Space, { ...teamspaceData })
const id = await client.createDoc(card.class.CardSpace, core.space.Space, data)

if (rolesAssignment && !deepEqual(rolesAssignment, getRolesAssignment(roles))) {
await client.updateMixin(id, card.class.CardSpace, core.space.Space, core.mixin.SpacesTypeData, rolesAssignment)
Expand Down Expand Up @@ -221,7 +223,15 @@
<Label label={core.string.AutoJoin} />
<span><Label label={core.string.AutoJoinDescr} /></span>
</div>
<Toggle id={'teamspace-autoJoin'} bind:on={autoJoin} />
<Toggle id={'space-autoJoin'} bind:on={autoJoin} />
</div>

<div class="antiGrid-row">
<div class="antiGrid-row__header withDesciption">
<Label label={core.string.RBAC} />
<span><Label label={core.string.RBACDescr} /></span>
</div>
<Toggle id={'space-restricted'} bind:on={restricted} />
</div>

{#each roles as role}
Expand Down
Loading