-
-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
18 changed files
with
412 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Module } from '@nestjs/common' | ||
import { ApiKeyController } from './controller/api-key.controller' | ||
import { ApiKeyService } from './service/api-key.service' | ||
|
||
@Module({ | ||
controllers: [ApiKeyController], | ||
providers: [ApiKeyService] | ||
}) | ||
export class ApiKeyModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { | ||
ApiKey, | ||
ApiKeyProjectRole, | ||
Project, | ||
ProjectScope | ||
} from '@prisma/client' | ||
|
||
export interface Scope { | ||
projectId: Project['id'] | ||
roles: ApiKeyProjectRole[] | ||
} | ||
|
||
export interface ApiKeyWithProjectScopes extends Partial<ApiKey> { | ||
projectScopes: ProjectScope[] | ||
} |
18 changes: 18 additions & 0 deletions
18
apps/api/src/api-key/controller/api-key.controller.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { ApiKeyController } from './api-key.controller'; | ||
|
||
describe('ApiKeyController', () => { | ||
let controller: ApiKeyController; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [ApiKeyController], | ||
}).compile(); | ||
|
||
controller = module.get<ApiKeyController>(ApiKeyController); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(controller).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { Controller } from '@nestjs/common'; | ||
|
||
@Controller('api-key') | ||
export class ApiKeyController {} |
7 changes: 7 additions & 0 deletions
7
apps/api/src/api-key/dto/create.api-key/create.api-key.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { CreateApiKey } from './create.api-key'; | ||
|
||
describe('CreateApiKey', () => { | ||
it('should be defined', () => { | ||
expect(new CreateApiKey()).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { ApiKey, ApiKeyGeneralRole, ProjectScope } from '@prisma/client' | ||
import { IsString } from 'class-validator' | ||
|
||
export class CreateApiKey { | ||
@IsString() | ||
name: ApiKey['name'] | ||
|
||
@IsString() | ||
expiresAfter: '1d' | '7d' | '30d' | '90d' | '365d' | 'never' | ||
|
||
generalRoles: ApiKeyGeneralRole[] | ||
|
||
scopes: ProjectScope[] | ||
} |
7 changes: 7 additions & 0 deletions
7
apps/api/src/api-key/dto/update.api-key/update.api-key.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { UpdateApiKey } from './update.api-key'; | ||
|
||
describe('UpdateApiKey', () => { | ||
it('should be defined', () => { | ||
expect(new UpdateApiKey()).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { PartialType } from '@nestjs/swagger' | ||
import { CreateApiKey } from '../create.api-key/create.api-key' | ||
import { IsOptional } from 'class-validator' | ||
import { ProjectScope } from '@prisma/client' | ||
|
||
export class UpdateApiKey extends PartialType(CreateApiKey) { | ||
@IsOptional() | ||
projectToAdd: ProjectScope[] | ||
|
||
@IsOptional() | ||
projectToRemove: ProjectScope['id'][] | ||
|
||
@IsOptional() | ||
projectToUpdate: ProjectScope[] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { ApiKey, ApiKeyProjectRole, User } from '@prisma/client' | ||
import { PrismaService } from '../../prisma/prisma.service' | ||
import { IApiKeyRepository } from './interface.repository' | ||
import { ApiKeyWithProjectScopes } from '../api-key.types' | ||
|
||
export class ApiKeyRepository implements IApiKeyRepository { | ||
constructor(private readonly prisma: PrismaService) {} | ||
|
||
async createApiKey(user: User, apiKey: Partial<ApiKey>) { | ||
return this.prisma.apiKey.create({ | ||
data: { | ||
name: apiKey.name, | ||
value: apiKey.value, | ||
expiresAt: apiKey.expiresAt, | ||
generalRoles: apiKey.generalRoles, | ||
user: { | ||
connect: { | ||
id: user.id | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
|
||
async updateApiKey(apiKeyId: ApiKey['id'], apiKey: ApiKeyWithProjectScopes) { | ||
return this.prisma.apiKey.update({ | ||
where: { | ||
id: apiKeyId | ||
}, | ||
data: { | ||
name: apiKey.name, | ||
expiresAt: apiKey.expiresAt, | ||
generalRoles: apiKey.generalRoles, | ||
projectScopes: { | ||
deleteMany: { | ||
projectId: { | ||
in: apiKey.projectScopes.map((scope) => scope.projectId) | ||
} | ||
}, | ||
createMany: { | ||
data: apiKey.projectScopes | ||
} | ||
} | ||
} | ||
}) | ||
} | ||
|
||
async updateRolesOfProjectScope( | ||
userId: string, | ||
projectId: string, | ||
roles: ApiKeyProjectRole[] | ||
): Promise<void> { | ||
if (roles.length === 0) { | ||
await this.prisma.projectScope.deleteMany({ | ||
where: { | ||
projectId, | ||
apiKey: { | ||
userId | ||
} | ||
} | ||
}) | ||
} else { | ||
await this.prisma.projectScope.updateMany({ | ||
where: { | ||
projectId, | ||
apiKey: { | ||
userId | ||
} | ||
}, | ||
data: { | ||
roles | ||
} | ||
}) | ||
} | ||
} | ||
|
||
async deleteApiKey(apiKeyId: ApiKey['id']) { | ||
await this.prisma.apiKey.delete({ | ||
where: { | ||
id: apiKeyId | ||
} | ||
}) | ||
} | ||
|
||
async findApiKeyByValue(apiKeyValue: ApiKey['value']) { | ||
return this.prisma.apiKey.findUnique({ | ||
where: { | ||
value: apiKeyValue | ||
}, | ||
include: { | ||
user: true | ||
} | ||
}) | ||
} | ||
|
||
async findApiKeyByIdAndUserId(apiKeyId: ApiKey['id'], userId: User['id']) { | ||
return this.prisma.apiKey.findUnique({ | ||
where: { | ||
id: apiKeyId, | ||
userId | ||
}, | ||
include: { | ||
projectScopes: true | ||
} | ||
}) | ||
} | ||
|
||
async findAllApiKeysByUserId(userId: User['id']) { | ||
return this.prisma.apiKey.findMany({ | ||
where: { | ||
userId | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { ApiKey, ApiKeyProjectRole, ProjectScope, User } from '@prisma/client' | ||
import { ApiKeyWithProjectScopes } from '../api-key.types' | ||
|
||
export const API_KEY_REPOSITORY = 'API_KEY_REPOSITORY' | ||
|
||
/** | ||
* Interface for the ApiKey Repository. | ||
*/ | ||
export interface IApiKeyRepository { | ||
/** | ||
* Creates a new API key for a user. | ||
* @param {User} user - The user for whom the API key is created. | ||
* @param {Partial<ApiKey>} apiKey - The data for the new API key. | ||
* @returns {Promise<ApiKey>} - A promise that resolves to the created API key. | ||
*/ | ||
createApiKey(user: User, apiKey: Partial<ApiKey>): Promise<ApiKey> | ||
|
||
/** | ||
* Updates an existing API key. | ||
* @param {ApiKey['id']} apiKeyId - The ID of the API key to update. | ||
* @param {ApiKeyWithProjectScopes} apiKey - The updated API key data. | ||
* @returns {Promise<ApiKey>} - A promise that resolves to the updated API key. | ||
*/ | ||
updateApiKey( | ||
apiKeyId: ApiKey['id'], | ||
apiKey: ApiKeyWithProjectScopes | ||
): Promise<ApiKey> | ||
|
||
/** | ||
* Updates the roles of a project scope. If the roles array is empty, the project scope is deleted. | ||
* @param {User['id']} userId - The ID of the user. | ||
* @param {ProjectScope['projectId']} projectId - The ID of the project. | ||
* @param {ApiKeyProjectRole[]} roles - The new roles of the project scope. | ||
* @returns {Promise<void>} - A promise that resolves when the roles are successfully updated. | ||
*/ | ||
updateRolesOfProjectScope( | ||
userId: User['id'], | ||
projectId: ProjectScope['projectId'], | ||
roles: ApiKeyProjectRole[] | ||
): Promise<void> | ||
|
||
/** | ||
* Deletes an API key. | ||
* @param {ApiKey['id']} apiKeyId - The ID of the API key to delete. | ||
* @returns {Promise<void>} - A promise that resolves when the API key is successfully deleted. | ||
*/ | ||
deleteApiKey(apiKeyId: ApiKey['id']): Promise<void> | ||
|
||
/** | ||
* Finds an API key by its value. | ||
* @param {ApiKey['value']} apiKeyValue - The value of the API key to find. | ||
* @returns {Promise<ApiKey | null>} - A promise that resolves to the found API key or null if not found. | ||
*/ | ||
findApiKeyByValue(apiKeyValue: ApiKey['value']): Promise<ApiKey | null> | ||
|
||
/** | ||
* Finds an API key by its ID and user ID. | ||
* @param {ApiKey['id']} apiKeyId - The ID of the API key to find. | ||
* @param {User['id']} userId - The ID of the user. | ||
* @returns {Promise<ApiKey | null>} - A promise that resolves to the found API key or null if not found. | ||
*/ | ||
findApiKeyByIdAndUserId( | ||
apiKeyId: ApiKey['id'], | ||
userId: User['id'] | ||
): Promise<ApiKey | null> | ||
|
||
/** | ||
* Finds all API keys for a user. | ||
* @param {User['id']} userId - The ID of the user. | ||
* @returns {Promise<ApiKey[]>} - A promise that resolves to an array of API keys for the user. | ||
*/ | ||
findAllApiKeysByUserId(userId: User['id']): Promise<ApiKey[]> | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export class MockApiKeyRepository {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { ApiKeyService } from './api-key.service'; | ||
|
||
describe('ApiKeyService', () => { | ||
let service: ApiKeyService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ApiKeyService], | ||
}).compile(); | ||
|
||
service = module.get<ApiKeyService>(ApiKeyService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { Inject, Injectable } from '@nestjs/common' | ||
import { | ||
API_KEY_REPOSITORY, | ||
IApiKeyRepository | ||
} from '../repository/interface.repository' | ||
import { | ||
IProjectRepository, | ||
PROJECT_REPOSITORY | ||
} from '../../project/repository/interface.repository' | ||
import { | ||
IUserRepository, | ||
USER_REPOSITORY | ||
} from '../../user/repository/interface.repository' | ||
import { ApiKeyProjectRole, Project, User } from '@prisma/client' | ||
// import { CreateApiKey } from '../dto/create.api-key/create.api-key' | ||
import { ProjectPermission } from '../../project/misc/project.permission' | ||
|
||
@Injectable() | ||
export class ApiKeyService { | ||
constructor( | ||
@Inject(API_KEY_REPOSITORY) | ||
private readonly apiKeyRepository: IApiKeyRepository, | ||
@Inject(PROJECT_REPOSITORY) | ||
private readonly projectRepository: IProjectRepository, | ||
private readonly projectPermissionService: ProjectPermission, | ||
@Inject(USER_REPOSITORY) | ||
private readonly userRepository: IUserRepository | ||
) {} | ||
|
||
async getPermissableScopesOfProjecr(user: User, projectId: Project['id']) { | ||
const roles: ApiKeyProjectRole[] = [] | ||
|
||
if (this.projectPermissionService.isProjectMember(user, projectId)) { | ||
roles.push( | ||
...[ | ||
ApiKeyProjectRole.READ_PROJECT, | ||
ApiKeyProjectRole.READ_SECRET, | ||
ApiKeyProjectRole.READ_ENVIRONMENT | ||
] | ||
) | ||
} | ||
} | ||
|
||
// async createApiKey(user: User, dto: CreateApiKey) { | ||
// // For each project scope, check if the user has the required roles to perform the action. | ||
// } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { randomBytes } from 'crypto' | ||
|
||
export const generateApiKey = (): string => | ||
'ks_' + randomBytes(48).toString('hex') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import { ApiKeyProjectRole } from '@prisma/client' | ||
|
||
export const MEMBERSHIP_ROLES: ApiKeyProjectRole[] = [ | ||
ApiKeyProjectRole.READ_PROJECT, | ||
ApiKeyProjectRole.READ_SECRET, | ||
ApiKeyProjectRole.READ_ENVIRONMENT, | ||
ApiKeyProjectRole.READ_USERS | ||
] | ||
|
||
export const MAINTAINER_ROLES: ApiKeyProjectRole[] = [ | ||
ApiKeyProjectRole.CREATE_SECRET, | ||
ApiKeyProjectRole.UPDATE_SECRET, | ||
ApiKeyProjectRole.DELETE_SECRET, | ||
ApiKeyProjectRole.CREATE_ENVIRONMENT, | ||
ApiKeyProjectRole.UPDATE_ENVIRONMENT, | ||
ApiKeyProjectRole.DELETE_ENVIRONMENT | ||
] | ||
|
||
export const OWNER_ROLES: ApiKeyProjectRole[] = [ | ||
ApiKeyProjectRole.UPDATE_PROJECT, | ||
ApiKeyProjectRole.DELETE_PROJECT, | ||
ApiKeyProjectRole.ADD_USER, | ||
ApiKeyProjectRole.REMOVE_USER, | ||
ApiKeyProjectRole.UPDATE_USER_ROLE | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { createHash } from 'crypto' | ||
|
||
export const toSHA256 = (value: string): string => | ||
createHash('sha256').update(value).digest().toString('hex') |
Oops, something went wrong.