Skip to content

Commit

Permalink
remove exception (for security, expose minimal info)
Browse files Browse the repository at this point in the history
remove @datam classes are not rows in a database
consistent responses
remove deprecated MockBean
remove var, be explicit
remove @SneakyThrows in main
add @Sneakythrwows in test
reformat
  • Loading branch information
bhuism committed Jan 13, 2025
1 parent edf913e commit 7b6de69
Show file tree
Hide file tree
Showing 44 changed files with 285 additions and 426 deletions.
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package nl.appsource.configuration;

import jakarta.annotation.PostConstruct;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import nl.appsource.service.exception.IdentifierPrivateKeyException;
import nl.appsource.service.exception.TokenPrivateKeyException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Component
@ConfigurationProperties(prefix = "pseudoniemenservice")
@Data
@Getter
@Setter
@RequiredArgsConstructor
@Accessors(chain = true)
public final class PseudoniemenServiceProperties {

Expand All @@ -25,21 +27,15 @@ public final class PseudoniemenServiceProperties {
* This method performs a post-construction validation of the `PseudoniemenServiceProperties` object to ensure that
* the `tokenPrivateKey` and `identifierPrivateKey` are properly configured. If either of these properties is not set
* or is empty, specific exceptions are thrown:
* <p>
* - If `tokenPrivateKey` is null or empty, a {@link TokenPrivateKeyException} is thrown.
* - If `identifierPrivateKey` is null or empty, a {@link IdentifierPrivateKeyException} is thrown.
*
* @throws TokenPrivateKeyException if the `tokenPrivateKey` is missing or empty.
* @throws IdentifierPrivateKeyException if the `identifierPrivateKey` is missing or empty.
*/
@PostConstruct
public void validate() {

if (!StringUtils.hasText(tokenPrivateKey)) {
throw new TokenPrivateKeyException("Please set a private token key");
throw new RuntimeException("Please set a private token key");
}
if (!StringUtils.hasText(identifierPrivateKey)) {
throw new IdentifierPrivateKeyException("Please set a private identifier key");
throw new RuntimeException("Please set a private identifier key");
}
}
}
Expand Down
110 changes: 3 additions & 107 deletions src/main/java/nl/appsource/controller/GlobalExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
package nl.appsource.controller;

import lombok.extern.slf4j.Slf4j;
import nl.appsource.service.exception.IdentifierPrivateKeyException;
import nl.appsource.service.exception.InvalidOINException;
import nl.appsource.service.exception.InvalidWsIdentifierRequestTypeException;
import nl.appsource.service.exception.InvalidWsIdentifierTokenException;
import nl.appsource.service.exception.TokenPrivateKeyException;
import nl.appsource.service.exception.WsGetTokenProcessingException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

@Slf4j
@ControllerAdvice
Expand All @@ -25,107 +18,10 @@ public class GlobalExceptionHandler {
* @return a ResponseEntity containing a generic error message and an INTERNAL_SERVER_ERROR
* (500) status
*/
@ExceptionHandler(Exception.class)
@ExceptionHandler(Throwable.class)
@ResponseBody
public ResponseEntity<String> handleGenericException(final Exception ex) {

log.error("Unexpected error occurred", ex);
return new ResponseEntity<>(
"An unexpected error occurred: " + ex.getMessage(),
HttpStatus.INTERNAL_SERVER_ERROR
);
}

/**
* Handles exceptions of type IdentifierPrivateKeyException and returns an appropriate HTTP
* response with the exception message.
*
* @param ex the IdentifierPrivateKeyException to be handled
* @return a ResponseEntity containing the exception message and an UNPROCESSABLE_ENTITY (422)
* status
*/
@ExceptionHandler(IdentifierPrivateKeyException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleIdentifierPrivateKeyException(final IdentifierPrivateKeyException ex) {

return ex.getMessage();
}

/**
* Handles exceptions of type InvalidWsIdentifierRequestTypeException and returns the exception
* message. This handler sets the HTTP response status to UNPROCESSABLE_ENTITY (422).
*
* @param ex the InvalidWsIdentifierRequestTypeException to be handled
* @return the exception message as a String
*/
@ExceptionHandler(InvalidWsIdentifierRequestTypeException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleInvalidWsIdentifierRequestTypeException(
final InvalidWsIdentifierRequestTypeException ex) {

return ex.getMessage();
}

/**
* Handles exceptions of type InvalidWsIdentifierTokenException and returns an appropriate HTTP
* response with the exception message.
*
* @param ex the InvalidWsIdentifierTokenException to be handled
* @return the exception message as a String
*/
@ExceptionHandler(InvalidWsIdentifierTokenException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleInvalidWsIdentifierTokenException(
final InvalidWsIdentifierTokenException ex) {

return ex.getMessage();
public ResponseEntity<Void> handleGenericException(final Exception ex) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

/**
* Handles exceptions of type TokenPrivateKeyException and returns an appropriate HTTP response
* with the exception message.
*
* @param ex the TokenPrivateKeyException to be handled
* @return the exception message as a String
*/
@ExceptionHandler(TokenPrivateKeyException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleTokenPrivateKeyException(final TokenPrivateKeyException ex) {

return ex.getMessage();
}

/**
* Handles exceptions of type WsGetTokenProcessingException and returns an appropriate HTTP
* response with the exception message.
*
* @param ex the WsGetTokenProcessingException to be handled
* @return the exception message as a String
*/
@ExceptionHandler(WsGetTokenProcessingException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleWsGetTokenProcessingException(final WsGetTokenProcessingException ex) {

return ex.getMessage();
}

/**
* Handles exceptions of type InvalidOINException and returns the exception message. This
* handler sets the HTTP response status to UNPROCESSABLE_ENTITY (422).
*
* @param ex the InvalidOINException to be handled
* @return the exception message as a String
*/
@ExceptionHandler(InvalidOINException.class)
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public String handleInvalidOINException(final InvalidOINException ex) {

return ex.getMessage();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ public final class ExchangeIdentifierController implements ExchangeIdentifierApi
@Override
public ResponseEntity<WsExchangeIdentifierResponse> exchangeIdentifier(final String callerOIN,
final WsExchangeIdentifierRequest wsExchangeRequest) {

final var identifier = exchangeIdentifierService.exchangeIdentifier(wsExchangeRequest);
return ResponseEntity.ok(identifier);
try {
final WsExchangeIdentifierResponse identifier = exchangeIdentifierService.exchangeIdentifier(wsExchangeRequest);
return ResponseEntity.ok(identifier);
} catch (final Exception e) {
log.error("", e);
return ResponseEntity.unprocessableEntity().build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,13 @@ public final class ExchangeTokenController implements ExchangeTokenApi, VersionO
@Override
public ResponseEntity<WsExchangeTokenResponse> exchangeToken(final String callerOIN,
final WsExchangeTokenRequest wsExchangeTokenForIdentifierRequest) {

final var wsExchangeTokenResponse = exchangeTokenService.exchangeToken(callerOIN,
wsExchangeTokenForIdentifierRequest);
return ResponseEntity.ok(wsExchangeTokenResponse);
try {
final WsExchangeTokenResponse wsExchangeTokenResponse = exchangeTokenService.exchangeToken(callerOIN,
wsExchangeTokenForIdentifierRequest);
return ResponseEntity.ok(wsExchangeTokenResponse);
} catch (final Exception e) {
log.error("", e);
return ResponseEntity.unprocessableEntity().build();
}
}
}
16 changes: 11 additions & 5 deletions src/main/java/nl/appsource/controller/v1/GetTokenController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import nl.appsource.pseudoniemenservice.generated.server.api.GetTokenApi;
import nl.appsource.pseudoniemenservice.generated.server.model.WsGetTokenRequest;
import nl.appsource.pseudoniemenservice.generated.server.model.WsGetTokenResponse;
import nl.appsource.pseudoniemenservice.generated.server.model.WsIdentifier;
import nl.appsource.service.GetTokenService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
Expand All @@ -29,10 +30,15 @@ public final class GetTokenController implements GetTokenApi, VersionOneControll
public ResponseEntity<WsGetTokenResponse> getToken(final String callerOIN,
final WsGetTokenRequest wsGetTokenRequest) {

final var recipientOIN = wsGetTokenRequest.getRecipientOIN();
final var identifier = wsGetTokenRequest.getIdentifier();
final var wsGetTokenResponse = getTokenService.getWsGetTokenResponse(
recipientOIN, identifier);
return ResponseEntity.ok(wsGetTokenResponse);
final String recipientOIN = wsGetTokenRequest.getRecipientOIN();
final WsIdentifier identifier = wsGetTokenRequest.getIdentifier();
try {
final WsGetTokenResponse wsGetTokenResponse = getTokenService.getWsGetTokenResponse(
recipientOIN, identifier);
return ResponseEntity.ok(wsGetTokenResponse);
} catch (final Exception e) {
log.error("", e);
return ResponseEntity.unprocessableEntity().build();
}
}
}
10 changes: 7 additions & 3 deletions src/main/java/nl/appsource/model/Identifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


@Data
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@NoArgsConstructor
public class Identifier {

private String version;
Expand Down
10 changes: 8 additions & 2 deletions src/main/java/nl/appsource/model/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Data
@Getter
@Setter
@Builder
@AllArgsConstructor
@EqualsAndHashCode
@NoArgsConstructor
public class Token {

private String version;
Expand Down
23 changes: 14 additions & 9 deletions src/main/java/nl/appsource/service/ExchangeIdentifierService.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package nl.appsource.service;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import nl.appsource.pseudoniemenservice.generated.server.model.WsExchangeIdentifierRequest;
import nl.appsource.pseudoniemenservice.generated.server.model.WsExchangeIdentifierResponse;
import nl.appsource.service.exception.InvalidWsIdentifierRequestTypeException;
import nl.appsource.pseudoniemenservice.generated.server.model.WsIdentifier;
import nl.appsource.pseudoniemenservice.generated.server.model.WsIdentifierTypes;
import nl.appsource.service.map.BsnPseudoMapper;
import nl.appsource.service.map.PseudoBsnMapper;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.springframework.stereotype.Service;

import java.io.IOException;

import static nl.appsource.pseudoniemenservice.generated.server.model.WsIdentifierTypes.BSN;
import static nl.appsource.pseudoniemenservice.generated.server.model.WsIdentifierTypes.ORGANISATION_PSEUDO;

Expand All @@ -31,21 +34,23 @@ public final class ExchangeIdentifierService {
* @return A {@link WsExchangeIdentifierResponse} containing the exchanged identifier. Returns
* null if no appropriate mapping exists for the provided inputs.
*/
@SneakyThrows
public WsExchangeIdentifierResponse exchangeIdentifier(
final WsExchangeIdentifierRequest wsExchangeIdentifierForIdentifierRequest) {
final WsExchangeIdentifierRequest wsExchangeIdentifierForIdentifierRequest) throws InvalidCipherTextException, IOException {

final WsIdentifier wsIdentifierRequest = wsExchangeIdentifierForIdentifierRequest.getIdentifier();
final String recipientOIN = wsExchangeIdentifierForIdentifierRequest.getRecipientOIN();
final WsIdentifierTypes recipientIdentifierType = wsExchangeIdentifierForIdentifierRequest.getRecipientIdentifierType();

final var wsIdentifierRequest = wsExchangeIdentifierForIdentifierRequest.getIdentifier();
final var recipientOIN = wsExchangeIdentifierForIdentifierRequest.getRecipientOIN();
final var recipientIdentifierType = wsExchangeIdentifierForIdentifierRequest.getRecipientIdentifierType();
if (BSN.equals(wsIdentifierRequest.getType()) && ORGANISATION_PSEUDO.equals(
recipientIdentifierType)) {
// BSN to ORG_PSEUDO
return bsnPseudoMapper.map(wsIdentifierRequest.getValue(), recipientOIN);
} else if (ORGANISATION_PSEUDO.equals(wsIdentifierRequest.getType()) && BSN.equals(
recipientIdentifierType)) {
// ORG_PSEUDO to BSN
return pseudoBsnMapper.map(wsIdentifierRequest.getValue(), recipientOIN);
} else {
throw new RuntimeException("Unsupported types for convertion");
}
throw new InvalidWsIdentifierRequestTypeException(
"Invalid WsIdentifierRequest type cannot be processed.");
}
}
28 changes: 19 additions & 9 deletions src/main/java/nl/appsource/service/ExchangeTokenService.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
package nl.appsource.service;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import nl.appsource.model.Token;
import nl.appsource.pseudoniemenservice.generated.server.model.WsExchangeTokenRequest;
import nl.appsource.pseudoniemenservice.generated.server.model.WsExchangeTokenResponse;
import nl.appsource.service.crypto.AesGcmCryptographerService;
import nl.appsource.service.crypto.TokenConverter;
import nl.appsource.service.exception.InvalidOINException;
import nl.appsource.service.exception.InvalidWsIdentifierTokenException;
import nl.appsource.service.map.BsnTokenMapper;
import nl.appsource.service.map.OrganisationPseudoTokenMapper;
import nl.appsource.service.validate.OINValidator;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

@Slf4j
@RequiredArgsConstructor
@RestController
Expand All @@ -39,24 +46,27 @@ public final class ExchangeTokenService {
* @throws InvalidWsIdentifierTokenException if the identifier type in the request is invalid or
* cannot be processed
*/
@SneakyThrows
public WsExchangeTokenResponse exchangeToken(final String callerOIN,
final WsExchangeTokenRequest wsExchangeTokenForIdentifierRequest) {
final WsExchangeTokenRequest wsExchangeTokenForIdentifierRequest)
throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, IOException, InvalidCipherTextException {

final var encodedToken = aesGcmCryptographerService.decrypt(
final String encodedToken = aesGcmCryptographerService.decrypt(
wsExchangeTokenForIdentifierRequest.getToken(), callerOIN);
final var token = tokenConverter.deSerialize(encodedToken);

final Token token = tokenConverter.deSerialize(encodedToken);

if (!oinValidator.isValid(callerOIN, token)) {
throw new InvalidOINException("CallerOIN and token are mismatched.");
throw new RuntimeException("CallerOIN and token are mismatched.");
}

switch (wsExchangeTokenForIdentifierRequest.getIdentifierType()) {
case BSN -> {
return bsnTokenMapper.map(token);
}
case ORGANISATION_PSEUDO -> {
return organisationPseudoTokenMapper.map(callerOIN, token);
}
default -> throw new InvalidWsIdentifierTokenException(
default -> throw new RuntimeException(
"Invalid identifier cannot be processed.");
}
}
Expand Down
Loading

0 comments on commit 7b6de69

Please sign in to comment.