Fix permissions for mixins/tags (#10294)

* Fix permissions for mixins/tags

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>

* Fix

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>

---------

Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
Denis Bykhov
2025-12-12 10:15:56 +07:00
committed by GitHub
parent a10dde0b9a
commit 0762a42922
15 changed files with 54 additions and 30 deletions
@@ -174,10 +174,10 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
const permissions = this.permissionsBySpace[space]?.[account.uuid] ?? []
let withoutMatch: Permission | undefined
for (const permission of permissions) {
if (permission.txClass === undefined || permission.txClass !== tx._class) continue
if (!isTxClassMatched(tx, permission)) continue
if (
permission.objectClass !== undefined &&
!this.context.hierarchy.isDerived(tx.objectClass, permission.objectClass)
!this.context.hierarchy.isDerived(getTxObjectClass(tx), permission.objectClass)
) {
continue
}
@@ -203,12 +203,13 @@ export class SpacePermissionsMiddleware extends BaseMiddleware implements Middle
if (this.restrictedSpaces.has(space)) {
const attachedDocAncestors = this.context.hierarchy.getAncestors(core.class.AttachedDoc)
const ancestors = this.context.hierarchy.getAncestors(tx.objectClass)
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: tx._class
txClass
})
const matched = permissions.filter((p) => {
if (p.txMatch === undefined) return false
@@ -460,3 +461,20 @@ 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 isTxClassMatched (tx: Tx, permission: Permission): boolean {
const txClass = getTxClass(tx)
return permission.txClass === txClass
}
function getTxObjectClass (tx: TxCUD<Doc>): Ref<Class<Doc>> {
return tx._class === core.class.TxMixin ? (tx as TxMixin<Doc, Doc>).mixin : tx.objectClass
}
@@ -217,7 +217,16 @@
<Component is={extension.component} props={{ collaborators, data, space: _space }} on:change={handleChange} />
{/if}
</div>
<div slot="afterContent" class="error p-4 flex-row-reverse">
{#if !allowed}
<Label label={view.string.NoCreatePermissionTitle} />
{/if}
</div>
</Modal>
<style lang="scss">
.error {
color: var(--theme-error-color);
}
</style>
+10 -25
View File
@@ -751,33 +751,18 @@ export const permissionsStore = derived(
}
)
const spaceTypesQuery = createQuery(true)
const permissionsQuery = createQuery(true)
type TargetClassesProjection = Record<Ref<Class<Space>>, number>
spaceTypesQuery.query(core.class.SpaceType, {}, (types) => {
const targetClasses = types.reduce<TargetClassesProjection>((acc, st) => {
acc[st.targetClass] = 1
return acc
}, {})
permissionsQuery.query(
core.class.Space,
{},
(res) => {
spacesStore.set(res)
},
{
showArchived: true,
projection: {
_id: 1,
type: 1,
members: 1,
...targetClasses
} as any
}
)
})
permissionsQuery.query(
core.class.Space,
{},
(res) => {
spacesStore.set(res)
},
{
showArchived: true
}
)
export function getAccountClient (): AccountClient {
const accountsUrl = getMetadata(login.metadata.AccountsUrl)
+1
View File
@@ -15,6 +15,7 @@
"Role": "Role",
"DeleteObject": "Smazat objekt",
"DeleteObjectConfirm": "Chcete smazat {count, plural, =1 {tento objekt} other {těchto # objektů}}?",
"NoCreatePermissionTitle": "Nemáte dostatečná oprávnění pro vytvoření",
"DeletePopupNoPermissionTitle": "Nemáte dostatečná oprávnění pro smazání.",
"DeletePopupNoPermissionLabel": "Kontaktujte prosím osoby ze seznamu níže, aby to mohly provést.",
"DeletePopupCreatorLabel": "Tvůrci (mohou mazat pouze objekty, které vytvořili):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Rolle",
"DeleteObject": "Objekt löschen",
"DeleteObjectConfirm": "Möchten Sie {count, plural, =1 {dieses Objekt} other {diese # Objekte}} löschen?",
"NoCreatePermissionTitle": "Keine ausreichenden Berechtigungen zum Erstellen",
"DeletePopupNoPermissionTitle": "Keine ausreichenden Berechtigungen zum Löschen.",
"DeletePopupNoPermissionLabel": "Bitte kontaktieren Sie Personen aus der folgenden Liste.",
"DeletePopupCreatorLabel": "Ersteller (können nur von ihnen erstellte Objekte löschen):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Role",
"DeleteObject": "Delete object",
"DeleteObjectConfirm": "Do you want to delete {count, plural, =1 {this object} other {these # objects}}?",
"NoCreatePermissionTitle": "Not enough permissions to create",
"DeletePopupNoPermissionTitle": "Not enough permissions to delete.",
"DeletePopupNoPermissionLabel": "Please contact people from the list below for them to do so.",
"DeletePopupCreatorLabel": "Creators (can delete only ones that were created by them):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Rol",
"DeleteObject": "Eliminar objeto",
"DeleteObjectConfirm": "¿Desea eliminar {count, plural, =1 {este objeto} other {estos # objetos}}?",
"NoCreatePermissionTitle": "Permisos insuficientes para crear",
"DeletePopupNoPermissionTitle": "Permisos insuficientes para eliminar.",
"DeletePopupNoPermissionLabel": "Por favor, contacte con las personas de la siguiente lista para que puedan eliminarlo.",
"DeletePopupCreatorLabel": "Creadores (pueden eliminar solo los que ellos crearon):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Rôle",
"DeleteObject": "Supprimer l'objet",
"DeleteObjectConfirm": "Voulez-vous supprimer {count, plural, =1 {cet objet} other {ces # objets}} ?",
"NoCreatePermissionTitle": "Permissions insuffisantes pour créer",
"DeletePopupNoPermissionTitle": "Permissions insuffisantes pour supprimer.",
"DeletePopupNoPermissionLabel": "Veuillez contacter les personnes de la liste ci-dessous pour qu'elles le fassent.",
"DeletePopupCreatorLabel": "Créateurs (peuvent supprimer uniquement ceux qu'ils ont créés) :",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Ruolo",
"DeleteObject": "Elimina oggetto",
"DeleteObjectConfirm": "Vuoi eliminare {count, plural, =1 {questo oggetto} other {questi # oggetti}}?",
"NoCreatePermissionTitle": "Permessi insufficienti per creare",
"DeletePopupNoPermissionTitle": "Permessi insufficienti per eliminare.",
"DeletePopupNoPermissionLabel": "Contatta le persone della lista qui sotto affinché possano farlo.",
"DeletePopupCreatorLabel": "Creatori (possono eliminare solo quelli creati da loro):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "役割",
"DeleteObject": "オブジェクトを削除",
"DeleteObjectConfirm": "{count, plural, =1 {このオブジェクト} other {これらの # 個のオブジェクト}}を削除しますか?",
"NoCreatePermissionTitle": "作成する権限がありません",
"DeletePopupNoPermissionTitle": "削除する権限がありません。",
"DeletePopupNoPermissionLabel": "削除するには、以下のリストの担当者にご連絡ください。",
"DeletePopupCreatorLabel": "作成者 (自分で作成したもののみ削除できます):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Função",
"DeleteObject": "Apagar objecto",
"DeleteObjectConfirm": "Deseja apagar {count, plural, =1 {este objecto} other {estes # objectos}}?",
"NoCreatePermissionTitle": "Sem permissões suficientes para criar",
"DeletePopupNoPermissionTitle": "Sem permissões suficientes para apagar.",
"DeletePopupNoPermissionLabel": "Por favor, contacte as pessoas da lista abaixo para que o possam fazer.",
"DeletePopupCreatorLabel": "Criadores (só podem apagar os que criaram):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Роль",
"DeleteObject": "Удалить объект",
"DeleteObjectConfirm": "Вы действительно хотите удалить {count, plural, =1 {этот объект} one {этот # объект} few {эти # объекта} other {эти # объектов}}?",
"NoCreatePermissionTitle": "Недостаточно прав для создания",
"DeletePopupNoPermissionTitle": "Недостаточно прав для удаления.",
"DeletePopupNoPermissionLabel": "Пожалуйста, обратитесь к людям из списка ниже, чтобы они это сделали.",
"DeletePopupCreatorLabel": "Создатели объектов (могут удалить лишь те, что были созданы ими):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "Rol",
"DeleteObject": "Nesneyi sil",
"DeleteObjectConfirm": "{count, plural, =1 {bu nesneyi} other {bu # nesneyi}} silmek istiyor musunuz?",
"NoCreatePermissionTitle": "Oluşturmak için yeterli izin yok",
"DeletePopupNoPermissionTitle": "Silmek için yeterli izin yok.",
"DeletePopupNoPermissionLabel": "Lütfen aşağıdaki listedeki kişilerle iletişime geçin.",
"DeletePopupCreatorLabel": "Oluşturanlar (sadece kendilerinin oluşturduklarını silebilir):",
+1
View File
@@ -15,6 +15,7 @@
"Role": "角色",
"DeleteObject": "删除对象",
"DeleteObjectConfirm": "您想要删除 {count, plural, =1 {这个对象} other {这些 # 个对象}} 吗?",
"NoCreatePermissionTitle": "没有足够的权限创建",
"DeletePopupNoPermissionTitle": "没有足够的权限删除。",
"DeletePopupNoPermissionLabel": "请联系以下列表中的人员以便他们执行此操作。",
"DeletePopupCreatorLabel": "创建者(只能删除他们创建的):",
+2 -1
View File
@@ -255,7 +255,8 @@ const view = plugin(viewId, {
CopyDocumentMarkdown: '' as IntlString,
RoleLabel: '' as IntlString,
ForbidAttributeChanges: '' as IntlString,
AllowAttributeChanges: '' as IntlString
AllowAttributeChanges: '' as IntlString,
NoCreatePermissionTitle: '' as IntlString
},
icon: {
Table: '' as Asset,