-
Notifications
You must be signed in to change notification settings - Fork 2.4k
FINERACT-2006: Forgot password on login page #5369
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| /** | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
| package org.apache.fineract.infrastructure.security.service; | ||
|
|
||
| import org.apache.fineract.useradministration.domain.AppUser; | ||
| import org.springframework.security.authentication.BadCredentialsException; | ||
| import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||
| import org.springframework.security.authentication.dao.DaoAuthenticationProvider; | ||
| import org.springframework.security.core.AuthenticationException; | ||
| import org.springframework.security.core.userdetails.UserDetails; | ||
|
|
||
| /** | ||
| * Supports authentication with either the permanent password or a non-expired temporary password. | ||
| */ | ||
| public class TemporaryPasswordAwareAuthenticationProvider extends DaoAuthenticationProvider { | ||
|
|
||
| @Override | ||
| @SuppressWarnings("java:S1874") | ||
| protected void additionalAuthenticationChecks(final UserDetails userDetails, final UsernamePasswordAuthenticationToken authentication) | ||
| throws AuthenticationException { | ||
| if (authentication.getCredentials() == null) { | ||
| throw new BadCredentialsException( | ||
| messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); | ||
| } | ||
|
|
||
| final String presentedPassword = authentication.getCredentials().toString(); | ||
| if (getPasswordEncoder().matches(presentedPassword, userDetails.getPassword())) { | ||
| return; | ||
| } | ||
|
|
||
| if (userDetails instanceof AppUser appUser && appUser.hasValidTemporaryPassword() | ||
| && getPasswordEncoder().matches(presentedPassword, appUser.getTemporaryPassword())) { | ||
| return; | ||
| } | ||
|
|
||
| throw new BadCredentialsException( | ||
| messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials")); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| /** | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you under the Apache License, Version 2.0 (the | ||
| * "License"); you may not use this file except in compliance | ||
| * with the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, | ||
| * software distributed under the License is distributed on an | ||
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
| * KIND, either express or implied. See the License for the | ||
| * specific language governing permissions and limitations | ||
| * under the License. | ||
| */ | ||
| package org.apache.fineract.useradministration.api; | ||
|
|
||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import io.swagger.v3.oas.annotations.media.Content; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import io.swagger.v3.oas.annotations.parameters.RequestBody; | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||
| import jakarta.ws.rs.Consumes; | ||
| import jakarta.ws.rs.POST; | ||
| import jakarta.ws.rs.Path; | ||
| import jakarta.ws.rs.Produces; | ||
| import jakarta.ws.rs.core.MediaType; | ||
| import jakarta.ws.rs.core.Response; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.apache.fineract.useradministration.service.ForgotPasswordService; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Path("/v1/password") | ||
| @Component | ||
| @Tag(name = "Password Management", description = "APIs for password management operations including forgot password functionality.") | ||
| @RequiredArgsConstructor | ||
| public class ForgotPasswordApiResource { | ||
|
|
||
| private final ForgotPasswordService forgotPasswordService; | ||
|
|
||
| @POST | ||
| @Path("/forgot") | ||
| @Consumes({ MediaType.APPLICATION_JSON }) | ||
| @Produces({ MediaType.APPLICATION_JSON }) | ||
| @Operation(summary = "Request password reset", description = """ | ||
| Requests a password reset for the user with the given email. | ||
| If the email exists and the user is active, a temporary password will be sent to the email address. | ||
| The temporary password expires in 1 hour.""") | ||
| @RequestBody(required = true, content = @Content(schema = @Schema(implementation = ForgotPasswordRequest.class))) | ||
| @ApiResponse(responseCode = "200", description = "OK") | ||
| public Response forgotPassword(final ForgotPasswordRequest request) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to crosscheck if there is any rate-limit or any safeguards against abuse
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The endpoint has several safeguards in many places but rate limiting is not implemnted. |
||
| this.forgotPasswordService.requestPasswordReset(request.email()); | ||
| return Response.ok().build(); | ||
| } | ||
|
|
||
| public record ForgotPasswordRequest(String email) { | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use textblock instead as it was introduced in 15+ version
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried using text blocks initially, but SpotBugs throws a VA_FORMAT_STRING_USES_NEWLINE error when using .formatted() with text blocks containing literal newlines. I've kept the string concatenation approach to match the existing
sendToUserAccount method pattern in the same file, which avoids the SpotBugs issue while maintaining consistency.