Skip to content

Commit bf52105

Browse files
authored
Merge pull request #1415 from credebl/fix/inactive-session-deletion
Inactive session deletion
2 parents a6b69fd + 7f4071b commit bf52105

File tree

8 files changed

+136
-87
lines changed

8 files changed

+136
-87
lines changed

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

Lines changed: 81 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class UserController {
6262
private readonly userService: UserService,
6363
private readonly commonService: CommonService,
6464
private readonly awsService: AwsService
65-
) { }
65+
) {}
6666

6767
/**
6868
*
@@ -105,9 +105,9 @@ export class UserController {
105105
return res.status(HttpStatus.OK).json(finalResponse);
106106
}
107107

108-
/**
108+
/**
109109
* Get public profile details of a user by username.
110-
*
110+
*
111111
* @param username The username of the user.
112112
* @returns Public profile information.
113113
*/
@@ -134,7 +134,6 @@ export class UserController {
134134
return res.status(HttpStatus.OK).json(finalResponse);
135135
}
136136

137-
138137
/**
139138
* Retrieves the profile details of the currently logged-in user.
140139
*
@@ -149,7 +148,7 @@ export class UserController {
149148
@ApiBearerAuth()
150149
async getProfile(@User() reqUser: user, @Res() res: Response): Promise<Response> {
151150
const userData = await this.userService.getProfile(reqUser.id);
152-
151+
153152
const finalResponse: IResponse = {
154153
statusCode: HttpStatus.OK,
155154
message: ResponseMessages.user.success.fetchProfile,
@@ -159,9 +158,9 @@ export class UserController {
159158
return res.status(HttpStatus.OK).json(finalResponse);
160159
}
161160

162-
/**
161+
/**
163162
* Retrieves all platform settings.
164-
*
163+
*
165164
* @returns The platform settings.
166165
*/
167166
@Get('/platform-settings')
@@ -185,11 +184,11 @@ export class UserController {
185184
}
186185

187186
/**
188-
* Fetch user activities.
189-
*
190-
* @param limit - Number of activities to fetch.
191-
* @returns A response containing user activity data.
192-
*/
187+
* Fetch user activities.
188+
*
189+
* @param limit - Number of activities to fetch.
190+
* @returns A response containing user activity data.
191+
*/
193192
@Get('/activity')
194193
@ApiOperation({
195194
summary: 'Fetch users activity',
@@ -214,12 +213,11 @@ export class UserController {
214213
return res.status(HttpStatus.OK).json(finalResponse);
215214
}
216215

217-
218-
/**
219-
* Fetch organization invitations.
220-
*
221-
* @returns A paginated list of organization invitations.
222-
*/
216+
/**
217+
* Fetch organization invitations.
218+
*
219+
* @returns A paginated list of organization invitations.
220+
*/
223221
@Get('/org-invitations')
224222
@ApiOperation({
225223
summary: 'organization invitations',
@@ -248,7 +246,7 @@ export class UserController {
248246
required: false
249247
})
250248
async invitations(
251-
@Query() getAllInvitationsDto: GetAllInvitationsDto,
249+
@Query() getAllInvitationsDto: GetAllInvitationsDto,
252250
@User() reqUser: user,
253251
@Res() res: Response
254252
): Promise<Response> {
@@ -271,14 +269,17 @@ export class UserController {
271269
return res.status(HttpStatus.OK).json(finalResponse);
272270
}
273271

274-
/**
275-
* Checks if a user is registered and verifies email existence.
276-
*
277-
* @param email The email address to check.
278-
* @returns Returns user registration and email verification status.
279-
*/
272+
/**
273+
* Checks if a user is registered and verifies email existence.
274+
*
275+
* @param email The email address to check.
276+
* @returns Returns user registration and email verification status.
277+
*/
280278
@Get('/:email')
281-
@ApiOperation({ summary: 'Check user registration and email verification status', description: 'Check if a user is already registered and if their email already exists.' })
279+
@ApiOperation({
280+
summary: 'Check user registration and email verification status',
281+
description: 'Check if a user is already registered and if their email already exists.'
282+
})
282283
async checkUserExist(@Param() emailParam: EmailValidator, @Res() res: Response): Promise<Response> {
283284
const userDetails = await this.userService.checkUserExist(emailParam.email);
284285

@@ -292,12 +293,12 @@ export class UserController {
292293
}
293294

294295
/**
295-
* Accept or reject an organization invitation.
296-
*
297-
* @param invitationId The ID of the organization invitation.
298-
* @body AcceptRejectInvitationDto
299-
* @returns The status of the organization invitation response.
300-
*/
296+
* Accept or reject an organization invitation.
297+
*
298+
* @param invitationId The ID of the organization invitation.
299+
* @body AcceptRejectInvitationDto
300+
* @returns The status of the organization invitation response.
301+
*/
301302
@Post('/org-invitations/:invitationId')
302303
@ApiOperation({
303304
summary: 'accept/reject organization invitation',
@@ -306,8 +307,17 @@ export class UserController {
306307
@UseGuards(AuthGuard('jwt'), UserAccessGuard)
307308
@ApiBearerAuth()
308309
async acceptRejectInvitaion(
309-
@Body() acceptRejectInvitation: AcceptRejectInvitationDto,
310-
@Param('invitationId', TrimStringParamPipe, new ParseUUIDPipe({exceptionFactory: (): Error => { throw new BadRequestException(`Invalid format for InvitationId`); }})) invitationId: string,
310+
@Body() acceptRejectInvitation: AcceptRejectInvitationDto,
311+
@Param(
312+
'invitationId',
313+
TrimStringParamPipe,
314+
new ParseUUIDPipe({
315+
exceptionFactory: (): Error => {
316+
throw new BadRequestException(`Invalid format for InvitationId`);
317+
}
318+
})
319+
)
320+
invitationId: string,
311321
@User() reqUser: user,
312322
@Res() res: Response
313323
): Promise<Response> {
@@ -320,13 +330,13 @@ export class UserController {
320330
};
321331
return res.status(HttpStatus.CREATED).json(finalResponse);
322332
}
323-
333+
324334
/**
325-
* Updates the user profile.
326-
*
327-
* @body UpdateUserProfileDto
328-
* @returns A response indicating the success of the update operation.
329-
*/
335+
* Updates the user profile.
336+
*
337+
* @body UpdateUserProfileDto
338+
* @returns A response indicating the success of the update operation.
339+
*/
330340
@Put('/')
331341
@ApiOperation({
332342
summary: 'Update user profile',
@@ -343,48 +353,48 @@ export class UserController {
343353
const userId = reqUser.id;
344354
updateUserProfileDto.id = userId;
345355
await this.userService.updateUserProfile(updateUserProfileDto);
346-
356+
347357
const finalResponse: IResponse = {
348358
statusCode: HttpStatus.OK,
349359
message: ResponseMessages.user.success.update
350360
};
351361
return res.status(HttpStatus.OK).json(finalResponse);
352362
}
353363

354-
/**
364+
/**
355365
* @body AddPasskeyDetailsDto
356366
* @returns User's profile update status
357367
*/
358-
359-
360-
@Put('/password/:email')
361-
@ApiOperation({ summary: 'Store user password details', description: 'Securely store and update the user’s password details.' })
362-
@ApiExcludeEndpoint()
363-
@ApiBearerAuth()
364-
@UseGuards(AuthGuard('jwt'), UserAccessGuard)
365-
366-
async addPasskey(
367-
@Body() userInfo: AddPasskeyDetailsDto,
368-
@User() reqUser: user,
369-
@Res() res: Response
370-
): Promise<Response> {
371-
372-
const userDetails = await this.userService.addPasskey(reqUser.email, userInfo);
373-
const finalResponse = {
374-
statusCode: HttpStatus.OK,
375-
message: ResponseMessages.user.success.update,
376-
data: userDetails
377-
};
378-
379-
return res.status(HttpStatus.OK).json(finalResponse);
380-
}
368+
369+
@Put('/password/:email')
370+
@ApiOperation({
371+
summary: 'Store user password details',
372+
description: 'Securely store and update the user’s password details.'
373+
})
374+
@ApiExcludeEndpoint()
375+
@ApiBearerAuth()
376+
@UseGuards(AuthGuard('jwt'), UserAccessGuard)
377+
async addPasskey(
378+
@Body() userInfo: AddPasskeyDetailsDto,
379+
@User() reqUser: user,
380+
@Res() res: Response
381+
): Promise<Response> {
382+
const userDetails = await this.userService.addPasskey(reqUser.email, userInfo);
383+
const finalResponse = {
384+
statusCode: HttpStatus.OK,
385+
message: ResponseMessages.user.success.update,
386+
data: userDetails
387+
};
388+
389+
return res.status(HttpStatus.OK).json(finalResponse);
390+
}
381391

382392
/**
383-
* Updates platform settings.
384-
* @body UpdatePlatformSettingsDto
385-
*
386-
* @returns Status of the update operation.
387-
*/
393+
* Updates platform settings.
394+
* @body UpdatePlatformSettingsDto
395+
*
396+
* @returns Status of the update operation.
397+
*/
388398
@Put('/platform-settings')
389399
@ApiOperation({
390400
summary: 'Update platform settings',
@@ -406,4 +416,4 @@ export class UserController {
406416

407417
return res.status(HttpStatus.OK).json(finalResponse);
408418
}
409-
}
419+
}

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import { NATSClient } from '@credebl/common/NATSClient';
1515

1616
@Injectable()
1717
export class UserService extends BaseService {
18-
constructor(@Inject('NATS_CLIENT') private readonly serviceProxy: ClientProxy, private readonly natsClient : NATSClient) {
18+
constructor(
19+
@Inject('NATS_CLIENT') private readonly serviceProxy: ClientProxy,
20+
private readonly natsClient: NATSClient
21+
) {
1922
super('User Service');
2023
}
2124

@@ -25,8 +28,8 @@ export class UserService extends BaseService {
2528
}
2629

2730
async getPublicProfile(username: string): Promise<object> {
28-
const payload = { username };
29-
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-public-profile', payload);
31+
const payload = { username };
32+
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-public-profile', payload);
3033
}
3134

3235
async updateUserProfile(updateUserProfileDto: UpdateUserProfileDto): Promise<user> {
@@ -43,24 +46,19 @@ export class UserService extends BaseService {
4346
const payload = { id };
4447
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-user-by-keycloak', payload);
4548
}
46-
49+
4750
async invitations(id: string, status: string, getAllInvitationsDto: GetAllInvitationsDto): Promise<IUserInvitations> {
4851
const { pageNumber, pageSize, search } = getAllInvitationsDto;
4952
const payload = { id, status, pageNumber, pageSize, search };
5053
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-org-invitations', payload);
5154
}
5255

53-
async acceptRejectInvitaion(
54-
acceptRejectInvitation: AcceptRejectInvitationDto,
55-
userId: string
56-
): Promise<string> {
56+
async acceptRejectInvitaion(acceptRejectInvitation: AcceptRejectInvitationDto, userId: string): Promise<string> {
5757
const payload = { acceptRejectInvitation, userId };
5858
return this.natsClient.sendNatsMessage(this.serviceProxy, 'accept-reject-invitations', payload);
5959
}
6060

61-
async get(
62-
paginationDto:PaginationDto
63-
): Promise<object> {
61+
async get(paginationDto: PaginationDto): Promise<object> {
6462
const { pageNumber, pageSize, search } = paginationDto;
6563
const payload = { pageNumber, pageSize, search };
6664
return this.natsClient.sendNatsMessage(this.serviceProxy, 'fetch-users', payload);

apps/organization/src/organization.service.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ import { DeleteOrgInvitationsEmail } from '../templates/delete-organization-invi
6464
import { IOrgRoles } from 'libs/org-roles/interfaces/org-roles.interface';
6565
import { NATSClient } from '@credebl/common/NATSClient';
6666
import { UserRepository } from 'apps/user/repositories/user.repository';
67+
import * as jwt from 'jsonwebtoken';
68+
6769
@Injectable()
6870
export class OrganizationService {
6971
constructor(
@@ -708,17 +710,24 @@ export class OrganizationService {
708710
let addSessionDetails;
709711
// Fetch owner organization details for getting the user id
710712
const orgRoleDetails = await this.organizationRepository.getOrgAndOwnerUser(clientId);
713+
// called seprate method to delete exp session
714+
this.userRepository.deleteInactiveSessions(orgRoleDetails['user'].id);
715+
711716
// Fetch the total number of sessions for the requested user to check and restrict the creation of multiple sessions.
712717
const userSessionDetails = await this.userRepository.fetchUserSessions(orgRoleDetails['user'].id);
713718
if (Number(process.env.SESSIONS_LIMIT) <= userSessionDetails?.length) {
714719
throw new BadRequestException(ResponseMessages.user.error.sessionLimitReached);
715720
}
721+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
722+
const decodedToken: any = jwt.decode(authenticationResult?.access_token);
723+
const expiresAt = new Date(decodedToken.exp * 1000);
716724
// Session payload
717725
const sessionData = {
718726
sessionToken: authenticationResult?.access_token,
719727
userId: orgRoleDetails['user'].id,
720728
expires: authenticationResult?.expires_in,
721-
sessionType: SessionType.ORG_SESSION
729+
sessionType: SessionType.ORG_SESSION,
730+
expiresAt
722731
};
723732
// Note:
724733
// Fetch account details to check whether the requested user account exists

apps/user/interfaces/user.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ export interface ISession {
191191
type?: string;
192192
accountId?: string;
193193
sessionType?: string;
194+
expiresAt?: Date;
194195
}
195196

196197
export interface IUpdateAccountDetails {

apps/user/repositories/user.repository.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ export class UserRepository {
678678

679679
async createSession(tokenDetails: ISession): Promise<session> {
680680
try {
681-
const { sessionToken, userId, expires, refreshToken, accountId, sessionType } = tokenDetails;
681+
const { sessionToken, userId, expires, refreshToken, accountId, sessionType, expiresAt } = tokenDetails;
682682
const sessionResponse = await this.prisma.session.create({
683683
data: {
684684
id: tokenDetails.id,
@@ -687,7 +687,8 @@ export class UserRepository {
687687
userId,
688688
refreshToken,
689689
accountId,
690-
sessionType
690+
sessionType,
691+
expiresAt
691692
}
692693
});
693694
return sessionResponse;
@@ -987,4 +988,22 @@ export class UserRepository {
987988
throw error;
988989
}
989990
}
991+
992+
async deleteInactiveSessions(userId: string): Promise<Prisma.BatchPayload> {
993+
try {
994+
const response = await this.prisma.session.deleteMany({
995+
where: {
996+
expiresAt: {
997+
lt: new Date()
998+
},
999+
userId
1000+
}
1001+
});
1002+
this.logger.debug('Deleted inactive sessions::', response);
1003+
return response;
1004+
} catch (error) {
1005+
this.logger.error(`Error in deleting the in active sessions::${error.message}`);
1006+
throw error;
1007+
}
1008+
}
9901009
}

0 commit comments

Comments
 (0)