Skip to content

Commit

Permalink
Merge pull request #32 from vishnuvinay89/all-saas-0.2-dev
Browse files Browse the repository at this point in the history
Task #235460 - Password change for learner API
  • Loading branch information
sudeeppr1998 authored Feb 21, 2025
2 parents d209d76 + 941b42b commit e4608c8
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 1 deletion.
86 changes: 86 additions & 0 deletions src/adapters/postgres/user-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,92 @@ export class PostgresUserService implements IServicelocator {
}
}

async learnerforgotPassword(request: any, body: any, response: Response<any, Record<string, any>>) {
const apiId = APIID.USER_FORGOT_PASSWORD;
const tenantId = request.headers.tenantid;
const decoded: any = jwt_decode(request.headers.authorization);

try {
// Check if User Exists
const userDetail = await this.usersRepository.findOne({
where: { username: body.userName }
});
if (!userDetail) {
return APIResponse.error(
response,
apiId,
API_RESPONSES.USERNAME_NOT_FOUND,
'User not found.',
HttpStatus.NOT_FOUND
);
}

// Validate Learner-Tenant & Admin-Tenant Mapping in a single function
await this.validateUserandTenantMapping(userDetail.userId, tenantId);
await this.validateUserandTenantMapping(decoded.sub, tenantId);

// Get Keycloak Admin Token
const keycloakResponse = await getKeycloakAdminToken();
const keyClocktoken = keycloakResponse.data.access_token;

// Check Role and Access
const role = await this.getUserRoles(decoded.sub, tenantId);
if (role.code === 'cohort_admin') {
const cohortIds = await this.getCohortIdsForTenant(decoded.sub, tenantId);
const learnercohorts = await this.cohortMemberRepository.find({
where: { userId: userDetail.userId, cohortId: In(cohortIds) }
});
if (learnercohorts.length === 0) {
return APIResponse.error(
response,
apiId,
'You don\'t have access to update the password of this learner',
'Unauthorized access attempt.',
HttpStatus.UNAUTHORIZED
);
}
}

// Validate new password
if (!body.newPassword || body.newPassword.length < 8) {
return APIResponse.error(
response,
apiId,
'New password does not meet security requirements.',
'Password too short.',
HttpStatus.BAD_REQUEST
);
}

// Reset Keycloak Password
await this.resetKeycloakPassword(
request,
userDetail,
keyClocktoken,
body.newPassword,
userDetail.userId
);

return APIResponse.success(
response,
apiId,
{},
HttpStatus.OK,
API_RESPONSES.FORGOT_PASSWORD_SUCCESS
);

} catch (error) {
console.error('Error in learnerforgotPassword:', error);
return APIResponse.error(
response,
apiId,
API_RESPONSES.INTERNAL_SERVER_ERROR,
`Error: ${error.message || error}`,
error.status || HttpStatus.INTERNAL_SERVER_ERROR
);
}
}

// Utility function to check user-tenant mapping
async validateUserandTenantMapping(userId: string, tenantId: string) {
let userTenantMapping = await this.userTenantMappingRepository.find({
Expand Down
11 changes: 11 additions & 0 deletions src/user/dto/passwordReset.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,15 @@ export class ForgotPasswordDto {
@IsString()
@IsNotEmpty()
token: string;
}

export class learnerForgotPasswordDto {

@IsString()
@IsNotEmpty()
newPassword: string;

@IsString()
@IsNotEmpty()
userName: string;
}
16 changes: 15 additions & 1 deletion src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ import { JwtAuthGuard } from "src/common/guards/keycloak.guard";
import { Request, Response } from "express";
import { AllExceptionsFilter } from "src/common/filters/exception.filter";
import { APIID } from "src/common/utils/api-id.config";
import { ForgotPasswordDto, ResetUserPasswordDto, SendPasswordResetLinkDto } from "./dto/passwordReset.dto";
import { ForgotPasswordDto, ResetUserPasswordDto, SendPasswordResetLinkDto,learnerForgotPasswordDto } from "./dto/passwordReset.dto";
import { PostgresUserService } from "src/adapters/postgres/user-adapter";

export interface UserData {
context: string;
// tenantId: string;
Expand All @@ -54,6 +56,7 @@ export interface UserData {
export class UserController {
constructor(
private userAdapter: UserAdapter,
private postgresUserService: PostgresUserService,
) { }

@UseFilters(new AllExceptionsFilter(APIID.USER_GET))
Expand Down Expand Up @@ -180,6 +183,17 @@ export class UserController {
return await this.userAdapter.buildUserAdapter().forgotPassword(request, reqBody, response)
}

@Post("/learner-forgot-password")
@ApiOkResponse({ description: 'Forgot password reset successfully.' })
@UsePipes(new ValidationPipe({ transform: true }))
public async learnerforgotPassword(
@Req() request: Request,
@Res() response: Response,
@Body() reqBody: learnerForgotPasswordDto
) {
return await this.postgresUserService.learnerforgotPassword(request, reqBody, response)
}

@UseFilters(new AllExceptionsFilter(APIID.USER_RESET_PASSWORD))
@Post("/reset-password")
@UseGuards(JwtAuthGuard)
Expand Down

0 comments on commit e4608c8

Please sign in to comment.