feat(SPG-885): add validation login admin queue
continuous-integration/drone/push Build is passing Details

pull/89/head
Firman Ramdhani 2024-09-12 19:17:46 +07:00
parent 14dd2880bc
commit c897e4fcde
14 changed files with 136 additions and 21 deletions

View File

@ -0,0 +1,17 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumnItemId1726139426994 implements MigrationInterface {
name = 'AddColumnItemId1726139426994';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users_login" ADD "item_id" uuid`);
await queryRunner.query(`ALTER TABLE "log_users_login" ADD "item_id" uuid`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "log_users_login" DROP COLUMN "item_id"`,
);
await queryRunner.query(`ALTER TABLE "users_login" DROP COLUMN "item_id"`);
}
}

View File

@ -0,0 +1,23 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumnItemName1726141393404 implements MigrationInterface {
name = 'AddColumnItemName1726141393404';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "users_login" ADD "item_name" character varying`,
);
await queryRunner.query(
`ALTER TABLE "log_users_login" ADD "item_name" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "log_users_login" DROP COLUMN "item_name"`,
);
await queryRunner.query(
`ALTER TABLE "users_login" DROP COLUMN "item_name"`,
);
}
}

View File

@ -13,11 +13,15 @@ import { AuthAdminQueueController } from './infrastructure/auth-admin-queue.cont
import { AuthAdminQueueOrchestrator } from './domain/auth-admin-queue.orchestrator'; import { AuthAdminQueueOrchestrator } from './domain/auth-admin-queue.orchestrator';
import { LoginAdminQueueManager } from './domain/managers/admin-queue/login-admin-queue.manager'; import { LoginAdminQueueManager } from './domain/managers/admin-queue/login-admin-queue.manager';
import { LogoutAdminQueueManager } from './domain/managers/admin-queue/logout-admin-queue.manager'; import { LogoutAdminQueueManager } from './domain/managers/admin-queue/logout-admin-queue.manager';
import { UserLoginModel } from 'src/modules/user-related/user/data/models/user-login.model';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot(), ConfigModule.forRoot(),
TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), TypeOrmModule.forFeature(
[UserModel, UserLoginModel],
CONNECTION_NAME.DEFAULT,
),
CqrsModule, CqrsModule,
], ],
controllers: [AuthController, AuthAdminQueueController], controllers: [AuthController, AuthAdminQueueController],

View File

@ -33,6 +33,10 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
} }
async process(): Promise<void> { async process(): Promise<void> {
const itemLogin = await this.dataService.getLoginUserByItem(
this.data.item_id,
);
// get user active by username // get user active by username
this.userLogin = await this.dataService.getOneByOptions({ this.userLogin = await this.dataService.getOneByOptions({
where: { where: {
@ -42,6 +46,7 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
}, },
relations: ['user_login'], relations: ['user_login'],
}); });
if (!this.userLogin) this.throwError(); if (!this.userLogin) this.throwError();
// validasi password // validasi password
@ -51,13 +56,22 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
); );
if (!valid) this.throwError(); if (!valid) this.throwError();
if ( const userLoginItem = await this.dataService.getOneByOptions({
this.userLogin.user_login && where: {
this.userLogin.role === UserRole.QUEUE_ADMIN id: itemLogin?.user_id,
) { },
});
if (this.userLogin.user_login) {
throw new UnauthorizedException({ throw new UnauthorizedException({
statusCode: HttpStatus.UNAUTHORIZED, statusCode: HttpStatus.UNAUTHORIZED,
message: `Gagal! akun anda sudah login di perangkat lain.`, message: `Akun anda sudah login di perangkat lain.`,
error: 'Unauthorized',
});
} else if (itemLogin) {
throw new UnauthorizedException({
statusCode: HttpStatus.UNAUTHORIZED,
message: `"${userLoginItem.name}" masih login sebagai admin antrian `,
error: 'Unauthorized', error: 'Unauthorized',
}); });
} }
@ -69,6 +83,8 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
username: this.userLogin.username, username: this.userLogin.username,
role: this.userLogin.role, role: this.userLogin.role,
user_privilege_id: this.userLogin.user_privilege_id, user_privilege_id: this.userLogin.user_privilege_id,
item_id: this.data.item_id,
item_name: this.data.item_name,
}; };
Logger.debug('Sign Token Admin Queue', 'LoginAdminQueueManager'); Logger.debug('Sign Token Admin Queue', 'LoginAdminQueueManager');
@ -79,22 +95,21 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
Logger.debug('Update Refresh Token Admin Queue', 'LoginAdminQueueManager'); Logger.debug('Update Refresh Token Admin Queue', 'LoginAdminQueueManager');
const newDataUser = { refresh_token: refreshToken };
if (this.userLogin.role === UserRole.QUEUE_ADMIN) {
Object.assign(newDataUser, {
user_login: {
user_id: this.userLogin.id,
login_token: this.token,
login_date: new Date().getTime(),
},
});
}
// Update refresh token // Update refresh token
await this.dataService.update( await this.dataService.update(
this.queryRunner, this.queryRunner,
this.entityTarget, this.entityTarget,
{ id: this.userLogin.id }, { id: this.userLogin.id },
newDataUser, {
refresh_token: refreshToken,
user_login: {
user_id: this.userLogin.id,
login_token: this.token,
login_date: new Date().getTime(),
item_id: this.data.item_id,
item_name: this.data.item_name,
},
},
); );
await this.publishEvents(); await this.publishEvents();
@ -113,6 +128,8 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
username: this.userLogin.username, username: this.userLogin.username,
role: this.userLogin.role, role: this.userLogin.role,
token: this.token, token: this.token,
item_id: this.data.item_id,
item_name: this.data.item_name,
}; };
} }
@ -130,6 +147,8 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
user_id: this.userLogin.id, user_id: this.userLogin.id,
username: this.userLogin.username, username: this.userLogin.username,
created_at: new Date().getTime(), created_at: new Date().getTime(),
item_id: this.data.item_id,
item_name: this.data.item_name,
}, },
}, },
]; ];

View File

@ -1,7 +1,7 @@
import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common'; import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common';
import { ExcludePrivilege, Public } from 'src/core/guards'; import { ExcludePrivilege, Public } from 'src/core/guards';
import { ApiBearerAuth } from '@nestjs/swagger'; import { ApiBearerAuth } from '@nestjs/swagger';
import { LoginDto } from './dto/login.dto'; import { LoginQueueDto } from './dto/login.dto';
import { AuthAdminQueueOrchestrator } from '../domain/auth-admin-queue.orchestrator'; import { AuthAdminQueueOrchestrator } from '../domain/auth-admin-queue.orchestrator';
@Controller('v1/auth/queue') @Controller('v1/auth/queue')
@ -10,7 +10,7 @@ export class AuthAdminQueueController {
@Post() @Post()
@Public(true) @Public(true)
async login(@Body() body: LoginDto) { async login(@Body() body: LoginQueueDto) {
return await this.orchestrator.login(body); return await this.orchestrator.login(body);
} }

View File

@ -11,3 +11,21 @@ export class LoginDto implements LoginRequest {
@IsString() @IsString()
password: string; password: string;
} }
export class LoginQueueDto implements LoginRequest {
@ApiProperty({ name: 'username', required: true, default: 'superadmin' })
@IsString()
username: string;
@ApiProperty({ name: 'password', required: true, default: 'Eigen123!' })
@IsString()
password: string;
@ApiProperty({ name: 'item_id', required: true, default: 'string' })
@IsString()
item_id: string;
@ApiProperty({ name: 'item_name', required: true, default: 'string' })
@IsString()
item_name: string;
}

View File

@ -51,6 +51,7 @@ import {
import { SeasonPeriodDataService } from 'src/modules/season-related/season-period/data/services/season-period-data.service'; import { SeasonPeriodDataService } from 'src/modules/season-related/season-period/data/services/season-period-data.service';
import { SeasonPeriodModel } from 'src/modules/season-related/season-period/data/models/season-period.model'; import { SeasonPeriodModel } from 'src/modules/season-related/season-period/data/models/season-period.model';
import { TransactionDemographyModel } from 'src/modules/transaction/transaction/data/models/transaction-demography.model'; import { TransactionDemographyModel } from 'src/modules/transaction/transaction/data/models/transaction-demography.model';
import { UserLoginModel } from 'src/modules/user-related/user/data/models/user-login.model';
@Module({ @Module({
imports: [ imports: [
@ -61,6 +62,7 @@ import { TransactionDemographyModel } from 'src/modules/transaction/transaction/
ItemRateModel, ItemRateModel,
SeasonPeriodModel, SeasonPeriodModel,
UserModel, UserModel,
UserLoginModel,
TransactionModel, TransactionModel,
TransactionTaxModel, TransactionTaxModel,
TransactionItemModel, TransactionItemModel,

View File

@ -20,6 +20,12 @@ export class LogUserLoginModel
@Column({ type: 'uuid', nullable: true }) @Column({ type: 'uuid', nullable: true })
user_id: string; user_id: string;
@Column({ type: 'uuid', nullable: true })
item_id: string;
@Column({ type: 'varchar', nullable: true })
item_name: string;
@Column({ type: 'varchar', nullable: true }) @Column({ type: 'varchar', nullable: true })
username: string; username: string;

View File

@ -6,6 +6,8 @@ export interface LogUserLoginEntity extends BaseCoreEntity {
type: LogUserType; type: LogUserType;
role: UserRole; role: UserRole;
user_id: string; user_id: string;
item_id: string;
item_name: string;
username: string; username: string;
created_at: number; created_at: number;
} }

View File

@ -25,11 +25,15 @@ import { UserDataService } from '../user/data/services/user-data.service';
import { UserReadService } from '../user/data/services/user-read.service'; import { UserReadService } from '../user/data/services/user-read.service';
import { TenantItemReadController } from './infrastructure/tenant-item-read.controller'; import { TenantItemReadController } from './infrastructure/tenant-item-read.controller';
import { TenantItemDataController } from './infrastructure/tenant-item-data.controller'; import { TenantItemDataController } from './infrastructure/tenant-item-data.controller';
import { UserLoginModel } from '../user/data/models/user-login.model';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot(), ConfigModule.forRoot(),
TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), TypeOrmModule.forFeature(
[UserModel, UserLoginModel],
CONNECTION_NAME.DEFAULT,
),
CqrsModule, CqrsModule,
], ],
controllers: [ controllers: [

View File

@ -19,6 +19,12 @@ export class UserLoginModel
@Column('varchar', { name: 'user_id', nullable: true }) @Column('varchar', { name: 'user_id', nullable: true })
user_id: string; user_id: string;
@Column({ type: 'uuid', nullable: true })
item_id: string;
@Column({ type: 'varchar', nullable: true })
item_name: string;
@OneToOne(() => UserModel, (model) => model.user_login, { @OneToOne(() => UserModel, (model) => model.user_login, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
onUpdate: 'CASCADE', onUpdate: 'CASCADE',

View File

@ -4,14 +4,24 @@ import { UserEntity } from '../../domain/entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { UserModel } from '../models/user.model'; import { UserModel } from '../models/user.model';
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { Repository } from 'typeorm'; import { IsNull, Not, Repository } from 'typeorm';
import { UserLoginModel } from '../models/user-login.model';
@Injectable() @Injectable()
export class UserDataService extends BaseDataService<UserEntity> { export class UserDataService extends BaseDataService<UserEntity> {
constructor( constructor(
@InjectRepository(UserModel, CONNECTION_NAME.DEFAULT) @InjectRepository(UserModel, CONNECTION_NAME.DEFAULT)
private repo: Repository<UserModel>, private repo: Repository<UserModel>,
@InjectRepository(UserLoginModel, CONNECTION_NAME.DEFAULT)
private repoLoginUser: Repository<UserLoginModel>,
) { ) {
super(repo); super(repo);
} }
async getLoginUserByItem(itemId: string) {
return this.repoLoginUser.findOne({
where: { item_id: itemId, user_id: Not(IsNull()) },
});
}
} }

View File

@ -3,4 +3,6 @@ import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entit
export interface UserLoginEntity extends BaseCoreEntity { export interface UserLoginEntity extends BaseCoreEntity {
login_date: number; login_date: number;
login_token: string; login_token: string;
item_id: string;
item_name: string;
} }

View File

@ -52,6 +52,8 @@ export class IndexUserManager extends BaseIndexManager<UserEntity> {
'user_login.id', 'user_login.id',
'user_login.login_date', 'user_login.login_date',
'user_login.item_id',
'user_login.item_name',
]; ];
} }