Skip to content

Commit ddbfb57

Browse files
bhavanakarwadeshitrerohit
authored andcommitted
feat: implemented destroy session functionality
Signed-off-by: bhavanakarwade <[email protected]>
1 parent 1b4b699 commit ddbfb57

File tree

11 files changed

+113
-17
lines changed

11 files changed

+113
-17
lines changed

.env.demo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ OTEL_LOGS_OTLP_ENDPOINT='http://localhost:4318/v1/logs'
156156
OTEL_HEADERS_KEY=88ca6b1XXXXXXXXXXXXXXXXXXXXXXXXXXX
157157
OTEL_LOGGER_NAME='credebl-platform-logger'
158158
HOSTNAME='localhost'
159-
159+
SESSIONS_LIMIT=10
160160
# SSO
161161
# To add more clients, simply add comma separated values of client names
162162
SUPPORTED_SSO_CLIENTS=CREDEBL

.env.sample

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ SCHEMA_FILE_SERVER_URL= // Please provide schema URL
153153
SCHEMA_FILE_SERVER_TOKEN=xxxxxxxx // Please provide schema file server token for polygon
154154

155155
FILEUPLOAD_CACHE_TTL= //Provide file upload cache ttl
156-
156+
SESSIONS_LIMIT= //Provide limits of sessions
157157
FIELD_UPLOAD_SIZE= //Provide field upload size
158158

159159
IS_ECOSYSTEM_ENABLE= //Set this flag to `true` to enable the ecosystem, or `false` to disable it.

apps/api-gateway/src/authz/authz.controller.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
} from '@nestjs/common';
1616
import { AuthzService } from './authz.service';
1717
import { CommonService } from '../../../../libs/common/src/common.service';
18-
import { ApiBody, ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
18+
import { ApiBearerAuth, ApiBody, ApiOperation, ApiQuery, ApiResponse, ApiTags } from '@nestjs/swagger';
1919
import { ApiResponseDto } from '../dtos/apiResponse.dto';
2020
import { UserEmailVerificationDto } from '../user/dto/create-user.dto';
2121
import IResponseType from '@credebl/common/interfaces/response.interface';
@@ -33,9 +33,9 @@ import { RefreshTokenDto } from './dtos/refresh-token.dto';
3333
import { getDefaultClient } from '../user/utils';
3434
import { ClientAliasValidationPipe } from './decorators/user-auth-client';
3535
import { SessionGuard } from './guards/session.guard';
36-
interface SessionDetails {
37-
sessionId: string;
38-
}
36+
import { UserLogoutDto } from './dtos/user-logout.dto';
37+
import { AuthGuard } from '@nestjs/passport';
38+
import { ISessionData } from 'apps/user/interfaces/user.interface';
3939
@Controller('auth')
4040
@ApiTags('auth')
4141
@UseFilters(CustomExceptionFilter)
@@ -190,11 +190,7 @@ export class AuthzController {
190190
required: false
191191
})
192192
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: AuthTokenResponse })
193-
async sessionDetails(
194-
@Res() res: Response,
195-
@Req() req: Request,
196-
@Query() sessionId: SessionDetails
197-
): Promise<Response> {
193+
async sessionDetails(@Res() res: Response, @Req() req: Request, @Query() sessionId: ISessionData): Promise<Response> {
198194
this.logger.debug(`in authz controller`);
199195

200196
let sessionDetails;
@@ -309,4 +305,30 @@ export class AuthzController {
309305

310306
return res.status(HttpStatus.OK).json(finalResponse);
311307
}
308+
309+
/**
310+
* Log out user.
311+
*
312+
* @body LogoutUserDto
313+
* @returns Logged out user from current session
314+
*/
315+
@Post('/signout')
316+
@ApiOperation({
317+
summary: 'Logout user',
318+
description: 'Logout user from current session.'
319+
})
320+
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto })
321+
@UseGuards(AuthGuard('jwt'))
322+
@ApiBearerAuth()
323+
@ApiBody({ type: UserLogoutDto })
324+
async logout(@Body() logoutUserDto: UserLogoutDto, @Res() res: Response): Promise<Response> {
325+
await this.authzService.logout(logoutUserDto);
326+
327+
const finalResponse: IResponseType = {
328+
statusCode: HttpStatus.OK,
329+
message: ResponseMessages.user.success.logout
330+
};
331+
332+
return res.status(HttpStatus.OK).json(finalResponse);
333+
}
312334
}

apps/api-gateway/src/authz/authz.service.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { ResetTokenPasswordDto } from './dtos/reset-token-password';
1818
import { NATSClient } from '@credebl/common/NATSClient';
1919
import { user } from '@prisma/client';
2020
import { ISessionDetails } from 'apps/user/interfaces/user.interface';
21+
import { UserLogoutDto } from './dtos/user-logout.dto';
2122
@Injectable()
2223
@WebSocketGateway()
2324
export class AuthzService extends BaseService {
@@ -79,4 +80,8 @@ export class AuthzService extends BaseService {
7980
const payload = { userInfo };
8081
return this.natsClient.sendNatsMessage(this.authServiceProxy, 'add-user', payload);
8182
}
83+
84+
async logout(logoutUserDto: UserLogoutDto): Promise<string> {
85+
return this.natsClient.sendNatsMessage(this.authServiceProxy, 'user-logout', logoutUserDto);
86+
}
8287
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator';
2+
3+
import { ApiPropertyOptional } from '@nestjs/swagger';
4+
5+
export class UserLogoutDto {
6+
@ApiPropertyOptional({
7+
description: 'List of session IDs to log out',
8+
type: [String]
9+
})
10+
@IsOptional()
11+
@IsArray({ message: 'sessions must be an array' })
12+
@IsString({ each: true, message: 'each session Id must be a string' })
13+
@IsNotEmpty({ each: true, message: 'session Id must not be empty' })
14+
sessions?: string[];
15+
}

apps/organization/src/organization.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ export class OrganizationService {
705705
this.logger.debug(`orgRoleDetails::::${JSON.stringify(orgRoleDetails)}`);
706706
// check seesion details
707707
const userSessionDetails = await this.userRepository.fetchUserSessions(orgRoleDetails['user'].id);
708-
if (10 <= userSessionDetails?.length) {
708+
if (Number(process.env.SESSIONS_LIMIT) <= userSessionDetails?.length) {
709709
throw new BadRequestException(ResponseMessages.user.error.sessionLimitReached);
710710
}
711711
// Creation sessison and account

apps/user/interfaces/user.interface.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ export interface UserRoleMapping {
244244
userRoleId: string;
245245
}
246246

247+
export interface ISessions {
248+
sessions: string[];
249+
}
247250
export interface UserRoleDetails {
248251
id: string;
249252
role: $Enums.UserRole;
@@ -273,3 +276,6 @@ export interface IAccountDetails {
273276
id_token?: string;
274277
session_state?: string;
275278
}
279+
export interface ISessionData {
280+
sessionId: string;
281+
}

apps/user/repositories/user.repository.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@ import {
1616
UserRoleMapping
1717
} from '../interfaces/user.interface';
1818
import { Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common';
19-
import { ProviderType, UserRole } from '@credebl/enum/enum';
2019
// eslint-disable-next-line camelcase
21-
import { RecordType, account, client_aliases, schema, session, token, user, user_org_roles } from '@prisma/client';
20+
import {
21+
Prisma,
22+
RecordType,
23+
account,
24+
client_aliases,
25+
schema,
26+
session,
27+
token,
28+
user,
29+
user_org_roles
30+
} from '@prisma/client';
31+
import { ProviderType, UserRole } from '@credebl/enum/enum';
2232

2333
import { PrismaService } from '@credebl/prisma-service';
2434

@@ -959,4 +969,21 @@ export class UserRepository {
959969
throw error;
960970
}
961971
}
972+
973+
async destroySession(sessions: string[]): Promise<Prisma.BatchPayload> {
974+
try {
975+
const userSessions = await this.prisma.session.deleteMany({
976+
where: {
977+
id: {
978+
in: sessions
979+
}
980+
}
981+
});
982+
983+
return userSessions;
984+
} catch (error) {
985+
this.logger.error(`Error in logging out user: ${error.message}`);
986+
throw error;
987+
}
988+
}
962989
}

apps/user/src/user.controller.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
ICheckUserDetails,
33
IOrgUsers,
44
ISessionDetails,
5+
ISessions,
56
IUserDeletedActivity,
67
IUserForgotPassword,
78
IUserInformation,
@@ -263,4 +264,9 @@ export class UserController {
263264
async getuserOrganizationByUserId(payload: { userId: string }): Promise<user_org_roles[]> {
264265
return this.userService.getuserOrganizationByUserId(payload.userId);
265266
}
267+
268+
@MessagePattern({ cmd: 'user-logout' })
269+
async logout(logoutUserDto: ISessions): Promise<string> {
270+
return this.userService.logout(logoutUserDto);
271+
}
266272
}

apps/user/src/user.service.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import {
4040
UserKeycloakId,
4141
IEcosystemConfig,
4242
IUserForgotPassword,
43-
ISessionDetails
43+
ISessionDetails,
44+
ISessions
4445
} from '../interfaces/user.interface';
4546
import { AcceptRejectInvitationDto } from '../dtos/accept-reject-invitation.dto';
4647
import { UserActivityService } from '@credebl/user-activity';
@@ -451,7 +452,7 @@ export class UserService {
451452

452453
const userSessionDetails = await this.userRepository.fetchUserSessions(userData?.id);
453454

454-
if (3 <= userSessionDetails?.length) {
455+
if (Number(process.env.SESSIONS_LIMIT) <= userSessionDetails?.length) {
455456
throw new BadRequestException(ResponseMessages.user.error.sessionLimitReached);
456457
}
457458

@@ -1300,4 +1301,17 @@ export class UserService {
13001301
throw new RpcException(error.response ? error.response : error);
13011302
}
13021303
}
1304+
1305+
async logout(logoutUserDto: ISessions): Promise<string> {
1306+
try {
1307+
if (logoutUserDto?.sessions) {
1308+
await this.userRepository.destroySession(logoutUserDto?.sessions);
1309+
}
1310+
1311+
return 'user logged out successfully';
1312+
} catch (error) {
1313+
this.logger.error(`Error in logging out session: ${error}`);
1314+
throw new RpcException(error.response ? error.response : error);
1315+
}
1316+
}
13031317
}

0 commit comments

Comments
 (0)