feat(core): Add scopes to API Keys (#14176)

Co-authored-by: Charlie Kolb <charlie@n8n.io>
Co-authored-by: Danny Martini <danny@n8n.io>
This commit is contained in:
Ricardo Espinoza
2025-04-16 09:03:16 -04:00
committed by GitHub
parent bc12f662e7
commit e1b9407fe9
65 changed files with 3216 additions and 125 deletions

View File

@@ -1,17 +1,24 @@
import { Container } from '@n8n/di';
import type { ApiKeyScope, GlobalRole } from '@n8n/permissions';
import { hash } from 'bcryptjs';
import { AuthIdentity } from '@/databases/entities/auth-identity';
import { type GlobalRole, type User } from '@/databases/entities/user';
import { type User } from '@/databases/entities/user';
import { AuthIdentityRepository } from '@/databases/repositories/auth-identity.repository';
import { AuthUserRepository } from '@/databases/repositories/auth-user.repository';
import { UserRepository } from '@/databases/repositories/user.repository';
import { MfaService } from '@/mfa/mfa.service';
import { TOTPService } from '@/mfa/totp.service';
import { getApiKeyScopesForRole } from '@/public-api/permissions.ee';
import { PublicApiKeyService } from '@/services/public-api-key.service';
import { randomEmail, randomName, randomValidPassword } from '../random';
type ApiKeyOptions = {
expiresAt?: number | null;
scopes?: ApiKeyScope[];
};
// pre-computed bcrypt hash for the string 'password', using `await hash('password', 10)`
const passwordHash = '$2a$10$njedH7S6V5898mj6p0Jr..IGY9Ms.qNwR7RbSzzX9yubJocKfvGGK';
@@ -82,28 +89,35 @@ export async function createUserWithMfaEnabled(
export const addApiKey = async (
user: User,
{ expiresAt = null }: { expiresAt?: number | null } = {},
{ expiresAt = null, scopes = [] }: { expiresAt?: number | null; scopes?: ApiKeyScope[] } = {},
) => {
return await Container.get(PublicApiKeyService).createPublicApiKeyForUser(user, {
label: randomName(),
expiresAt,
scopes: scopes.length ? scopes : getApiKeyScopesForRole(user.role),
});
};
export async function createOwnerWithApiKey({
expiresAt = null,
}: { expiresAt?: number | null } = {}) {
export async function createOwnerWithApiKey({ expiresAt = null, scopes = [] }: ApiKeyOptions = {}) {
const owner = await createOwner();
const apiKey = await addApiKey(owner, { expiresAt });
const apiKey = await addApiKey(owner, { expiresAt, scopes });
owner.apiKeys = [apiKey];
return owner;
}
export async function createMemberWithApiKey({
expiresAt = null,
}: { expiresAt?: number | null } = {}) {
scopes = [],
}: ApiKeyOptions = {}) {
const member = await createMember();
const apiKey = await addApiKey(member, { expiresAt });
const apiKey = await addApiKey(member, { expiresAt, scopes });
member.apiKeys = [apiKey];
return member;
}
export async function createAdminWithApiKey({ expiresAt = null, scopes = [] }: ApiKeyOptions = {}) {
const member = await createAdmin();
const apiKey = await addApiKey(member, { expiresAt, scopes });
member.apiKeys = [apiKey];
return member;
}