mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
fix: Fix user reinvites on FE and BE (#8261)
This commit is contained in:
parent
b1c1372bc2
commit
0dabe5c74e
|
@ -270,7 +270,7 @@ export class UserService {
|
|||
const usersInvited = await this.sendEmails(
|
||||
owner,
|
||||
Object.fromEntries(createdUsers),
|
||||
toCreateUsers[0].role, // same role for all invited users
|
||||
attributes[0].role, // same role for all invited users
|
||||
);
|
||||
|
||||
return { usersInvited, usersCreated: toCreateUsers.map(({ email }) => email) };
|
||||
|
|
|
@ -346,6 +346,29 @@ describe('POST /invitations', () => {
|
|||
assertInvitedUsersOnDb(storedUser);
|
||||
});
|
||||
|
||||
test('should reinvite member', async () => {
|
||||
mailer.invite.mockResolvedValue({ emailSent: false });
|
||||
|
||||
await ownerAgent.post('/invitations').send([{ email: randomEmail(), role: 'member' }]);
|
||||
|
||||
await ownerAgent
|
||||
.post('/invitations')
|
||||
.send([{ email: randomEmail(), role: 'member' }])
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
test('should reinvite admin if licensed', async () => {
|
||||
license.isAdvancedPermissionsLicensed.mockReturnValue(true);
|
||||
mailer.invite.mockResolvedValue({ emailSent: false });
|
||||
|
||||
await ownerAgent.post('/invitations').send([{ email: randomEmail(), role: 'admin' }]);
|
||||
|
||||
await ownerAgent
|
||||
.post('/invitations')
|
||||
.send([{ email: randomEmail(), role: 'admin' }])
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
test('should fail to create admin shell if not licensed', async () => {
|
||||
license.isAdvancedPermissionsLicensed.mockReturnValue(false);
|
||||
mailer.invite.mockResolvedValue({ emailSent: false });
|
||||
|
|
|
@ -687,6 +687,8 @@ export type IPersonalizationSurveyVersions =
|
|||
|
||||
export type IRole = 'default' | 'owner' | 'member' | 'admin';
|
||||
|
||||
export type InvitableRoleName = 'member' | 'admin';
|
||||
|
||||
export interface IUserResponse {
|
||||
id: string;
|
||||
firstName?: string;
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import type { CurrentUserResponse, IInviteResponse, IRestApiContext, IRole } from '@/Interface';
|
||||
import type {
|
||||
CurrentUserResponse,
|
||||
IInviteResponse,
|
||||
IRestApiContext,
|
||||
InvitableRoleName,
|
||||
} from '@/Interface';
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
import { makeRestApiRequest } from '@/utils/apiUtils';
|
||||
|
||||
|
@ -12,7 +17,7 @@ type AcceptInvitationParams = {
|
|||
|
||||
export async function inviteUsers(
|
||||
context: IRestApiContext,
|
||||
params: Array<{ email: string; role: IRole }>,
|
||||
params: Array<{ email: string; role: InvitableRoleName }>,
|
||||
) {
|
||||
return makeRestApiRequest<IInviteResponse[]>(context, 'POST', '/invitations', params);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import type {
|
|||
IUserResponse,
|
||||
IUsersState,
|
||||
CurrentUserResponse,
|
||||
InvitableRoleName,
|
||||
} from '@/Interface';
|
||||
import { getCredentialPermissions } from '@/permissions';
|
||||
import { getPersonalizedNodeTypes, ROLE } from '@/utils/userUtils';
|
||||
|
@ -302,7 +303,9 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
|||
const users = await getUsers(rootStore.getRestApiContext);
|
||||
this.addUsers(users);
|
||||
},
|
||||
async inviteUsers(params: Array<{ email: string; role: IRole }>): Promise<IInviteResponse[]> {
|
||||
async inviteUsers(
|
||||
params: Array<{ email: string; role: InvitableRoleName }>,
|
||||
): Promise<IInviteResponse[]> {
|
||||
const rootStore = useRootStore();
|
||||
const users = await inviteUsers(rootStore.getRestApiContext, params);
|
||||
this.addUsers(
|
||||
|
@ -314,11 +317,9 @@ export const useUsersStore = defineStore(STORES.USERS, {
|
|||
);
|
||||
return users;
|
||||
},
|
||||
async reinviteUser(params: { email: string }): Promise<void> {
|
||||
async reinviteUser({ email, role }: { email: string; role: InvitableRoleName }): Promise<void> {
|
||||
const rootStore = useRootStore();
|
||||
const invitationResponse = await inviteUsers(rootStore.getRestApiContext, [
|
||||
{ email: params.email },
|
||||
]);
|
||||
const invitationResponse = await inviteUsers(rootStore.getRestApiContext, [{ email, role }]);
|
||||
if (!invitationResponse[0].user.emailSent) {
|
||||
throw Error(invitationResponse[0].error);
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ export async function request(config: {
|
|||
baseURL: string;
|
||||
endpoint: string;
|
||||
headers?: IDataObject;
|
||||
data?: IDataObject;
|
||||
data?: IDataObject | IDataObject[];
|
||||
withCredentials?: boolean;
|
||||
}) {
|
||||
const { method, baseURL, endpoint, headers, data } = config;
|
||||
|
@ -119,7 +119,7 @@ export async function makeRestApiRequest<T>(
|
|||
context: IRestApiContext,
|
||||
method: Method,
|
||||
endpoint: string,
|
||||
data?: IDataObject,
|
||||
data?: IDataObject | IDataObject[],
|
||||
) {
|
||||
const response = await request({
|
||||
method,
|
||||
|
|
|
@ -89,7 +89,7 @@ import { defineComponent } from 'vue';
|
|||
import { mapStores } from 'pinia';
|
||||
import { EnterpriseEditionFeature, INVITE_USER_MODAL_KEY, VIEWS } from '@/constants';
|
||||
|
||||
import type { IUser, IUserListAction } from '@/Interface';
|
||||
import type { IUser, IUserListAction, InvitableRoleName } from '@/Interface';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
|
@ -207,9 +207,15 @@ export default defineComponent({
|
|||
},
|
||||
async onReinvite(userId: string) {
|
||||
const user = this.usersStore.getUserById(userId);
|
||||
if (user?.email) {
|
||||
if (user?.email && user?.globalRole) {
|
||||
if (!['admin', 'member'].includes(user.globalRole.name)) {
|
||||
throw new Error('Invalid role name on reinvite');
|
||||
}
|
||||
try {
|
||||
await this.usersStore.reinviteUser({ email: user.email });
|
||||
await this.usersStore.reinviteUser({
|
||||
email: user.email,
|
||||
role: user.globalRole.name as InvitableRoleName,
|
||||
});
|
||||
this.showToast({
|
||||
type: 'success',
|
||||
title: this.$locale.baseText('settings.users.inviteResent'),
|
||||
|
|
Loading…
Reference in a new issue