fix(core): Better input validation for the changeRole endpoint (#8189)

also refactored the code to
1. stop passing around `scope === 'global'`, since this code can be used
only for changing globalRole.
2. leak less details when input validation fails.

## Review / Merge checklist
- [x] PR title and summary are descriptive
- [x] Tests included
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2024-01-03 09:33:35 +01:00
committed by GitHub
parent 11cda41214
commit cfe9525dd4
7 changed files with 102 additions and 160 deletions

View File

@@ -7,7 +7,6 @@ import type {
} from '@/Interface';
import type { IDataObject } from 'n8n-workflow';
import { makeRestApiRequest } from '@/utils/apiUtils';
import type { ScopeLevel } from '@n8n/permissions';
export async function loginCurrentUser(
context: IRestApiContext,
@@ -146,9 +145,14 @@ export async function submitPersonalizationSurvey(
await makeRestApiRequest(context, 'POST', '/me/survey', params as unknown as IDataObject);
}
export async function updateRole(
context: IRestApiContext,
{ id, role }: { id: string; role: { scope: ScopeLevel; name: IRole } },
): Promise<IUserResponse> {
return makeRestApiRequest(context, 'PATCH', `/users/${id}/role`, { newRole: role });
export interface UpdateGlobalRolePayload {
id: string;
newRoleName: Exclude<IRole, 'default' | 'owner'>;
}
export async function updateGlobalRole(
context: IRestApiContext,
{ id, newRoleName }: UpdateGlobalRolePayload,
): Promise<IUserResponse> {
return makeRestApiRequest(context, 'PATCH', `/users/${id}/role`, { newRoleName });
}

View File

@@ -1,3 +1,4 @@
import type { UpdateGlobalRolePayload } from '@/api/users';
import {
changePassword,
deleteUser,
@@ -15,7 +16,7 @@ import {
updateOtherUserSettings,
validatePasswordToken,
validateSignupToken,
updateRole,
updateGlobalRole,
} from '@/api/users';
import { PERSONALIZATION_MODAL_KEY, STORES } from '@/constants';
import type {
@@ -40,7 +41,7 @@ import { useCloudPlanStore } from './cloudPlan.store';
import { disableMfa, enableMfa, getMfaQR, verifyMfaToken } from '@/api/mfa';
import { confirmEmail, getCloudUserInfo } from '@/api/cloudPlans';
import { useRBACStore } from '@/stores/rbac.store';
import type { Scope, ScopeLevel } from '@n8n/permissions';
import type { Scope } from '@n8n/permissions';
import { inviteUsers, acceptInvitation } from '@/api/invitation';
const isPendingUser = (user: IUserResponse | null) => !!user?.isPending;
@@ -379,9 +380,9 @@ export const useUsersStore = defineStore(STORES.USERS, {
await confirmEmail(useRootStore().getRestApiContext);
},
async updateRole({ id, role }: { id: string; role: { scope: ScopeLevel; name: IRole } }) {
async updateGlobalRole({ id, newRoleName }: UpdateGlobalRolePayload) {
const rootStore = useRootStore();
await updateRole(rootStore.getRestApiContext, { id, role });
await updateGlobalRole(rootStore.getRestApiContext, { id, newRoleName });
await this.fetchUsers();
},
},

View File

@@ -99,6 +99,7 @@ import { useSSOStore } from '@/stores/sso.store';
import { hasPermission } from '@/rbac/permissions';
import { ROLE } from '@/utils/userUtils';
import { useClipboard } from '@/composables/useClipboard';
import type { UpdateGlobalRolePayload } from '@/api/users';
export default defineComponent({
name: 'SettingsUsersView',
@@ -280,8 +281,8 @@ export default defineComponent({
goToUpgradeAdvancedPermissions() {
void this.uiStore.goToUpgrade('settings-users', 'upgrade-advanced-permissions');
},
async onRoleChange(user: IUser, name: IRole) {
await this.usersStore.updateRole({ id: user.id, role: { scope: 'global', name } });
async onRoleChange(user: IUser, newRoleName: UpdateGlobalRolePayload['newRoleName']) {
await this.usersStore.updateGlobalRole({ id: user.id, newRoleName });
},
},
});