fix: Project Viewer always seeing a connection error when testing credentials (#10417)

This commit is contained in:
Val 2024-08-15 09:39:48 +01:00 committed by GitHub
parent 6bb57108a1
commit 613cdd2ba2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 79 additions and 22 deletions

View file

@ -127,11 +127,11 @@ export class CredentialsController {
const mergedCredentials = deepCopy(credentials); const mergedCredentials = deepCopy(credentials);
const decryptedData = this.credentialsService.decrypt(storedCredential); const decryptedData = this.credentialsService.decrypt(storedCredential);
// When a sharee opens a credential, the fields and the credential data are missing // When a sharee (or project viewer) opens a credential, the fields and the
// so the payload will be empty // credential data are missing so the payload will be empty
// We need to replace the credential contents with the db version if that's the case // We need to replace the credential contents with the db version if that's the case
// So the credential can be tested properly // So the credential can be tested properly
this.credentialsService.replaceCredentialContentsForSharee( await this.credentialsService.replaceCredentialContentsForSharee(
req.user, req.user,
storedCredential, storedCredential,
decryptedData, decryptedData,

View file

@ -38,6 +38,7 @@ import { NotFoundError } from '@/errors/response-errors/not-found.error';
import type { ProjectRelation } from '@/databases/entities/ProjectRelation'; import type { ProjectRelation } from '@/databases/entities/ProjectRelation';
import { RoleService } from '@/services/role.service'; import { RoleService } from '@/services/role.service';
import { UserRepository } from '@/databases/repositories/user.repository'; import { UserRepository } from '@/databases/repositories/user.repository';
import { userHasScope } from '@/permissions/checkAccess';
export type CredentialsGetSharedOptions = export type CredentialsGetSharedOptions =
| { allowGlobalScope: true; globalScope: Scope } | { allowGlobalScope: true; globalScope: Scope }
@ -599,28 +600,20 @@ export class CredentialsService {
); );
} }
replaceCredentialContentsForSharee( async replaceCredentialContentsForSharee(
user: User, user: User,
credential: CredentialsEntity, credential: CredentialsEntity,
decryptedData: ICredentialDataDecryptedObject, decryptedData: ICredentialDataDecryptedObject,
mergedCredentials: ICredentialsDecrypted, mergedCredentials: ICredentialsDecrypted,
) { ) {
credential.shared.forEach((sharedCredentials) => { // We may want to change this to 'credential:decrypt' if that gets added, but this
if (sharedCredentials.role === 'credential:owner') { // works for now. The only time we wouldn't want to do this is if the user
if (sharedCredentials.project.type === 'personal') { // could actually be testing the credential before saving it, so this should cover
// Find the owner of this personal project // the cases we need it for.
sharedCredentials.project.projectRelations.forEach((projectRelation) => { if (
if ( !(await userHasScope(user, ['credential:update'], false, { credentialId: credential.id }))
projectRelation.role === 'project:personalOwner' && ) {
projectRelation.user.id !== user.id mergedCredentials.data = decryptedData;
) { }
// If we realize that the current user does not own this credential
// We replace the payload with the stored decrypted data
mergedCredentials.data = decryptedData;
}
});
}
}
});
} }
} }

View file

@ -7,6 +7,7 @@ import { SharedCredentialsRepository } from '@/databases/repositories/sharedCred
import Container from 'typedi'; import Container from 'typedi';
import { CredentialsService } from '@/credentials/credentials.service'; import { CredentialsService } from '@/credentials/credentials.service';
import * as testDb from '../shared/testDb'; import * as testDb from '../shared/testDb';
import { createTeamProject, linkUserToProject } from '@test-integration/db/projects';
const credentialPayload = randomCredentialPayload(); const credentialPayload = randomCredentialPayload();
let memberWhoOwnsCredential: User; let memberWhoOwnsCredential: User;
@ -42,7 +43,7 @@ describe('credentials service', () => {
data: { accessToken: '' }, data: { accessToken: '' },
}; };
Container.get(CredentialsService).replaceCredentialContentsForSharee( await Container.get(CredentialsService).replaceCredentialContentsForSharee(
memberWhoDoesNotOwnCredential, memberWhoDoesNotOwnCredential,
storedCredential!, storedCredential!,
decryptedData, decryptedData,
@ -51,5 +52,68 @@ describe('credentials service', () => {
expect(mergedCredentials.data).toEqual({ accessToken: credentialPayload.data.accessToken }); expect(mergedCredentials.data).toEqual({ accessToken: credentialPayload.data.accessToken });
}); });
it('should replace the contents of the credential for project viewer', async () => {
const [project, viewerMember] = await Promise.all([createTeamProject(), createMember()]);
await linkUserToProject(viewerMember, project, 'project:viewer');
const projectCredential = await saveCredential(credentialPayload, {
project,
role: 'credential:owner',
});
const storedProjectCredential = await Container.get(
SharedCredentialsRepository,
).findCredentialForUser(projectCredential.id, viewerMember, ['credential:read']);
const decryptedData = Container.get(CredentialsService).decrypt(storedProjectCredential!);
const mergedCredentials = {
id: projectCredential.id,
name: projectCredential.name,
type: projectCredential.type,
data: { accessToken: '' },
};
await Container.get(CredentialsService).replaceCredentialContentsForSharee(
viewerMember,
storedProjectCredential!,
decryptedData,
mergedCredentials,
);
expect(mergedCredentials.data).toEqual({ accessToken: credentialPayload.data.accessToken });
});
it('should not replace the contents of the credential for project editor', async () => {
const [project, editorMember] = await Promise.all([createTeamProject(), createMember()]);
await linkUserToProject(editorMember, project, 'project:editor');
const projectCredential = await saveCredential(credentialPayload, {
project,
role: 'credential:owner',
});
const storedProjectCredential = await Container.get(
SharedCredentialsRepository,
).findCredentialForUser(projectCredential.id, editorMember, ['credential:read']);
const decryptedData = Container.get(CredentialsService).decrypt(storedProjectCredential!);
const originalData = { accessToken: '' };
const mergedCredentials = {
id: projectCredential.id,
name: projectCredential.name,
type: projectCredential.type,
data: originalData,
};
await Container.get(CredentialsService).replaceCredentialContentsForSharee(
editorMember,
storedProjectCredential!,
decryptedData,
mergedCredentials,
);
expect(mergedCredentials.data).toBe(originalData);
});
}); });
}); });