diff --git a/smp-angular/src/app/common/enums/smp-error-code.enum.ts b/smp-angular/src/app/common/enums/smp-error-code.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..177211d242a58ec36a61e63031b65358f18194e1 --- /dev/null +++ b/smp-angular/src/app/common/enums/smp-error-code.enum.ts @@ -0,0 +1,10 @@ +/** + * The used SMP error codes in the application. The SMP can send different error codes in the repponse, + * but the application only uses a subset of them. + * + * @since 5.1 + * @author Joze RIHTARSIC + */ +export enum SmpErrorCode { + ERROR_CODE_INVALID_NEW_PASSWORD = 'SMP:010', +} diff --git a/smp-angular/src/app/security/security.service.ts b/smp-angular/src/app/security/security.service.ts index cac431f82907de54a52c53f1112a753c227f5049..edd2e2671383f1dbd21f40515e86fe0f5fe80984 100644 --- a/smp-angular/src/app/security/security.service.ts +++ b/smp-angular/src/app/security/security.service.ts @@ -15,6 +15,7 @@ import {MatDialog} from "@angular/material/dialog"; import {Router} from "@angular/router"; import {TranslateService} from "@ngx-translate/core"; import {WindowSpinnerService} from "../common/services/window-spinner.service"; +import {SmpErrorCode} from "../common/enums/smp-error-code.enum"; @Injectable() export class SecurityService { @@ -122,7 +123,11 @@ export class SecurityService { }, // completeHandler error: (error: any) => { this.windowSpinnerService.showSpinner = false; - this.router.navigate(['/login']); + // if the error is not related to the password, we redirect to the login page + console + if (error.error?.errorCode !== SmpErrorCode.ERROR_CODE_INVALID_NEW_PASSWORD) { + this.router.navigate(['/login']); + } this.alertService.error(error); }, // errorHandler next: async () => { @@ -194,13 +199,15 @@ export class SecurityService { let subject = new ReplaySubject<boolean>(); if (callServer) { //we get the username from the server to trigger the redirection to the login screen in case the user is not authenticated - this.getCurrentUsernameFromServer().subscribe((user: User) => { - if (!user) { - this.clearLocalStorage(); + this.getCurrentUsernameFromServer().subscribe({ + next: (user: User) => { + if (!user) { + this.clearLocalStorage(); + } + subject.next(user !== null); + }, error: (user: any) => { + subject.next(false); } - subject.next(user !== null); - }, (user: string) => { - subject.next(false); }); } else { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/exceptions/ErrorResponseRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/exceptions/ErrorResponseRO.java index 3ba836cfc21c9f1e0409ca95b208010c152d56cd..8e3f75e6ba717b7be3eda5fd42464bab9c8990a1 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/exceptions/ErrorResponseRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/exceptions/ErrorResponseRO.java @@ -28,6 +28,7 @@ import java.util.Objects; */ public class ErrorResponseRO { protected String businessCode; + protected String errorCode; protected String errorDescription; protected String errorUniqueId; @@ -67,6 +68,13 @@ public class ErrorResponseRO { this.errorDescription = value; } + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } public String getErrorUniqueId() { return errorUniqueId; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java index cd351e9e3cd43844622f735facca38eefd45af7a..1bbda1d3f3be80a85f64e156ae6be83f5b37af5e 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java @@ -34,6 +34,9 @@ public enum ErrorCode { UNAUTHORIZED_INVALID_IDENTIFIER(401, "SMP:005",ErrorBusinessCode.UNAUTHORIZED, "Invalid entity identifier! User not authorized to access the entity data"), UNAUTHORIZED_INVALID_RESET_TOKEN(401, "SMP:006",ErrorBusinessCode.UNAUTHORIZED, "The reset token it is invalid or not active any more. Please try to reset your password again."), + USER_CHANGE_INVALID_NEW_CREDENTIAL(400, "SMP:010",ErrorBusinessCode.INVALID_INPUT_DATA, "Password change failed. %s"), + + INVALID_ENCODING (500, "SMP:100",ErrorBusinessCode.TECHNICAL, "Unsupported or invalid encoding for %s!"), SML_INVALID_IDENTIFIER (400,"SMP:101",ErrorBusinessCode.FORMAT_ERROR,"Malformed identifier, scheme and id should be delimited by double colon: %s "), @@ -79,6 +82,7 @@ public enum ErrorCode { // SML integration SML_INTEGRATION_EXCEPTION (500,"SMP:150",ErrorBusinessCode.TECHNICAL,"SML integration error! Error: %s "), XML_SIGNING_EXCEPTION (500,"SMP:500",ErrorBusinessCode.TECHNICAL,"Error occurred while signing response!"), + INTERNAL_ERROR_GENERIC (500,"SMP:501",ErrorBusinessCode.TECHNICAL, "Internal error!"), JAXB_INITIALIZATION (500,"SMP:511",ErrorBusinessCode.TECHNICAL, "Could not create Unmarshaller for class [%s]!"), XML_PARSE_EXCEPTION (500,"SMP:512",ErrorBusinessCode.TECHNICAL, "Error occurred while parsing input stream for [%s]. Error: %s!"), INVALID_REQUEST(400,"SMP:513",ErrorBusinessCode.TECHNICAL, "Invalid request [%s]. Error: %s!"), diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java index 859f922ac81eff2057296be36de3a34d924b331a..f9c906f2a546ce6a6de09e39d7b0d210fc1a1eae 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java @@ -366,23 +366,24 @@ public class CredentialService { // retrieve user Optional credentials by username Optional<DBCredential> optCredential = getActiveCredentialsForUsernameToReset(username, false); if (!optCredential.isPresent()) { + LOG.warn("User [{}] does not have active reset token!", username); throw UNAUTHORIZED_INVALID_RESET_TOKEN; } DBCredential dbCredential = optCredential.get(); if (!resetToken.equals(dbCredential.getResetToken())) { - LOG.warn("User [{}] reset token does not match the active reset token! The request is ignored", username); - return; + LOG.warn("User [{}] reset token does not match the active reset token!", username); + throw UNAUTHORIZED_INVALID_RESET_TOKEN; } Pattern pattern = configurationService.getPasswordPolicyRexExp(); if (pattern != null && !pattern.matcher(newPassword).matches()) { LOG.info(SMPLogger.SECURITY_MARKER, "Change/set password failed because it does not match password policy!: [{}]", username); - throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "PasswordChange", configurationService.getPasswordPolicyValidationMessage()); + throw new SMPRuntimeException(ErrorCode.USER_CHANGE_INVALID_NEW_CREDENTIAL, configurationService.getPasswordPolicyValidationMessage()); } if (StringUtils.isNotBlank(dbCredential.getValue()) && BCrypt.checkpw(newPassword, dbCredential.getValue())) { LOG.info(SMPLogger.SECURITY_MARKER, "Change/set password failed because 'new' password match the old password for user: [{}]", username); - throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "PasswordChange", configurationService.getPasswordPolicyValidationMessage()); + throw new SMPRuntimeException(ErrorCode.USER_CHANGE_INVALID_NEW_CREDENTIAL, configurationService.getPasswordPolicyValidationMessage()); } OffsetDateTime now = OffsetDateTime.now(); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java index ef65571b9dad35753b0143a427647eb613da80a7..6aa5f34a0c633d7a90319602ae9f8cb2a1c012e5 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java @@ -114,7 +114,6 @@ public class SMPAuthenticationService { credentialService.delayResponse(CredentialType.USERNAME_PASSWORD, startTime); throw e; } - credentialService.validatePasswordResetToken(resetToken); } public void logout(HttpServletRequest request, HttpServletResponse response) { diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java index 7f114ab42bfa7cbaf1a38325eff69d7ed7001d4f..7cf2c175b66421ddc4f6213175949058a3ad4bcc 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/AbstractErrorControllerAdvice.java @@ -21,9 +21,9 @@ package eu.europa.ec.edelivery.smp.error; import eu.europa.ec.dynamicdiscovery.exception.MalformedIdentifierException; import eu.europa.ec.edelivery.smp.data.ui.exceptions.ErrorResponseRO; -import eu.europa.ec.edelivery.smp.error.exceptions.SMPResponseStatusException; import eu.europa.ec.edelivery.smp.exceptions.BadRequestException; import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode; +import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,10 +43,7 @@ abstract class AbstractErrorControllerAdvice { ResponseEntity response; if (runtimeException instanceof SMPRuntimeException) { SMPRuntimeException ex = (SMPRuntimeException)runtimeException; - response = buildAndLog(HttpStatus.resolve(ex.getErrorCode().getHttpCode()), ex.getErrorCode().getErrorBusinessCode(), ex.getMessage(), ex); - } else if (runtimeException instanceof SMPResponseStatusException ){ - SMPResponseStatusException ex = (SMPResponseStatusException)runtimeException; - response = buildAndLog(ex.getStatus(), ex.getErrorBusinessCode(), ex.getMessage(), ex); + response = buildAndLog(HttpStatus.resolve(ex.getErrorCode().getHttpCode()), ex.getErrorCode(), ex.getMessage(), ex); } else if (runtimeException instanceof AuthenticationException ){ AuthenticationException ex = (AuthenticationException)runtimeException; response = buildAndLog(UNAUTHORIZED, ErrorBusinessCode.UNAUTHORIZED, ex.getMessage(), ex); @@ -66,7 +63,7 @@ abstract class AbstractErrorControllerAdvice { } - String errorCodeId = response.getBody() instanceof ErrorResponseRO? + String errorCodeId = response.getBody()!=null && response.getBody() instanceof ErrorResponseRO? ((ErrorResponseRO) response.getBody()).getErrorUniqueId(): null; @@ -74,5 +71,16 @@ abstract class AbstractErrorControllerAdvice { return response; } - abstract ResponseEntity buildAndLog(HttpStatus status, ErrorBusinessCode businessCode, String msg, Exception exception); + + ResponseEntity buildAndLog(HttpStatus status, ErrorBusinessCode businessCode, String msg, Exception exception) { + return buildAndLog(status, ErrorCode.INTERNAL_ERROR_GENERIC, businessCode, msg, exception); + } + + ResponseEntity buildAndLog(HttpStatus status, ErrorCode errorCode, String msg, Exception exception) { + return buildAndLog(status, errorCode, errorCode.getErrorBusinessCode(), msg, exception); + } + + abstract ResponseEntity buildAndLog(HttpStatus status, ErrorCode errorCode, + ErrorBusinessCode businessCode, + String msg, Exception exception); } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ErrorResponseBuilder.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ErrorResponseBuilder.java index 820d148b70cde0eac99e26a7243b063f19dc0a3e..90378a504d97db1bc630a20618697b97db31a985 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ErrorResponseBuilder.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ErrorResponseBuilder.java @@ -24,6 +24,7 @@ package eu.europa.ec.edelivery.smp.error; import eu.europa.ec.edelivery.smp.data.ui.exceptions.ErrorResponseRO; import eu.europa.ec.edelivery.smp.error.xml.ErrorResponse; import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode; +import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; @@ -42,11 +43,10 @@ import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; */ public class ErrorResponseBuilder { - private static final Logger LOG = LoggerFactory.getLogger(ErrorResponseBuilder.class); - public static final MediaType CONTENT_TYPE_TEXT_XML_UTF8 = MediaType.valueOf("text/xml; charset=UTF-8"); private HttpStatus status = INTERNAL_SERVER_ERROR; private ErrorBusinessCode errorBusinessCode = TECHNICAL; + private ErrorCode errorCode = ErrorCode.INTERNAL_ERROR_GENERIC; private String strErrorDescription = "Unexpected technical error occurred."; private static String getErrorUniqueId() { @@ -78,6 +78,7 @@ public class ErrorResponseBuilder { public ErrorResponseRO buildJSonBody() { ErrorResponseRO err = new ErrorResponseRO(); err.setBusinessCode(errorBusinessCode.name()); + err.setErrorCode(errorCode.getErrorCode()); err.setErrorDescription(strErrorDescription); err.setErrorUniqueId(getErrorUniqueId()); return err; @@ -88,6 +89,11 @@ public class ErrorResponseBuilder { return this; } + public ErrorResponseBuilder errorCode(ErrorCode errorCode) { + this.errorCode = errorCode; + return this; + } + public ErrorResponseBuilder errorDescription(String newErrorDescription) { this.strErrorDescription = newErrorDescription; return this; diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ServiceErrorControllerAdvice.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ServiceErrorControllerAdvice.java index 06df983531493c86f598aff784fd63ec943dead8..9c1161e710a6ddcb20b69dfb918a30a4ee97e955 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ServiceErrorControllerAdvice.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/ServiceErrorControllerAdvice.java @@ -19,10 +19,10 @@ package eu.europa.ec.edelivery.smp.error; -import eu.europa.ec.edelivery.smp.error.exceptions.SMPResponseStatusException; import eu.europa.ec.edelivery.smp.error.xml.ErrorResponse; import eu.europa.ec.edelivery.smp.exceptions.BadRequestException; import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode; +import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -44,7 +44,7 @@ import static org.springframework.http.HttpStatus.UNAUTHORIZED; public class ServiceErrorControllerAdvice extends AbstractErrorControllerAdvice { @Override - @ExceptionHandler({RuntimeException.class, SMPRuntimeException.class, SMPResponseStatusException.class, AuthenticationException.class,}) + @ExceptionHandler({RuntimeException.class, SMPRuntimeException.class, AuthenticationException.class,}) public ResponseEntity handleRuntimeException(RuntimeException ex) { return super.handleRuntimeException(ex); } @@ -64,16 +64,16 @@ public class ServiceErrorControllerAdvice extends AbstractErrorControllerAdvice return buildAndLog(UNAUTHORIZED, ErrorBusinessCode.UNAUTHORIZED, ex.getMessage() + " - Only SMP Admin or owner of given ServiceGroup is allowed to perform this action", ex); } - ResponseEntity buildAndLog(HttpStatus status, ErrorBusinessCode businessCode, String msg, Exception exception) { + ResponseEntity buildAndLog(HttpStatus status, ErrorCode errorCode, ErrorBusinessCode businessCode, String msg, Exception exception) { ResponseEntity response = ErrorResponseBuilder.status(status) .businessCode(businessCode) + .errorCode(errorCode) .errorDescription(msg) .build(); String errorUniqueId = ((ErrorResponse) response.getBody()).getErrorUniqueId(); String logMsg = errorUniqueId == null ? "Null Error ID" : format("UI Error unique ID: %s", errorUniqueId); - LOG.warn(logMsg, exception); return response; } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/UIErrorControllerAdvice.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/UIErrorControllerAdvice.java index f35922153ba46c95a2c9d333b6cbd37edbbb8174..936294fc8f8a73aa85228435deb60831c6b100ce 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/UIErrorControllerAdvice.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/error/UIErrorControllerAdvice.java @@ -19,9 +19,9 @@ package eu.europa.ec.edelivery.smp.error; import eu.europa.ec.edelivery.smp.data.ui.exceptions.ErrorResponseRO; -import eu.europa.ec.edelivery.smp.error.exceptions.SMPResponseStatusException; import eu.europa.ec.edelivery.smp.exceptions.BadRequestException; import eu.europa.ec.edelivery.smp.exceptions.ErrorBusinessCode; +import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -47,17 +47,17 @@ public class UIErrorControllerAdvice extends AbstractErrorControllerAdvice { @ExceptionHandler({BadCredentialsException.class, RuntimeException.class, SMPRuntimeException.class, - SMPResponseStatusException.class, AuthenticationException.class, BadRequestException.class}) public ResponseEntity handleRuntimeException(RuntimeException ex) { return super.handleRuntimeException(ex); } - ResponseEntity buildAndLog(HttpStatus status, ErrorBusinessCode businessCode, String msg, Exception exception) { + protected ResponseEntity buildAndLog(HttpStatus status, ErrorCode errorCode, ErrorBusinessCode businessCode, String msg, Exception exception) { ResponseEntity response = ErrorResponseBuilder.status(status) .businessCode(businessCode) + .errorCode(errorCode) .errorDescription(msg) .buildJSon();