feat: RBAC (#8922)
Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Val <68596159+valya@users.noreply.github.com> Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in> Co-authored-by: Valya Bullions <valya@n8n.io> Co-authored-by: Danny Martini <danny@n8n.io> Co-authored-by: Danny Martini <despair.blue@gmail.com> Co-authored-by: Iván Ovejero <ivov.src@gmail.com> Co-authored-by: Omar Ajoue <krynble@gmail.com> Co-authored-by: oleg <me@olegivaniv.com> Co-authored-by: Michael Kret <michael.k@radency.com> Co-authored-by: Michael Kret <88898367+michael-radency@users.noreply.github.com> Co-authored-by: Elias Meire <elias@meire.dev> Co-authored-by: Giulio Andreini <andreini@netseven.it> Co-authored-by: Giulio Andreini <g.andreini@gmail.com> Co-authored-by: Ayato Hayashi <go12limchangyong@gmail.com>
This commit is contained in:
@@ -30,15 +30,15 @@ import { ICredentialsHelper, NodeHelpers, Workflow, ApplicationError } from 'n8n
|
||||
import type { ICredentialsDb } from '@/Interfaces';
|
||||
|
||||
import type { CredentialsEntity } from '@db/entities/CredentialsEntity';
|
||||
import { NodeTypes } from '@/NodeTypes';
|
||||
import { CredentialTypes } from '@/CredentialTypes';
|
||||
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
|
||||
import { RESPONSE_ERROR_MESSAGES } from './constants';
|
||||
|
||||
import { Logger } from '@/Logger';
|
||||
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
||||
import { CredentialNotFoundError } from './errors/credential-not-found.error';
|
||||
import { In } from '@n8n/typeorm';
|
||||
import { CacheService } from './services/cache/cache.service';
|
||||
|
||||
const mockNode = {
|
||||
name: '',
|
||||
@@ -77,12 +77,11 @@ const mockNodeTypes: INodeTypes = {
|
||||
@Service()
|
||||
export class CredentialsHelper extends ICredentialsHelper {
|
||||
constructor(
|
||||
private readonly logger: Logger,
|
||||
private readonly credentialTypes: CredentialTypes,
|
||||
private readonly nodeTypes: NodeTypes,
|
||||
private readonly credentialsOverwrites: CredentialsOverwrites,
|
||||
private readonly credentialsRepository: CredentialsRepository,
|
||||
private readonly sharedCredentialsRepository: SharedCredentialsRepository,
|
||||
private readonly cacheService: CacheService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -245,7 +244,6 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||
async getCredentials(
|
||||
nodeCredential: INodeCredentialsDetails,
|
||||
type: string,
|
||||
userId?: string,
|
||||
): Promise<Credentials> {
|
||||
if (!nodeCredential.id) {
|
||||
throw new ApplicationError('Found credential with no ID.', {
|
||||
@@ -257,14 +255,10 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||
let credential: CredentialsEntity;
|
||||
|
||||
try {
|
||||
credential = userId
|
||||
? await this.sharedCredentialsRepository
|
||||
.findOneOrFail({
|
||||
relations: ['credentials'],
|
||||
where: { credentials: { id: nodeCredential.id, type }, userId },
|
||||
})
|
||||
.then((shared) => shared.credentials)
|
||||
: await this.credentialsRepository.findOneByOrFail({ id: nodeCredential.id, type });
|
||||
credential = await this.credentialsRepository.findOneByOrFail({
|
||||
id: nodeCredential.id,
|
||||
type,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new CredentialNotFoundError(nodeCredential.id, type);
|
||||
}
|
||||
@@ -338,7 +332,7 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||
|
||||
await additionalData?.secretsHelpers?.waitForInit();
|
||||
|
||||
const canUseSecrets = await this.credentialOwnedByOwner(nodeCredentials);
|
||||
const canUseSecrets = await this.credentialCanUseExternalSecrets(nodeCredentials);
|
||||
|
||||
return this.applyDefaultsAndOverwrites(
|
||||
additionalData,
|
||||
@@ -457,28 +451,39 @@ export class CredentialsHelper extends ICredentialsHelper {
|
||||
await this.credentialsRepository.update(findQuery, newCredentialsData);
|
||||
}
|
||||
|
||||
async credentialOwnedByOwner(nodeCredential: INodeCredentialsDetails): Promise<boolean> {
|
||||
async credentialCanUseExternalSecrets(nodeCredential: INodeCredentialsDetails): Promise<boolean> {
|
||||
if (!nodeCredential.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const credential = await this.sharedCredentialsRepository.findOne({
|
||||
where: {
|
||||
role: 'credential:owner',
|
||||
user: {
|
||||
role: 'global:owner',
|
||||
},
|
||||
credentials: {
|
||||
id: nodeCredential.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
return (
|
||||
(await this.cacheService.get(`credential-can-use-secrets:${nodeCredential.id}`, {
|
||||
refreshFn: async () => {
|
||||
const credential = await this.sharedCredentialsRepository.findOne({
|
||||
where: {
|
||||
role: 'credential:owner',
|
||||
project: {
|
||||
projectRelations: {
|
||||
role: In(['project:personalOwner', 'project:admin']),
|
||||
user: {
|
||||
role: In(['global:owner', 'global:admin']),
|
||||
},
|
||||
},
|
||||
},
|
||||
credentials: {
|
||||
id: nodeCredential.id!,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!credential) {
|
||||
return false;
|
||||
}
|
||||
if (!credential) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
},
|
||||
})) ?? false
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user