feat: Introduce advanced permissions (#7844)

This PR introduces the possibility of inviting new users with an `admin`
role and changing the role of already invited users.
Also using scoped permission checks where applicable instead of using
user role checks.

---------

Co-authored-by: Val <68596159+valya@users.noreply.github.com>
Co-authored-by: Alex Grozav <alex@grozav.com>
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
Csaba Tuncsik
2023-12-08 12:52:25 +01:00
committed by GitHub
parent e00577b1d3
commit dbd62a4992
26 changed files with 364 additions and 71 deletions

View File

@@ -61,7 +61,7 @@
data-test-id="credentials-config-container-test-success"
/>
<template v-if="credentialPermissions.updateConnection">
<template v-if="credentialPermissions.update">
<n8n-notice v-if="documentationUrl && credentialProperties.length" theme="warning">
{{ $locale.baseText('credentialEdit.credentialConfig.needHelpFillingOutTheseFields') }}
<span class="ml-4xs">
@@ -104,7 +104,7 @@
</enterprise-edition>
<CredentialInputs
v-if="credentialType && credentialPermissions.updateConnection"
v-if="credentialType && credentialPermissions.update"
:credentialData="credentialData"
:credentialProperties="credentialProperties"
:documentationUrl="documentationUrl"

View File

@@ -17,7 +17,7 @@
<InlineNameEdit
:modelValue="credentialName"
:subtitle="credentialType ? credentialType.displayName : ''"
:readonly="!credentialPermissions.updateName || !credentialType"
:readonly="!credentialPermissions.update || !credentialType"
type="Credential"
@update:modelValue="onNameEdit"
data-test-id="credential-name"
@@ -224,6 +224,7 @@ export default defineComponent({
selectedCredential: '',
requiredCredentials: false, // Are credentials required or optional for the node
hasUserSpecifiedName: false,
isSharedWithChanged: false,
};
},
async mounted() {
@@ -683,6 +684,7 @@ export default defineComponent({
...this.credentialData,
sharedWith: sharees,
};
this.isSharedWithChanged = true;
this.hasUnsavedChanges = true;
},
@@ -920,10 +922,23 @@ export default defineComponent({
): Promise<ICredentialsResponse | null> {
let credential;
try {
credential = await this.credentialsStore.updateCredential({
id: this.credentialId,
data: credentialDetails,
});
if (this.credentialPermissions.update) {
credential = await this.credentialsStore.updateCredential({
id: this.credentialId,
data: credentialDetails,
});
}
if (
this.credentialPermissions.share &&
this.isSharedWithChanged &&
credentialDetails.sharedWith
) {
credential = await this.credentialsStore.setCredentialSharedWith({
credentialId: credentialDetails.id,
sharedWith: credentialDetails.sharedWith,
});
this.isSharedWithChanged = false;
}
this.hasUnsavedChanges = false;
} catch (error) {
this.showError(

View File

@@ -9,7 +9,7 @@
<el-col :span="16">
<div v-for="node in nodesWithAccess" :key="node.name" :class="$style.valueLabel">
<el-checkbox
v-if="credentialPermissions.updateNodeAccess"
v-if="credentialPermissions.update"
:label="
$locale.headerText({
key: `headers.${shortNodeType(node)}.displayName`,

View File

@@ -34,7 +34,7 @@
<n8n-info-tip v-if="credentialPermissions.isOwner" :bold="false" class="mb-s">
{{ $locale.baseText('credentialEdit.credentialSharing.info.owner') }}
</n8n-info-tip>
<n8n-info-tip v-if="!credentialPermissions.updateSharing" :bold="false" class="mb-s">
<n8n-info-tip v-if="!credentialPermissions.share" :bold="false" class="mb-s">
{{
$locale.baseText('credentialEdit.credentialSharing.info.sharee', {
interpolate: { credentialOwnerName },
@@ -42,10 +42,14 @@
}}
</n8n-info-tip>
<n8n-info-tip v-if="credentialPermissions.read" class="mb-s" :bold="false">
{{ $locale.baseText('credentialEdit.credentialSharing.info.reader') }}
<i18n-t keypath="credentialEdit.credentialSharing.info.reader">
<template v-if="!isCredentialSharedWithCurrentUser" #notShared>
{{ $locale.baseText('credentialEdit.credentialSharing.info.notShared') }}
</template>
</i18n-t>
</n8n-info-tip>
<n8n-user-select
v-if="credentialPermissions.updateSharing"
v-if="credentialPermissions.share"
class="mb-s"
size="large"
:users="usersList"
@@ -62,7 +66,7 @@
:actions="usersListActions"
:users="sharedWithList"
:currentUserId="usersStore.currentUser.id"
:readonly="!credentialPermissions.updateSharing"
:readonly="!credentialPermissions.share"
@delete="onRemoveSharee"
/>
</div>
@@ -114,13 +118,12 @@ export default defineComponent({
},
usersList(): IUser[] {
return this.usersStore.allUsers.filter((user: IUser) => {
const isCurrentUser = user.id === this.usersStore.currentUser?.id;
const isAlreadySharedWithUser = (this.credentialData.sharedWith || []).find(
(sharee: IUser) => sharee.id === user.id,
);
const isOwner = this.credentialData.ownedBy.id === user.id;
const isOwner = this.credentialData.ownedBy?.id === user.id;
return !isCurrentUser && !isAlreadySharedWithUser && !isOwner;
return !isAlreadySharedWithUser && !isOwner;
});
},
sharedWithList(): IUser[] {
@@ -134,6 +137,11 @@ export default defineComponent({
credentialOwnerName(): string {
return this.credentialsStore.getCredentialOwnerNameById(`${this.credentialId}`);
},
isCredentialSharedWithCurrentUser(): boolean {
return (this.credentialData.sharedWith || []).some((sharee: IUser) => {
return sharee.id === this.usersStore.currentUser?.id;
});
},
},
methods: {
async onAddSharee(userId: string) {