Skip to content

Restructure #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"tsconfig-paths/register"
],
"sourceMaps": true,
"envFile": "${workspaceFolder}/.env",
"cwd": "${workspaceRoot}",
"console": "integratedTerminal",
"protocol": "inspector"
Expand Down
14 changes: 6 additions & 8 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { Keystore, KeystoreSchema } from './schemas/keystore.schema';
import { UserModule } from '../user/user.module';
import { Role, RoleSchema } from './schemas/role.schema';
import { RolesGuard } from './guards/roles.guard';
import { ApiKeyGuard } from './guards/apikey.guard';
import { ApiKey, ApiKeySchema } from './schemas/apikey.schema';

@Module({
imports: [
Expand All @@ -19,21 +21,17 @@ import { RolesGuard } from './guards/roles.guard';
imports: [ConfigModule],
useClass: TokenFactory,
}),
MongooseModule.forFeature([{ name: ApiKey.name, schema: ApiKeySchema }]),
MongooseModule.forFeature([
{ name: Keystore.name, schema: KeystoreSchema },
]),
MongooseModule.forFeature([{ name: Role.name, schema: RoleSchema }]),
UserModule,
],
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
{
provide: APP_GUARD,
useClass: RolesGuard,
},
{ provide: APP_GUARD, useClass: ApiKeyGuard },
{ provide: APP_GUARD, useClass: AuthGuard },
{ provide: APP_GUARD, useClass: RolesGuard },
AuthService,
],
controllers: [AuthController],
Expand Down
43 changes: 36 additions & 7 deletions src/core/core.service.spec.ts → src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { Test } from '@nestjs/testing';
import { CoreService } from './core.service';
import { AuthService } from './auth.service';
import { ApiKey } from './schemas/apikey.schema';
import { getModelToken } from '@nestjs/mongoose';
import { Keystore } from './schemas/keystore.schema';
import { Role } from './schemas/role.schema';
import { JwtService } from '@nestjs/jwt';
import { UserService } from '../user/user.service';
import { ConfigService } from '@nestjs/config';

describe('CoreService', () => {
describe('AuthService', () => {
const validKey = 'api_key';
const expectedResult = {
key: 'api_key',
Expand All @@ -16,26 +21,50 @@ describe('CoreService', () => {
})),
}));

let coreService: CoreService;
let keyService: AuthService;

beforeEach(async () => {
findOneMockFn.mockClear();
const module = await Test.createTestingModule({
providers: [
CoreService,
AuthService,
{
provide: getModelToken(ApiKey.name),
useValue: {
findOne: findOneMockFn,
},
},
{
provide: getModelToken(Keystore.name),
useValue: {},
},
{
provide: getModelToken(Role.name),
useValue: {},
},
{
provide: getModelToken(Role.name),
useValue: {},
},
{
provide: JwtService,
useValue: {},
},
{
provide: UserService,
useValue: {},
},
{
provide: ConfigService,
useValue: {},
},
],
}).compile();
coreService = module.get(CoreService);
keyService = module.get(AuthService);
});

it('should return the API key if correct key is sent', async () => {
const result = await coreService.findApiKey(validKey);
const result = await keyService.findApiKey(validKey);
expect(result).toEqual(expectedResult);
expect(findOneMockFn).toHaveBeenCalledWith({
key: validKey,
Expand All @@ -45,7 +74,7 @@ describe('CoreService', () => {

it('should return null if wrong key is sent', async () => {
const wrongKey = 'api_key_1';
const result = await coreService.findApiKey(wrongKey);
const result = await keyService.findApiKey(wrongKey);
expect(result).toBeNull();
expect(findOneMockFn).toHaveBeenCalledWith({
key: wrongKey,
Expand Down
15 changes: 15 additions & 0 deletions src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import { TokenRefreshDto } from './dto/token-refresh.dto';
import { UserTokensDto } from './dto/user-tokens.dto';
import { SignUpBasicDto } from './dto/signup-basic.dto';
import { Role, RoleCode } from './schemas/role.schema';
import { ApiKey } from './schemas/apikey.schema';

@Injectable()
export class AuthService {
constructor(
@InjectModel(ApiKey.name) private readonly apikeyModel: Model<ApiKey>,
@InjectModel(Keystore.name) private readonly keystoreModel: Model<Keystore>,
@InjectModel(Role.name) private readonly roleModel: Model<Role>,
private readonly jwtService: JwtService,
Expand Down Expand Up @@ -258,4 +260,17 @@ export class AuthService {
async deleteRole(role: Role) {
return this.roleModel.findByIdAndDelete(role._id).lean().exec();
}

async findApiKey(key: string): Promise<ApiKey | null> {
return this.apikeyModel.findOne({ key: key, status: true }).lean().exec();
}

async createApiKey(apikey: Omit<ApiKey, '_id' | 'status'>): Promise<ApiKey> {
const created = await this.apikeyModel.create(apikey);
return created.toObject();
}

async deleteApiKey(apikey: ApiKey) {
return this.apikeyModel.findByIdAndDelete(apikey._id).lean().exec();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ApiKeyGuard } from './apikey.guard';
import { CoreService } from '../core.service';
import { Reflector } from '@nestjs/core';
import { ExecutionContext, ForbiddenException } from '@nestjs/common';
import { HeaderName } from '../http/header';
import { ApiKey, Permission } from '../schemas/apikey.schema';
import { ApiKey, Permission } from '../../auth/schemas/apikey.schema';
import { AuthService } from '../auth.service';
import { HeaderName } from '../../core/http/header';

describe('ApiKeyGuard', () => {
let apiKeyGuard: ApiKeyGuard;
Expand All @@ -30,7 +30,7 @@ describe('ApiKeyGuard', () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
ApiKeyGuard,
{ provide: CoreService, useValue: { findApiKey: findApiKeyMock } },
{ provide: AuthService, useValue: { findApiKey: findApiKeyMock } },
{ provide: Reflector, useValue: { get: reflectorGetMock } },
],
}).compile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import {
ForbiddenException,
Injectable,
} from '@nestjs/common';
import { HeaderName } from '../http/header';
import { CoreService } from '../core.service';
import { HeaderName } from '../../core/http/header';
import { Reflector } from '@nestjs/core';
import { Permissions } from '../decorators/permissions.decorator';
import { PublicRequest } from '../http/request';
import { Permission } from '../schemas/apikey.schema';
import { PublicRequest } from '../../core/http/request';
import { Permission } from '../../auth/schemas/apikey.schema';
import { AuthService } from '../auth.service';

@Injectable()
export class ApiKeyGuard implements CanActivate {
constructor(
private readonly coreService: CoreService,
private readonly authService: AuthService,
private readonly reflector: Reflector,
) {}

Expand All @@ -29,7 +29,7 @@ export class ApiKeyGuard implements CanActivate {
const key = request.headers[HeaderName.API_KEY]?.toString();
if (!key) throw new ForbiddenException();

const apiKey = await this.coreService.findApiKey(key);
const apiKey = await this.authService.findApiKey(key);
if (!apiKey) throw new ForbiddenException();

request.apiKey = apiKey;
Expand Down
File renamed without changes.
13 changes: 2 additions & 11 deletions src/core/core.module.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import { Module, ValidationPipe } from '@nestjs/common';
import { APP_FILTER, APP_GUARD, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
import { ResponseTransformer } from './interceptors/response.transformer';
import { CoreService } from './core.service';
import { MongooseModule } from '@nestjs/mongoose';
import { ApiKey, ApiKeySchema } from './schemas/apikey.schema';
import { ApiKeyGuard } from './guards/apikey.guard';
import { ExpectionHandler } from './interceptors/exception.handler';
import { ResponseValidation } from './interceptors/response.validations';
import { ConfigModule } from '@nestjs/config';
import { WinstonLogger } from '../setup/winston.logger';
import { CoreController } from './core.controller';

@Module({
imports: [
MongooseModule.forFeature([{ name: ApiKey.name, schema: ApiKeySchema }]),
ConfigModule,
],
imports: [ConfigModule],
providers: [
{ provide: APP_INTERCEPTOR, useClass: ResponseTransformer },
{ provide: APP_INTERCEPTOR, useClass: ResponseValidation },
{ provide: APP_FILTER, useClass: ExpectionHandler },
{ provide: APP_GUARD, useClass: ApiKeyGuard },
{
provide: APP_PIPE,
useValue: new ValidationPipe({
Expand All @@ -29,7 +21,6 @@ import { CoreController } from './core.controller';
forbidNonWhitelisted: true,
}),
},
CoreService,
WinstonLogger,
],
controllers: [CoreController],
Expand Down
24 changes: 0 additions & 24 deletions src/core/core.service.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/core/http/request.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Request } from 'express';
import { User } from '../../user/schemas/user.schema';
import { ApiKey } from '../schemas/apikey.schema';
import { ApiKey } from '../../auth/schemas/apikey.schema';
import { Keystore } from '../../auth/schemas/keystore.schema';

export interface PublicRequest extends Request {
Expand Down
4 changes: 2 additions & 2 deletions src/message/message.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Body, Controller, Post, Request } from '@nestjs/common';
import { MessageService } from './message.service';
import { CreateMessageDto } from './dto/create-message.dto';
import { Permissions } from '../core/decorators/permissions.decorator';
import { Permission } from '../core/schemas/apikey.schema';
import { Permissions } from '../auth/decorators/permissions.decorator';
import { Permission } from '../auth/schemas/apikey.schema';
import { ProtectedRequest } from '../core/http/request';
import { Roles } from '../auth/decorators/roles.decorator';
import { RoleCode } from '../auth/schemas/role.schema';
Expand Down
24 changes: 12 additions & 12 deletions test/app-apikey.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import { StatusCode } from '../src/core/http/response';
import { Permission } from '../src/core/schemas/apikey.schema';
import { CoreService } from '../src/core/core.service';
import { Permission } from '../src/auth/schemas/apikey.schema';
import { AuthService } from '../src/auth/auth.service';

describe('AppController - API KEY (e2e)', () => {
let app: INestApplication;
let coreService: CoreService;
let authService: AuthService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();

coreService = module.get(CoreService);
authService = module.get(AuthService);
app = module.createNestApplication();

await app.init();
Expand All @@ -36,7 +36,7 @@ describe('AppController - API KEY (e2e)', () => {
});

it('should throw 403 when wrong x-api-key is provided', async () => {
const apiKey = await coreService.createApiKey({
const apiKey = await authService.createApiKey({
key: 'test_api_key',
version: 1,
permissions: [Permission.GENERAL],
Expand All @@ -54,12 +54,12 @@ describe('AppController - API KEY (e2e)', () => {
expect(response.body.message).toEqual('Forbidden');
});
} finally {
await coreService.deleteApiKey(apiKey);
await authService.deleteApiKey(apiKey);
}
});

it('should throw 403 when x-api-key does not have right permissions', async () => {
const apiKey = await coreService.createApiKey({
const apiKey = await authService.createApiKey({
key: 'test_api_key_1',
version: 1,
permissions: [Permission.XYZ_SERVICE],
Expand All @@ -77,12 +77,12 @@ describe('AppController - API KEY (e2e)', () => {
expect(response.body.message).toEqual('Forbidden');
});
} finally {
await coreService.deleteApiKey(apiKey);
await authService.deleteApiKey(apiKey);
}
});

it('should throw 401 when correct x-api-key is provided', async () => {
const apiKey = await coreService.createApiKey({
const apiKey = await authService.createApiKey({
key: 'test_api_key_2',
version: 1,
permissions: [Permission.GENERAL],
Expand All @@ -100,12 +100,12 @@ describe('AppController - API KEY (e2e)', () => {
expect(response.body.message).toEqual('Unauthorized');
});
} finally {
await coreService.deleteApiKey(apiKey);
await authService.deleteApiKey(apiKey);
}
});

it('should send 200 when correct x-api-key is provided for heartbeat', async () => {
const apiKey = await coreService.createApiKey({
const apiKey = await authService.createApiKey({
key: 'test_api_key_3',
version: 1,
permissions: [Permission.GENERAL],
Expand All @@ -123,7 +123,7 @@ describe('AppController - API KEY (e2e)', () => {
expect(response.body.message).toEqual('alive');
});
} finally {
await coreService.deleteApiKey(apiKey);
await authService.deleteApiKey(apiKey);
}
});
});
Loading
Loading