From 6955e8991ca2ec13e6298c3c18ec2b28853ceda4 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Mon, 18 Mar 2024 11:39:15 +0100 Subject: [PATCH] fix(editor): Remove `isOwner` from IUser interface (#8888) --- packages/editor-ui/src/Interface.ts | 10 +++++----- packages/editor-ui/src/__tests__/permissions.spec.ts | 5 ++++- .../editor-ui/src/__tests__/server/factories/user.ts | 9 ++++++--- packages/editor-ui/src/api/users.ts | 4 ++-- packages/editor-ui/src/api/workflow-webhooks.ts | 3 ++- .../editor-ui/src/components/InviteUsersModal.vue | 8 ++++++-- .../src/components/MainHeader/CollaborationPane.vue | 3 ++- .../src/components/__tests__/BannersStack.test.ts | 6 +++--- .../components/__tests__/CollaborationPane.test.ts | 11 ++++------- .../components/__tests__/PersonalizationModal.spec.ts | 4 ++-- .../src/components/banners/__tests__/V1Banner.spec.ts | 6 ++++-- packages/editor-ui/src/constants.ts | 7 +++++++ packages/editor-ui/src/permissions.ts | 3 ++- .../src/rbac/checks/__tests__/hasRole.test.ts | 4 ++-- packages/editor-ui/src/rbac/checks/hasRole.ts | 2 +- .../src/rbac/middleware/__tests__/role.test.ts | 7 +++---- packages/editor-ui/src/stores/__tests__/ui.test.ts | 11 ++++++----- packages/editor-ui/src/stores/cloudPlan.store.ts | 8 +++----- packages/editor-ui/src/stores/ui.store.ts | 1 - packages/editor-ui/src/stores/users.store.ts | 5 ++--- packages/editor-ui/src/utils/userUtils.ts | 10 +++------- packages/editor-ui/src/views/SettingsUsageAndPlan.vue | 5 +---- packages/editor-ui/src/views/SettingsUsersView.vue | 4 ++-- .../src/views/__tests__/SettingsPersonalView.test.ts | 3 ++- 24 files changed, 74 insertions(+), 65 deletions(-) diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index 21e54474e05..70848e67eaf 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -7,7 +7,8 @@ import type { REGULAR_NODE_CREATOR_VIEW, AI_OTHERS_NODE_CREATOR_VIEW, VIEWS, -} from './constants'; + ROLE, +} from '@/constants'; import type { IMenuItem } from 'n8n-design-system'; import { type GenericValue, @@ -688,9 +689,9 @@ export type IPersonalizationSurveyVersions = | IPersonalizationSurveyAnswersV2 | IPersonalizationSurveyAnswersV3; -export type IRole = 'default' | 'global:owner' | 'global:member' | 'global:admin'; - -export type InvitableRoleName = 'global:member' | 'global:admin'; +export type Roles = typeof ROLE; +export type IRole = Roles[keyof Roles]; +export type InvitableRoleName = Roles['Member' | 'Admin']; export interface IUserResponse { id: string; @@ -714,7 +715,6 @@ export interface IUser extends IUserResponse { isDefaultUser: boolean; isPendingUser: boolean; hasRecoveryCodesLeft: boolean; - isOwner: boolean; inviteAcceptUrl?: string; fullName?: string; createdAt?: string; diff --git a/packages/editor-ui/src/__tests__/permissions.spec.ts b/packages/editor-ui/src/__tests__/permissions.spec.ts index 987a6544269..f95b9549d94 100644 --- a/packages/editor-ui/src/__tests__/permissions.spec.ts +++ b/packages/editor-ui/src/__tests__/permissions.spec.ts @@ -1,5 +1,6 @@ import { parsePermissionsTable } from '@/permissions'; import type { IUser } from '@/Interface'; +import { ROLE } from '@/constants'; describe('parsePermissionsTable()', () => { const user: IUser = { @@ -7,9 +8,11 @@ describe('parsePermissionsTable()', () => { firstName: 'John', lastName: 'Doe', isDefaultUser: false, - isOwner: true, isPending: false, isPendingUser: false, + mfaEnabled: false, + hasRecoveryCodesLeft: false, + role: ROLE.Owner, }; it('should return permissions object using generic permissions table', () => { diff --git a/packages/editor-ui/src/__tests__/server/factories/user.ts b/packages/editor-ui/src/__tests__/server/factories/user.ts index f8d593b5645..e8fc06cec97 100644 --- a/packages/editor-ui/src/__tests__/server/factories/user.ts +++ b/packages/editor-ui/src/__tests__/server/factories/user.ts @@ -16,9 +16,6 @@ export const userFactory = Factory.extend({ isDefaultUser() { return false; }, - isOwner() { - return false; - }, isPending() { return false; }, @@ -28,4 +25,10 @@ export const userFactory = Factory.extend({ signInType(): SignInType { return SignInType.EMAIL; }, + mfaEnabled() { + return false; + }, + hasRecoveryCodesLeft() { + return false; + }, }); diff --git a/packages/editor-ui/src/api/users.ts b/packages/editor-ui/src/api/users.ts index 49c532d9b8a..e1a9daa3c2e 100644 --- a/packages/editor-ui/src/api/users.ts +++ b/packages/editor-ui/src/api/users.ts @@ -2,8 +2,8 @@ import type { CurrentUserResponse, IPersonalizationLatestVersion, IRestApiContext, - IRole, IUserResponse, + InvitableRoleName, } from '@/Interface'; import type { IDataObject } from 'n8n-workflow'; import { makeRestApiRequest } from '@/utils/apiUtils'; @@ -157,7 +157,7 @@ export async function submitPersonalizationSurvey( export interface UpdateGlobalRolePayload { id: string; - newRoleName: Exclude; + newRoleName: InvitableRoleName; } export async function updateGlobalRole( diff --git a/packages/editor-ui/src/api/workflow-webhooks.ts b/packages/editor-ui/src/api/workflow-webhooks.ts index 99ef13cb051..a84de1c9221 100644 --- a/packages/editor-ui/src/api/workflow-webhooks.ts +++ b/packages/editor-ui/src/api/workflow-webhooks.ts @@ -1,5 +1,6 @@ import type { IOnboardingCallPrompt, IUser } from '@/Interface'; import { get, post } from '@/utils/apiUtils'; +import { isUserGlobalOwner } from '@/utils/userUtils'; const N8N_API_BASE_URL = 'https://api.n8n.io/api'; const ONBOARDING_PROMPTS_ENDPOINT = '/prompts/onboarding'; @@ -12,7 +13,7 @@ export async function fetchNextOnboardingPrompt( return await get(N8N_API_BASE_URL, ONBOARDING_PROMPTS_ENDPOINT, { instance_id: instanceId, user_id: `${instanceId}#${currentUser.id}`, - is_owner: currentUser.isOwner ?? false, + is_owner: isUserGlobalOwner(currentUser), survey_results: currentUser.personalizationAnswers, }); } diff --git a/packages/editor-ui/src/components/InviteUsersModal.vue b/packages/editor-ui/src/components/InviteUsersModal.vue index ec9033f415c..b21fa9e4605 100644 --- a/packages/editor-ui/src/components/InviteUsersModal.vue +++ b/packages/editor-ui/src/components/InviteUsersModal.vue @@ -66,8 +66,12 @@ import { mapStores } from 'pinia'; import { useToast } from '@/composables/useToast'; import Modal from './Modal.vue'; import type { IFormInputs, IInviteResponse, IUser } from '@/Interface'; -import { ROLE } from '@/utils/userUtils'; -import { EnterpriseEditionFeature, VALID_EMAIL_REGEX, INVITE_USER_MODAL_KEY } from '@/constants'; +import { + EnterpriseEditionFeature, + VALID_EMAIL_REGEX, + INVITE_USER_MODAL_KEY, + ROLE, +} from '@/constants'; import { useUsersStore } from '@/stores/users.store'; import { useSettingsStore } from '@/stores/settings.store'; import { useUIStore } from '@/stores/ui.store'; diff --git a/packages/editor-ui/src/components/MainHeader/CollaborationPane.vue b/packages/editor-ui/src/components/MainHeader/CollaborationPane.vue index 541079cf5e1..31d387b3dbe 100644 --- a/packages/editor-ui/src/components/MainHeader/CollaborationPane.vue +++ b/packages/editor-ui/src/components/MainHeader/CollaborationPane.vue @@ -4,6 +4,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store'; import { useCollaborationStore } from '@/stores/collaboration.store'; import { onBeforeUnmount, onMounted, computed, ref } from 'vue'; import { TIME } from '@/constants'; +import { isUserGlobalOwner } from '@/utils/userUtils'; const collaborationStore = useCollaborationStore(); const usersStore = useUsersStore(); @@ -16,7 +17,7 @@ const activeUsersSorted = computed(() => { const currentWorkflowUsers = (collaborationStore.getUsersForCurrentWorkflow ?? []).map( (userInfo) => userInfo.user, ); - const owner = currentWorkflowUsers.find((user) => user.role === 'global:owner'); + const owner = currentWorkflowUsers.find(isUserGlobalOwner); return { defaultGroup: owner ? [owner, ...currentWorkflowUsers.filter((user) => user.id !== owner.id)] diff --git a/packages/editor-ui/src/components/__tests__/BannersStack.test.ts b/packages/editor-ui/src/components/__tests__/BannersStack.test.ts index 728d7a67572..dc4df6bc6eb 100644 --- a/packages/editor-ui/src/components/__tests__/BannersStack.test.ts +++ b/packages/editor-ui/src/components/__tests__/BannersStack.test.ts @@ -2,7 +2,7 @@ import { merge } from 'lodash-es'; import userEvent from '@testing-library/user-event'; import { SETTINGS_STORE_DEFAULT_STATE } from '@/__tests__/utils'; -import { STORES } from '@/constants'; +import { ROLE, STORES } from '@/constants'; import { createTestingPinia } from '@pinia/testing'; import BannerStack from '@/components/banners/BannerStack.vue'; @@ -26,11 +26,11 @@ const initialState = { users: { 'aaa-bbb': { id: 'aaa-bbb', - role: 'global:owner', + role: ROLE.Owner, }, 'bbb-bbb': { id: 'bbb-bbb', - role: 'global:member', + role: ROLE.Member, }, }, }, diff --git a/packages/editor-ui/src/components/__tests__/CollaborationPane.test.ts b/packages/editor-ui/src/components/__tests__/CollaborationPane.test.ts index 0fddfb7a96d..793bce590d0 100644 --- a/packages/editor-ui/src/components/__tests__/CollaborationPane.test.ts +++ b/packages/editor-ui/src/components/__tests__/CollaborationPane.test.ts @@ -1,6 +1,6 @@ import { merge } from 'lodash-es'; import { SETTINGS_STORE_DEFAULT_STATE, waitAllPromises } from '@/__tests__/utils'; -import { STORES } from '@/constants'; +import { ROLE, STORES } from '@/constants'; import { createTestingPinia } from '@pinia/testing'; import { useUIStore } from '@/stores/ui.store'; import CollaborationPane from '@/components//MainHeader/CollaborationPane.vue'; @@ -13,10 +13,9 @@ const OWNER_USER = { email: 'owner@user.com', firstName: 'Owner', lastName: 'User', - role: 'global:owner', + role: ROLE.Owner, disabled: false, isPending: false, - isOwner: true, fullName: 'Owner User', }; @@ -26,10 +25,9 @@ const MEMBER_USER = { email: 'member@user.com', firstName: 'Member', lastName: 'User', - role: 'global:member', + role: ROLE.Member, disabled: false, isPending: false, - isOwner: false, fullName: 'Member User', }; @@ -39,10 +37,9 @@ const MEMBER_USER_2 = { email: 'member2@user.com', firstName: 'Another Member', lastName: 'User', - role: 'global:member', + role: ROLE.Member, disabled: false, isPending: false, - isOwner: false, fullName: 'Another Member User', }; diff --git a/packages/editor-ui/src/components/__tests__/PersonalizationModal.spec.ts b/packages/editor-ui/src/components/__tests__/PersonalizationModal.spec.ts index 9b7d5a0d7d0..ccd515cce1e 100644 --- a/packages/editor-ui/src/components/__tests__/PersonalizationModal.spec.ts +++ b/packages/editor-ui/src/components/__tests__/PersonalizationModal.spec.ts @@ -1,7 +1,7 @@ import PersonalizationModal from '@/components/PersonalizationModal.vue'; import { createTestingPinia } from '@pinia/testing'; import userEvent from '@testing-library/user-event'; -import { PERSONALIZATION_MODAL_KEY, STORES, VIEWS } from '@/constants'; +import { PERSONALIZATION_MODAL_KEY, ROLE, STORES, VIEWS } from '@/constants'; import { retry } from '@/__tests__/utils'; import { createComponentRenderer } from '@/__tests__/render'; import { fireEvent } from '@testing-library/vue'; @@ -31,7 +31,7 @@ const pinia = createTestingPinia({ isDefaultUser: false, isPendingUser: false, hasRecoveryCodesLeft: true, - isOwner: true, + role: ROLE.Owner, mfaEnabled: false, }, }, diff --git a/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts b/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts index 346f8bf01b5..b2e7ac6f4a6 100644 --- a/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts +++ b/packages/editor-ui/src/components/banners/__tests__/V1Banner.spec.ts @@ -2,6 +2,8 @@ import { render } from '@testing-library/vue'; import V1Banner from '../V1Banner.vue'; import { createPinia, setActivePinia } from 'pinia'; import { useUsersStore } from '@/stores/users.store'; +import { ROLE } from '@/constants'; +import type { IUser } from '@/Interface'; describe('V1 Banner', () => { let pinia: ReturnType; @@ -22,8 +24,8 @@ describe('V1 Banner', () => { it('should render banner with dismiss call if user is owner', () => { vi.spyOn(usersStore, 'currentUser', 'get').mockReturnValue({ - role: 'global:owner', - }); + role: ROLE.Owner, + } as IUser); const { container } = render(V1Banner); expect(container).toMatchSnapshot(); diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts index 747efba6842..67271b81543 100644 --- a/packages/editor-ui/src/constants.ts +++ b/packages/editor-ui/src/constants.ts @@ -752,3 +752,10 @@ export const TEMPLATES_URLS = { BASE_WEBSITE_URL: 'https://n8n.io/workflows', UTM_QUERY: 'utm_source=n8n_app&utm_medium=template_library', }; + +export const ROLE = { + Owner: 'global:owner', + Member: 'global:member', + Admin: 'global:admin', + Default: 'default', // default user with no email when setting up instance +} as const; diff --git a/packages/editor-ui/src/permissions.ts b/packages/editor-ui/src/permissions.ts index a32820dc780..1343a8bd49e 100644 --- a/packages/editor-ui/src/permissions.ts +++ b/packages/editor-ui/src/permissions.ts @@ -8,6 +8,7 @@ import type { IUser, ICredentialsResponse, IWorkflowDb } from '@/Interface'; import { EnterpriseEditionFeature, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants'; import { useSettingsStore } from '@/stores/settings.store'; import { hasPermission } from './rbac/permissions'; +import { isUserGlobalOwner } from './utils/userUtils'; /** * Old permissions implementation @@ -43,7 +44,7 @@ export const parsePermissionsTable = ( table: IPermissionsTable, ): IPermissions => { const genericTable: IPermissionsTable = [ - { name: UserRole.InstanceOwner, test: () => !!user?.isOwner }, + { name: UserRole.InstanceOwner, test: () => (user ? isUserGlobalOwner(user) : false) }, ]; return [...genericTable, ...table].reduce( diff --git a/packages/editor-ui/src/rbac/checks/__tests__/hasRole.test.ts b/packages/editor-ui/src/rbac/checks/__tests__/hasRole.test.ts index adff9c6c58d..5e7ff0a4152 100644 --- a/packages/editor-ui/src/rbac/checks/__tests__/hasRole.test.ts +++ b/packages/editor-ui/src/rbac/checks/__tests__/hasRole.test.ts @@ -1,6 +1,6 @@ import { useUsersStore } from '@/stores/users.store'; import { hasRole } from '@/rbac/checks'; -import { ROLE } from '@/utils/userUtils'; +import { ROLE } from '@/constants'; vi.mock('@/stores/users.store', () => ({ useUsersStore: vi.fn(), @@ -12,7 +12,7 @@ describe('Checks', () => { vi.mocked(useUsersStore).mockReturnValue({ currentUser: { isDefaultUser: false, - role: 'global:owner', + role: ROLE.Owner, }, } as ReturnType); diff --git a/packages/editor-ui/src/rbac/checks/hasRole.ts b/packages/editor-ui/src/rbac/checks/hasRole.ts index 7920d42d05a..f9590026e77 100644 --- a/packages/editor-ui/src/rbac/checks/hasRole.ts +++ b/packages/editor-ui/src/rbac/checks/hasRole.ts @@ -1,6 +1,6 @@ import { useUsersStore } from '@/stores/users.store'; import type { RBACPermissionCheck, RolePermissionOptions } from '@/types/rbac'; -import { ROLE } from '@/utils/userUtils'; +import { ROLE } from '@/constants'; import type { IRole } from '@/Interface'; export const hasRole: RBACPermissionCheck = (checkRoles) => { diff --git a/packages/editor-ui/src/rbac/middleware/__tests__/role.test.ts b/packages/editor-ui/src/rbac/middleware/__tests__/role.test.ts index 2f07a97a461..17a87cf8d30 100644 --- a/packages/editor-ui/src/rbac/middleware/__tests__/role.test.ts +++ b/packages/editor-ui/src/rbac/middleware/__tests__/role.test.ts @@ -1,9 +1,8 @@ import { roleMiddleware } from '@/rbac/middleware/role'; import { useUsersStore } from '@/stores/users.store'; -import { ROLE } from '@/utils/userUtils'; import type { IUser } from '@/Interface'; import type { RouteLocationNormalized } from 'vue-router'; -import { VIEWS } from '@/constants'; +import { VIEWS, ROLE } from '@/constants'; vi.mock('@/stores/users.store', () => ({ useUsersStore: vi.fn(), @@ -15,7 +14,7 @@ describe('Middleware', () => { vi.mocked(useUsersStore).mockReturnValue({ currentUser: { isDefaultUser: false, - role: 'global:owner', + role: ROLE.Owner, } as IUser, } as ReturnType); @@ -54,7 +53,7 @@ describe('Middleware', () => { vi.mocked(useUsersStore).mockReturnValue({ currentUser: { isDefaultUser: false, - role: 'global:owner', + role: ROLE.Owner, } as IUser, } as ReturnType); diff --git a/packages/editor-ui/src/stores/__tests__/ui.test.ts b/packages/editor-ui/src/stores/__tests__/ui.test.ts index bbab742c5d2..092d66842a4 100644 --- a/packages/editor-ui/src/stores/__tests__/ui.test.ts +++ b/packages/editor-ui/src/stores/__tests__/ui.test.ts @@ -14,6 +14,7 @@ import { getNotTrialingUserResponse, } from './utils/cloudStoreUtils'; import type { IRole } from '@/Interface'; +import { ROLE } from '@/constants'; let uiStore: ReturnType; let settingsStore: ReturnType; @@ -33,7 +34,7 @@ function setUser(role: IRole) { } function setupOwnerAndCloudDeployment() { - setUser('global:owner'); + setUser(ROLE.Owner); settingsStore.setSettings( merge({}, SETTINGS_STORE_DEFAULT_STATE.settings, { n8nMetadata: { @@ -75,19 +76,19 @@ describe('UI store', () => { [ 'default', 'production', - 'global:owner', + ROLE.Owner, 'https://n8n.io/pricing?utm_campaign=utm-test-campaign&source=test_source', ], [ 'default', 'development', - 'global:owner', + ROLE.Owner, 'https://n8n.io/pricing?utm_campaign=utm-test-campaign&source=test_source', ], [ 'cloud', 'production', - 'global:owner', + ROLE.Owner, `https://app.n8n.cloud/login?code=123&returnPath=${encodeURIComponent( '/account/change-plan', )}&utm_campaign=utm-test-campaign&source=test_source`, @@ -95,7 +96,7 @@ describe('UI store', () => { [ 'cloud', 'production', - 'global:member', + ROLE.Member, 'https://n8n.io/pricing?utm_campaign=utm-test-campaign&source=test_source', ], ])( diff --git a/packages/editor-ui/src/stores/cloudPlan.store.ts b/packages/editor-ui/src/stores/cloudPlan.store.ts index e1dccf32a07..d2201e8e5cb 100644 --- a/packages/editor-ui/src/stores/cloudPlan.store.ts +++ b/packages/editor-ui/src/stores/cloudPlan.store.ts @@ -67,11 +67,9 @@ export const useCloudPlanStore = defineStore(STORES.CLOUD_PLAN, () => { const getUserCloudAccount = async () => { if (!hasCloudPlan.value) throw new Error('User does not have a cloud plan'); try { - if (hasPermission(['instanceOwner'])) { - await usersStore.fetchUserCloudAccount(); - if (!usersStore.currentUserCloudInfo?.confirmed && !userIsTrialing.value) { - useUIStore().pushBannerToStack('EMAIL_CONFIRMATION'); - } + await usersStore.fetchUserCloudAccount(); + if (!usersStore.currentUserCloudInfo?.confirmed && !userIsTrialing.value) { + useUIStore().pushBannerToStack('EMAIL_CONFIRMATION'); } } catch (error) { throw new Error(error.message); diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index e0b11b5c404..d3584fe0739 100644 --- a/packages/editor-ui/src/stores/ui.store.ts +++ b/packages/editor-ui/src/stores/ui.store.ts @@ -343,7 +343,6 @@ export const useUIStore = defineStore(STORES.UI, { let linkUrl = ''; const searchParams = new URLSearchParams(); - const { isInstanceOwner } = useUsersStore(); if (deploymentType === 'cloud' && hasPermission(['instanceOwner'])) { const adminPanelHost = new URL(window.location.href).host.split('.').slice(1).join('.'); diff --git a/packages/editor-ui/src/stores/users.store.ts b/packages/editor-ui/src/stores/users.store.ts index cfdec7bb656..36866e65139 100644 --- a/packages/editor-ui/src/stores/users.store.ts +++ b/packages/editor-ui/src/stores/users.store.ts @@ -18,7 +18,7 @@ import { validateSignupToken, updateGlobalRole, } from '@/api/users'; -import { PERSONALIZATION_MODAL_KEY, STORES } from '@/constants'; +import { PERSONALIZATION_MODAL_KEY, STORES, ROLE } from '@/constants'; import type { Cloud, ICredentialsResponse, @@ -46,7 +46,7 @@ import type { Scope } from '@n8n/permissions'; import { inviteUsers, acceptInvitation } from '@/api/invitation'; const isPendingUser = (user: IUserResponse | null) => !!user?.isPending; -const isInstanceOwner = (user: IUserResponse | null) => user?.role === 'global:owner'; +const isInstanceOwner = (user: IUserResponse | null) => user?.role === ROLE.Owner; const isDefaultUser = (user: IUserResponse | null) => isInstanceOwner(user) && isPendingUser(user); export const useUsersStore = defineStore(STORES.USERS, { @@ -139,7 +139,6 @@ export const useUsersStore = defineStore(STORES.USERS, { : undefined, isDefaultUser: isDefaultUser(updatedUser), isPendingUser: isPendingUser(updatedUser), - isOwner: isInstanceOwner(updatedUser), }; this.users = { diff --git a/packages/editor-ui/src/utils/userUtils.ts b/packages/editor-ui/src/utils/userUtils.ts index ea95dbefb71..5646c004e70 100644 --- a/packages/editor-ui/src/utils/userUtils.ts +++ b/packages/editor-ui/src/utils/userUtils.ts @@ -59,6 +59,7 @@ import { BAMBOO_HR_NODE_TYPE, GOOGLE_SHEETS_NODE_TYPE, CODE_NODE_TYPE, + ROLE, } from '@/constants'; import type { IPersonalizationSurveyAnswersV1, @@ -83,18 +84,13 @@ function isPersonalizationSurveyV2OrLater( return 'version' in data; } -export const ROLE = { - Owner: 'global:owner', - Member: 'global:member', - Admin: 'global:admin', - Default: 'default', // default user with no email when setting up instance -} as const; - export const LOGIN_STATUS: { LoggedIn: ILogInStatus; LoggedOut: ILogInStatus } = { LoggedIn: 'LoggedIn', // Can be owner or member or default user LoggedOut: 'LoggedOut', // Can only be logged out if UM has been setup }; +export const isUserGlobalOwner = (user: IUser): boolean => user.role === ROLE.Owner; + export function getPersonalizedNodeTypes( answers: | IPersonalizationSurveyAnswersV1 diff --git a/packages/editor-ui/src/views/SettingsUsageAndPlan.vue b/packages/editor-ui/src/views/SettingsUsageAndPlan.vue index 8f69d39a9d0..2495d1fd295 100644 --- a/packages/editor-ui/src/views/SettingsUsageAndPlan.vue +++ b/packages/editor-ui/src/views/SettingsUsageAndPlan.vue @@ -8,7 +8,6 @@ import { i18n as locale } from '@/plugins/i18n'; import { useUIStore } from '@/stores/ui.store'; import { N8N_PRICING_PAGE_URL } from '@/constants'; import { useToast } from '@/composables/useToast'; -import { ROLE } from '@/utils/userUtils'; import { hasPermission } from '@/rbac/permissions'; const usageStore = useUsageStore(); @@ -29,9 +28,7 @@ const activationKey = ref(''); const activationKeyInput = ref(null); const canUserActivateLicense = computed(() => - hasPermission(['role'], { - role: [ROLE.Owner], - }), + hasPermission(['rbac'], { rbac: { scope: 'license:manage' } }), ); const showActivationSuccess = () => { diff --git a/packages/editor-ui/src/views/SettingsUsersView.vue b/packages/editor-ui/src/views/SettingsUsersView.vue index 34aeaccd520..9245e4f3c21 100644 --- a/packages/editor-ui/src/views/SettingsUsersView.vue +++ b/packages/editor-ui/src/views/SettingsUsersView.vue @@ -87,7 +87,7 @@