Skip to content

Commit 1dd2223

Browse files
authored
ORV2-4838 Add AUDIT_LOGIN FF and rate limit (#2105)
1 parent 8eaa3c6 commit 1dd2223

File tree

8 files changed

+104
-4
lines changed

8 files changed

+104
-4
lines changed

database/mssql/scripts/sampledata/dbo.ORBC_FEATURE_FLAG.Table.sql

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,31 @@ VALUES
461461
N'dbo',
462462
GETUTCDATE()
463463
);
464+
465+
INSERT INTO
466+
[dbo].[ORBC_FEATURE_FLAG] (
467+
[FEATURE_ID],
468+
[FEATURE_KEY],
469+
[FEATURE_VALUE],
470+
[CONCURRENCY_CONTROL_NUMBER],
471+
[DB_CREATE_USERID],
472+
[DB_CREATE_TIMESTAMP],
473+
[DB_LAST_UPDATE_USERID],
474+
[DB_LAST_UPDATE_TIMESTAMP]
475+
)
476+
VALUES
477+
(
478+
'21',
479+
'AUDIT_LOGIN',
480+
'ENABLED',
481+
NULL,
482+
N'dbo',
483+
GETUTCDATE(),
484+
N'dbo',
485+
GETUTCDATE()
486+
);
487+
488+
464489
SET
465490
IDENTITY_INSERT [dbo].[ORBC_FEATURE_FLAG] OFF
466491
GO

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ services:
5858
VEHICLES_API_TYPEORM_LOG_LEVEL: ${VEHICLES_API_TYPEORM_LOG_LEVEL}
5959
VEHICLES_API_MAX_QUERY_EXECUTION_TIME_MS: ${VEHICLES_API_MAX_QUERY_EXECUTION_TIME_MS}
6060
VEHICLES_API_MSSQL_MAX_CONNECTION: ${VEHICLES_API_MSSQL_MAX_CONNECTION}
61+
VEHICLES_API_PUBLIC_AUTH_RATE_LIMIT: ${VEHICLES_API_PUBLIC_AUTH_RATE_LIMIT}
62+
VEHICLES_API_PUBLIC_AUTH_THROTTLER_TTL_MS: ${VEHICLES_API_PUBLIC_AUTH_THROTTLER_TTL_MS}
6163
DB_TYPE: ${DB_TYPE}
6264
MSSQL_HOST: sql-server-db
6365
MSSQL_PORT: ${MSSQL_PORT}

vehicles/package-lock.json

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vehicles/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"@nestjs/schematics": "^10.2.3",
5757
"@nestjs/swagger": "^7.4.2",
5858
"@nestjs/testing": "^10.4.15",
59+
"@nestjs/throttler": "^6.4.0",
5960
"@nestjs/typeorm": "^10.0.2",
6061
"@types/response-time": "^2.3.8",
6162
"cache-manager": "^5.7.6",

vehicles/src/app.module.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,21 @@ import { CreditAccountModule } from './modules/credit-account/credit-account.mod
4040
import { SpecialAuthModule } from './modules/special-auth/special-auth.module';
4141
import { CaseManagementModule } from './modules/case-management/case-management.module';
4242
import { VersionMatchMiddleware } from './common/middleware/version.middleware';
43+
import { ThrottlerModule } from '@nestjs/throttler';
4344

4445
const envPath = path.resolve(process.cwd() + '/../');
4546

4647
@Module({
4748
imports: [
4849
ConfigModule.forRoot({ envFilePath: `${envPath}/.env` }),
50+
ThrottlerModule.forRoot({
51+
throttlers: [
52+
{
53+
ttl: +process.env.VEHICLES_API_PUBLIC_AUTH_THROTTLER_TTL_MS || 60000,
54+
limit: +process.env.VEHICLES_API_PUBLIC_AUTH_RATE_LIMIT || 100,
55+
},
56+
],
57+
}),
4958
// Register the ClsModule,
5059
ClsModule.forRoot({
5160
global: true,

vehicles/src/modules/company-user-management/users/users.controller.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import {
33
Controller,
44
ForbiddenException,
55
Get,
6+
Inject,
67
Param,
78
Post,
89
Query,
910
Req,
11+
UseGuards,
1012
} from '@nestjs/common';
1113

1214
import {
@@ -37,6 +39,10 @@ import {
3739
IDIR_USER_ROLE_LIST,
3840
} from '../../../common/enum/user-role.enum';
3941
import { doesUserHaveRole } from '../../../common/helper/auth.helper';
42+
import { isFeatureEnabled } from '../../../common/helper/common.helper';
43+
import { CACHE_MANAGER } from '@nestjs/cache-manager';
44+
import { Cache } from 'cache-manager';
45+
import { ThrottlerGuard } from '@nestjs/throttler';
4046

4147
@ApiTags('Company and User Management - User')
4248
@ApiBadRequestResponse({
@@ -58,7 +64,11 @@ import { doesUserHaveRole } from '../../../common/helper/auth.helper';
5864
@ApiBearerAuth()
5965
@Controller('users')
6066
export class UsersController {
61-
constructor(private readonly userService: UsersService) {}
67+
constructor(
68+
private readonly userService: UsersService,
69+
@Inject(CACHE_MANAGER)
70+
private readonly cacheManager: Cache,
71+
) {}
6272

6373
/**
6474
* A POST method defined with a route of
@@ -79,6 +89,7 @@ export class UsersController {
7989
'It supports different identity providers, including IDIR and general ORBC user flows.',
8090
})
8191
@AuthOnly()
92+
@UseGuards(ThrottlerGuard)
8293
@Post('user-context')
8394
async find(@Req() request: Request): Promise<ReadUserOrbcStatusDto> {
8495
const currentUser = request.user as IUserJWT;
@@ -89,7 +100,9 @@ export class UsersController {
89100
} else {
90101
userExists = await this.userService.findORBCUser(currentUser);
91102
}
92-
await this.userService.saveLoginInformation(currentUser, userExists);
103+
if (await isFeatureEnabled(this.cacheManager, 'AUDIT_LOGIN')) {
104+
await this.userService.saveLoginInformation(currentUser, userExists);
105+
}
93106
return userExists;
94107
}
95108

vehicles/test/e2e/users.e2e-spec.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ import { readRedCompanyMetadataDtoMock } from 'test/util/mocks/data/company.mock
3939
import { App } from 'supertest/types';
4040
import { CompanyUser } from '../../src/modules/company-user-management/users/entities/company-user.entity';
4141
import { Login } from '../../src/modules/company-user-management/users/entities/login.entity';
42+
import { CACHE_MANAGER } from '@nestjs/cache-manager';
43+
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
44+
import { APP_GUARD } from '@nestjs/core';
4245

4346
let repo: DeepMocked<Repository<User>>;
4447
let repoCompanyUser: DeepMocked<Repository<CompanyUser>>;
@@ -48,6 +51,8 @@ let pendingUsersServiceMock: DeepMocked<PendingUsersService>;
4851
let pendingIdirUsersServiceMock: DeepMocked<PendingIdirUsersService>;
4952
let companyServiceMock: DeepMocked<CompanyService>;
5053

54+
let cacheManager: DeepMocked<Cache>;
55+
5156
describe('Users (e2e)', () => {
5257
let app: INestApplication<Express.Application>;
5358

@@ -60,11 +65,24 @@ describe('Users (e2e)', () => {
6065
pendingUsersServiceMock = createMock<PendingUsersService>();
6166
pendingIdirUsersServiceMock = createMock<PendingIdirUsersService>();
6267
companyServiceMock = createMock<CompanyService>();
68+
cacheManager = createMock<Cache>();
6369
const dataSourceMock = dataSourceMockFactory() as DataSource;
6470
const moduleFixture: TestingModule = await Test.createTestingModule({
65-
imports: [AutomapperModule],
71+
imports: [
72+
AutomapperModule,
73+
ThrottlerModule.forRoot({
74+
throttlers: [
75+
{
76+
ttl: +process.env.PUBLIC_API_THROTTLER_TTL_MS || 60000,
77+
limit: +process.env.PUBLIC_API_RATE_LIMIT || 100,
78+
},
79+
],
80+
}),
81+
],
6682
providers: [
6783
UsersService,
84+
{ provide: CACHE_MANAGER, useValue: cacheManager },
85+
{ provide: APP_GUARD, useValue: ThrottlerGuard },
6886
{
6987
provide: getRepositoryToken(User),
7088
useValue: repo,

vehicles/test/unit/users/users.controller.spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ import { IUserJWT } from '../../../src/common/interface/user-jwt.interface';
1919
import { GetStaffUserQueryParamsDto } from '../../../src/modules/company-user-management/users/dto/request/queryParam/getStaffUser.query-params.dto';
2020
import { IDIRUserRole } from '../../../src/common/enum/user-role.enum';
2121
import { BadRequestException } from '@nestjs/common';
22+
import { CACHE_MANAGER } from '@nestjs/cache-manager';
23+
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
24+
import { APP_GUARD } from '@nestjs/core';
2225

2326
const COMPANY_ID_99 = 99;
2427
let userService: DeepMocked<UsersService>;
@@ -28,15 +31,32 @@ interface FindUsersDtoMockParameters {
2831
companyId?: number[];
2932
}
3033

34+
let cacheManager: DeepMocked<Cache>;
35+
3136
describe('UsersController', () => {
3237
let controller: UsersController;
3338

3439
beforeEach(async () => {
3540
jest.clearAllMocks();
3641
userService = createMock<UsersService>();
42+
cacheManager = createMock<Cache>();
3743
const module: TestingModule = await Test.createTestingModule({
44+
imports: [
45+
ThrottlerModule.forRoot({
46+
throttlers: [
47+
{
48+
ttl: +process.env.PUBLIC_API_THROTTLER_TTL_MS || 60000,
49+
limit: +process.env.PUBLIC_API_RATE_LIMIT || 100,
50+
},
51+
],
52+
}),
53+
],
3854
controllers: [UsersController],
39-
providers: [{ provide: UsersService, useValue: userService }],
55+
providers: [
56+
{ provide: UsersService, useValue: userService },
57+
{ provide: CACHE_MANAGER, useValue: cacheManager },
58+
{ provide: APP_GUARD, useValue: ThrottlerGuard },
59+
],
4060
}).compile();
4161

4262
controller = module.get<UsersController>(UsersController);

0 commit comments

Comments
 (0)