mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
feat: Allow instance owners and admins to edit all credentials (#8716)
Co-authored-by: Danny Martini <despair.blue@gmail.com>
This commit is contained in:
parent
27f3166272
commit
737170893d
|
@ -74,7 +74,9 @@ export class CredentialsController {
|
|||
|
||||
credential = this.ownershipService.addOwnedByAndSharedWith(credential);
|
||||
|
||||
if (!includeDecryptedData || !userSharing || userSharing.role !== 'credential:owner') {
|
||||
// Below, if `userSharing` does not exist, it means this credential is being
|
||||
// fetched by the instance owner or an admin. In this case, they get the full data
|
||||
if (!includeDecryptedData || userSharing?.role === 'credential:user') {
|
||||
const { data: _, ...rest } = credential;
|
||||
return { ...rest };
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ export const ownerPermissions: Scope[] = [
|
|||
'auditLogs:manage',
|
||||
'credential:create',
|
||||
'credential:read',
|
||||
'credential:update',
|
||||
'credential:delete',
|
||||
'credential:list',
|
||||
'credential:share',
|
||||
|
|
|
@ -229,7 +229,7 @@ describe('GET /credentials/:id', () => {
|
|||
expect(response2.statusCode).toBe(200);
|
||||
|
||||
validateMainCredentialData(response2.body.data);
|
||||
expect(response2.body.data.data).toBeUndefined();
|
||||
expect(response2.body.data.data).toBeDefined(); // Instance owners should be capable of editing all credentials
|
||||
expect(response2.body.data.sharedWith).toHaveLength(1);
|
||||
});
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import type { SaveCredentialFunction } from './shared/types';
|
|||
import * as utils from './shared/utils/';
|
||||
import { affixRoleToSaveCredential, shareCredentialWithUsers } from './shared/db/credentials';
|
||||
import { createManyUsers, createUser } from './shared/db/users';
|
||||
import { Credentials } from 'n8n-core';
|
||||
|
||||
// mock that credentialsSharing is not enabled
|
||||
jest.spyOn(License.prototype, 'isSharingEnabled').mockReturnValue(false);
|
||||
|
@ -283,7 +284,7 @@ describe('PATCH /credentials/:id', () => {
|
|||
expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated
|
||||
});
|
||||
|
||||
test('should not update non-owned cred for owner', async () => {
|
||||
test('should update non-owned cred for owner', async () => {
|
||||
const savedCredential = await saveCredential(randomCredentialPayload(), { user: member });
|
||||
const patchPayload = randomCredentialPayload();
|
||||
|
||||
|
@ -291,22 +292,29 @@ describe('PATCH /credentials/:id', () => {
|
|||
.patch(`/credentials/${savedCredential.id}`)
|
||||
.send(patchPayload);
|
||||
|
||||
expect(response.statusCode).toBe(404);
|
||||
expect(response.statusCode).toBe(200);
|
||||
|
||||
const credential = await Container.get(CredentialsRepository).findOneByOrFail({
|
||||
id: savedCredential.id,
|
||||
});
|
||||
|
||||
expect(credential.name).not.toBe(patchPayload.name);
|
||||
expect(credential.type).not.toBe(patchPayload.type);
|
||||
expect(credential.data).not.toBe(patchPayload.data);
|
||||
expect(credential.name).toBe(patchPayload.name);
|
||||
expect(credential.type).toBe(patchPayload.type);
|
||||
|
||||
const credentialObject = new Credentials(
|
||||
{ id: credential.id, name: credential.name },
|
||||
credential.type,
|
||||
credential.nodesAccess,
|
||||
credential.data,
|
||||
);
|
||||
expect(credentialObject.getData()).toStrictEqual(patchPayload.data);
|
||||
|
||||
const sharedCredential = await Container.get(SharedCredentialsRepository).findOneOrFail({
|
||||
relations: ['credentials'],
|
||||
where: { credentialsId: credential.id },
|
||||
});
|
||||
|
||||
expect(sharedCredential.credentials.name).not.toBe(patchPayload.name); // updated
|
||||
expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated
|
||||
});
|
||||
|
||||
test('should update owned cred for member', async () => {
|
||||
|
@ -381,7 +389,7 @@ describe('PATCH /credentials/:id', () => {
|
|||
expect(shellCredential.name).not.toBe(patchPayload.name); // not updated
|
||||
});
|
||||
|
||||
test('should not update non-owned but shared cred for instance owner', async () => {
|
||||
test('should update non-owned but shared cred for instance owner', async () => {
|
||||
const savedCredential = await saveCredential(randomCredentialPayload(), { user: secondMember });
|
||||
await shareCredentialWithUsers(savedCredential, [owner]);
|
||||
const patchPayload = randomCredentialPayload();
|
||||
|
@ -390,13 +398,13 @@ describe('PATCH /credentials/:id', () => {
|
|||
.patch(`/credentials/${savedCredential.id}`)
|
||||
.send(patchPayload);
|
||||
|
||||
expect(response.statusCode).toBe(404);
|
||||
expect(response.statusCode).toBe(200);
|
||||
|
||||
const shellCredential = await Container.get(CredentialsRepository).findOneByOrFail({
|
||||
id: savedCredential.id,
|
||||
});
|
||||
|
||||
expect(shellCredential.name).not.toBe(patchPayload.name); // not updated
|
||||
expect(shellCredential.name).toBe(patchPayload.name); // updated
|
||||
});
|
||||
|
||||
test('should fail with invalid inputs', async () => {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
:message="
|
||||
$locale.baseText(
|
||||
`credentialEdit.credentialConfig.pleaseCheckTheErrorsBelow${
|
||||
credentialPermissions.isOwner ? '' : '.sharee'
|
||||
credentialPermissions.update || credentialPermissions.isOwner ? '' : '.sharee'
|
||||
}`,
|
||||
{ interpolate: { owner: credentialOwnerName } },
|
||||
)
|
||||
|
@ -19,7 +19,7 @@
|
|||
:message="
|
||||
$locale.baseText(
|
||||
`credentialEdit.credentialConfig.couldntConnectWithTheseSettings${
|
||||
credentialPermissions.isOwner ? '' : '.sharee'
|
||||
credentialPermissions.update || credentialPermissions.isOwner ? '' : '.sharee'
|
||||
}`,
|
||||
{ interpolate: { owner: credentialOwnerName } },
|
||||
)
|
||||
|
|
|
@ -89,7 +89,8 @@ export const getCredentialPermissions = (user: IUser | null, credential: ICreden
|
|||
},
|
||||
{
|
||||
name: 'update',
|
||||
test: (permissions) => !!permissions.isOwner,
|
||||
test: (permissions) =>
|
||||
hasPermission(['rbac'], { rbac: { scope: 'credential:update' } }) || !!permissions.isOwner,
|
||||
},
|
||||
{
|
||||
name: 'share',
|
||||
|
|
Loading…
Reference in a new issue