feat: RBAC (#8922)

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Val <68596159+valya@users.noreply.github.com>
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
Co-authored-by: Valya Bullions <valya@n8n.io>
Co-authored-by: Danny Martini <danny@n8n.io>
Co-authored-by: Danny Martini <despair.blue@gmail.com>
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Omar Ajoue <krynble@gmail.com>
Co-authored-by: oleg <me@olegivaniv.com>
Co-authored-by: Michael Kret <michael.k@radency.com>
Co-authored-by: Michael Kret <88898367+michael-radency@users.noreply.github.com>
Co-authored-by: Elias Meire <elias@meire.dev>
Co-authored-by: Giulio Andreini <andreini@netseven.it>
Co-authored-by: Giulio Andreini <g.andreini@gmail.com>
Co-authored-by: Ayato Hayashi <go12limchangyong@gmail.com>
This commit is contained in:
Csaba Tuncsik
2024-05-17 10:53:15 +02:00
committed by GitHub
parent b1f977ebd0
commit 596c472ecc
292 changed files with 14129 additions and 3989 deletions

View File

@@ -6,13 +6,15 @@
:filters="filters"
:additional-filters-handler="onFilter"
:type-props="{ itemSize: 80 }"
:show-aside="allWorkflows.length > 0"
:shareable="isShareable"
:initialize="initialize"
:disabled="readOnlyEnv"
@click:add="addWorkflow"
@update:filters="onFiltersUpdated"
>
<template #header>
<ProjectTabs v-if="showProjectTabs" />
</template>
<template #add-button="{ disabled }">
<n8n-tooltip :disabled="!readOnlyEnv">
<div>
@@ -23,7 +25,7 @@
data-test-id="resources-list-add"
@click="addWorkflow"
>
{{ $locale.baseText(`workflows.add`) }}
{{ addWorkflowButtonText }}
</n8n-button>
</div>
<template #content>
@@ -171,11 +173,11 @@ import { mapStores } from 'pinia';
import { useUIStore } from '@/stores/ui.store';
import { useSettingsStore } from '@/stores/settings.store';
import { useUsersStore } from '@/stores/users.store';
import { useTemplatesStore } from '@/stores/templates.store';
import { useWorkflowsStore } from '@/stores/workflows.store';
import { useCredentialsStore } from '@/stores/credentials.store';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { useTagsStore } from '@/stores/tags.store';
import { useProjectsStore } from '@/features/projects/projects.store';
import ProjectTabs from '@/features/projects/components/ProjectTabs.vue';
type IResourcesListLayoutInstance = InstanceType<typeof ResourcesListLayout>;
@@ -201,13 +203,13 @@ const WorkflowsView = defineComponent({
TagsDropdown,
SuggestedTemplatesPage,
SuggestedTemplatesSection,
ProjectTabs,
},
data() {
return {
filters: {
search: '',
ownedBy: '',
sharedWith: '',
homeProject: '',
status: StatusFilter.ALL as string | boolean,
tags: [] as string[],
},
@@ -220,11 +222,9 @@ const WorkflowsView = defineComponent({
useUIStore,
useUsersStore,
useWorkflowsStore,
useCredentialsStore,
useSourceControlStore,
useTagsStore,
useTemplatesStore,
useUsersStore,
useProjectsStore,
),
readOnlyEnv(): boolean {
return this.sourceControlStore.preferences.branchReadOnly;
@@ -269,14 +269,29 @@ const WorkflowsView = defineComponent({
}
return ['Sales', 'sales-and-marketing'].includes(this.userRole);
},
showProjectTabs() {
return (
!!this.$route.params.projectId ||
!!this.allWorkflows.length ||
this.projectsStore.myProjects.length > 1
);
},
addWorkflowButtonText() {
return this.projectsStore.currentProject
? this.$locale.baseText('workflows.project.add')
: this.$locale.baseText('workflows.add');
},
},
watch: {
'filters.tags'() {
this.sendFiltersTelemetry('tags');
},
'$route.params.projectId'() {
void this.initialize();
},
},
mounted() {
this.setFiltersFromQueryString();
async mounted() {
await this.setFiltersFromQueryString();
void this.usersStore.showPersonalizationSurvey();
@@ -298,7 +313,10 @@ const WorkflowsView = defineComponent({
},
addWorkflow() {
this.uiStore.nodeViewInitialized = false;
void this.$router.push({ name: VIEWS.NEW_WORKFLOW });
void this.$router.push({
name: VIEWS.NEW_WORKFLOW,
query: { projectId: this.$route?.params?.projectId },
});
this.$telemetry.track('User clicked add workflow button', {
source: 'Workflows list',
@@ -316,9 +334,8 @@ const WorkflowsView = defineComponent({
async initialize() {
await Promise.all([
this.usersStore.fetchUsers(),
this.workflowsStore.fetchAllWorkflows(),
this.workflowsStore.fetchAllWorkflows(this.$route?.params?.projectId as string | undefined),
this.workflowsStore.fetchActiveWorkflows(),
this.credentialsStore.fetchAllCredentials(),
]);
},
onClickTag(tagId: string, event: PointerEvent) {
@@ -367,32 +384,27 @@ const WorkflowsView = defineComponent({
query.tags = this.filters.tags.join(',');
}
if (this.filters.ownedBy) {
query.ownedBy = this.filters.ownedBy;
}
if (this.filters.sharedWith) {
query.sharedWith = this.filters.sharedWith;
if (this.filters.homeProject) {
query.homeProject = this.filters.homeProject;
}
void this.$router.replace({
query: Object.keys(query).length ? query : undefined,
});
},
isValidUserId(userId: string) {
return Object.keys(this.usersStore.users).includes(userId);
isValidProjectId(projectId: string) {
return this.projectsStore.projects.some((project) => project.id === projectId);
},
setFiltersFromQueryString() {
const { tags, status, search, ownedBy, sharedWith } = this.$route.query;
async setFiltersFromQueryString() {
const { tags, status, search, homeProject } = this.$route.query;
const filtersToApply: { [key: string]: string | string[] | boolean } = {};
if (ownedBy && typeof ownedBy === 'string' && this.isValidUserId(ownedBy)) {
filtersToApply.ownedBy = ownedBy;
}
if (sharedWith && typeof sharedWith === 'string' && this.isValidUserId(sharedWith)) {
filtersToApply.sharedWith = sharedWith;
if (homeProject && typeof homeProject === 'string') {
await this.projectsStore.getAllProjects();
if (this.isValidProjectId(homeProject)) {
filtersToApply.homeProject = homeProject;
}
}
if (search && typeof search === 'string') {