Skip to content
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

chore(): verify account certificate in obs #145

Merged
merged 3 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,39 +19,48 @@ public class JwtCertValidator {
private String SERVER_PUBLIC_KEY_JSON;

/**
* Validates the JWT by extracting the phoneNumberCert from its header and verifying signatures.
* Validates the JWT by extracting either accountJwt or phoneNumberJwt from its header and verifying signatures.
*
* @param jwtToken The JWT token string to validate.
* @return True if valid, false otherwise.
*/
public boolean validateJWT(String jwtToken) {
try {
SignedJWT signedJWT = parseJWT(jwtToken);
String phoneNumberCert = extractPhoneNumberCert(signedJWT);
SignedJWT phoneNumberCertJwt = parseJWT(phoneNumberCert);
String cert = extractCert(signedJWT);
SignedJWT certJwt = parseJWT(cert);
ECKey publicKey = loadPublicKey();

return verifySignature(phoneNumberCertJwt, publicKey);
return verifySignature(certJwt, publicKey);
} catch (Exception e) {
logger.error("Error during JWT validation: ", e);
return false;
}
}

private SignedJWT parseJWT(String jwt) throws ParseException, ParseException {
private SignedJWT parseJWT(String jwt) throws ParseException {
SignedJWT signedJWT = SignedJWT.parse(jwt);
logger.info("Parsed JWT: {}", signedJWT);
return signedJWT;
}

private String extractPhoneNumberCert(SignedJWT signedJWT) {
private String extractCert(SignedJWT signedJWT) {
// Check for accountJwt or phoneNumberJwt field in the JWT header
Object accountJwtObj = signedJWT.getHeader().toJSONObject().get("accountJwt");
if (accountJwtObj != null) {
String accountJwt = accountJwtObj.toString();
logger.info("Extracted accountJwt: {}", accountJwt);
return accountJwt;
}

Object phoneNumberCertObj = signedJWT.getHeader().toJSONObject().get("phoneNumberJwt");
if (phoneNumberCertObj == null) {
throw new IllegalArgumentException("Missing phoneNumberCert in JWT header.");
if (phoneNumberCertObj != null) {
String phoneNumberCert = phoneNumberCertObj.toString();
logger.info("Extracted phoneNumberJwt: {}", phoneNumberCert);
return phoneNumberCert;
}
String phoneNumberCert = phoneNumberCertObj.toString();
logger.info("Extracted phoneNumberCert: {}", phoneNumberCert);
return phoneNumberCert;

throw new IllegalArgumentException("Missing either accountJwt or phoneNumberJwt in JWT header.");
}

private ECKey loadPublicKey() throws ParseException {
Expand All @@ -67,12 +76,12 @@ private ECKey loadPublicKey() throws ParseException {
return publicKey;
}

private boolean verifySignature(SignedJWT phoneNumberCertJwt, ECKey publicKey) throws JOSEException {
JWSVerifier phoneNumberCertVerifier = new ECDSAVerifier(publicKey);
boolean isValid = phoneNumberCertJwt.verify(phoneNumberCertVerifier);
private boolean verifySignature(SignedJWT certJwt, ECKey publicKey) throws JOSEException {
JWSVerifier certVerifier = new ECDSAVerifier(publicKey);
boolean isValid = certJwt.verify(certVerifier);

if (!isValid) {
logger.error("phoneNumberCert signature validation failed.");
logger.error("JWT signature validation failed.");
} else {
logger.info("JWT validation successful.");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.adorsys.webank.obs.serviceimpl;

import com.adorsys.webank.obs.dto.BalanceRequest;
import com.adorsys.webank.obs.security.JwtCertValidator;
import com.adorsys.webank.obs.service.BalanceServiceApi;
import de.adorsys.webank.bank.api.domain.BalanceBO;
import de.adorsys.webank.bank.api.domain.BankAccountDetailsBO;
import de.adorsys.webank.bank.api.service.BankAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
Expand All @@ -16,29 +18,46 @@ public class BalanceServiceImpl implements BalanceServiceApi {

private final BankAccountService bankAccountService;

public BalanceServiceImpl(BankAccountService bankAccountService) {
@Autowired
private final JwtCertValidator jwtCertValidator;


public BalanceServiceImpl(BankAccountService bankAccountService, JwtCertValidator jwtCertValidator) {
this.bankAccountService = bankAccountService;
this.jwtCertValidator = jwtCertValidator;
}

@Override
public String getBalance(BalanceRequest balanceRequest, String accountCertificateJwt) {
String accountId = balanceRequest.getAccountID();
try {
boolean isValid = jwtCertValidator.validateJWT(accountCertificateJwt);

if (!isValid){
return "Invalid certificate or JWT. Account creation failed";
}
String accountId = balanceRequest.getAccountID();

BankAccountDetailsBO details = bankAccountService.getAccountDetailsById(
accountId,
LocalDateTime.now(),
true
);
BankAccountDetailsBO details = bankAccountService.getAccountDetailsById(
accountId,
LocalDateTime.now(),
true
);

if (details == null || details.getBalances() == null || details.getBalances().isEmpty()) {
return "Balance empty";
if (details == null || details.getBalances() == null || details.getBalances().isEmpty()) {
return "Balance empty";
}

// Assuming the first balance in the list is the latest balance
Optional<BalanceBO> latestBalance = details.getBalances().stream().findFirst();

return latestBalance.map(balance -> String.valueOf(balance.getAmount().getAmount()))
.orElse("Balance not available");
}
catch (Exception e) {
return "An error occurred while processing the request: " + e.getMessage();
}

// Assuming the first balance in the list is the latest balance
Optional<BalanceBO> latestBalance = details.getBalances().stream().findFirst();

return latestBalance.map(balance -> String.valueOf(balance.getAmount().getAmount()))
.orElse("Balance not available");
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import de.adorsys.webank.bank.api.domain.BalanceBO;
import de.adorsys.webank.bank.api.domain.BankAccountDetailsBO;
import de.adorsys.webank.bank.api.service.BankAccountService;
import com.adorsys.webank.obs.security.JwtCertValidator;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
Expand All @@ -28,6 +30,9 @@ class BalanceServiceImplTest {
@InjectMocks
private BalanceServiceImpl balanceService;

@Mock
private JwtCertValidator jwtCertValidator; // Mocking JwtCertValidator

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
Expand All @@ -38,7 +43,7 @@ void testGetBalance_WithValidBalance() {
// Arrange
BalanceRequest request = new BalanceRequest();
request.setAccountID("12345");
String accountCertificateJwt = "dummy-jwt-token"; // Provide a mock JWT token
String accountCertificateJwt = "valid-jwt-token"; // Provide a mock valid JWT token

AmountBO amount = new AmountBO();
amount.setCurrency(Currency.getInstance("XAF"));
Expand All @@ -50,73 +55,67 @@ void testGetBalance_WithValidBalance() {

when(bankAccountService.getAccountDetailsById(anyString(), any(LocalDateTime.class), anyBoolean()))
.thenReturn(accountDetails);
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(true);

// Act
String result = balanceService.getBalance(request, accountCertificateJwt);

// Assert
assertEquals("1000", result);
assertEquals("1000", result, "Balance should be '1000' when account has a valid balance");
}

@Test
void testGetBalance_WithEmptyBalance() {
// Arrange
BalanceRequest request = new BalanceRequest();
request.setAccountID("12345");
String accountCertificateJwt = "dummy-jwt-token"; // Provide a mock JWT token
String accountCertificateJwt = "valid-jwt-token"; // Provide a mock valid JWT token

BankAccountDetailsBO accountDetails = new BankAccountDetailsBO();
accountDetails.setBalances(Collections.emptyList());

when(bankAccountService.getAccountDetailsById(anyString(), any(LocalDateTime.class), anyBoolean()))
.thenReturn(accountDetails);
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(true);

// Act
String result = balanceService.getBalance(request, accountCertificateJwt);

// Assert
assertEquals("Balance empty", result);
assertEquals("Balance empty", result, "Balance should be empty when no balance is found.");
}

@Test
void testGetBalance_WithNullAccountDetails() {
// Arrange
BalanceRequest request = new BalanceRequest();
request.setAccountID("12345");
String accountCertificateJwt = "dummy-jwt-token"; // Provide a mock JWT token
String accountCertificateJwt = "valid-jwt-token"; // Provide a mock valid JWT token

when(bankAccountService.getAccountDetailsById(anyString(), any(LocalDateTime.class), anyBoolean()))
.thenReturn(null);
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(true);

// Act
String result = balanceService.getBalance(request, accountCertificateJwt);

// Assert
assertEquals("Balance empty", result);
assertEquals("Balance empty", result, "Balance should be empty when account details are null.");
}

@Test
void testGetBalance_WithNoBalanceAvailable() {
void testGetBalance_WithInvalidJWT() {
// Arrange
BalanceRequest request = new BalanceRequest();
request.setAccountID("12345");
String accountCertificateJwt = "dummy-jwt-token"; // Provide a mock JWT token

AmountBO amount = new AmountBO();
amount.setCurrency(Currency.getInstance("XAF"));
amount.setAmount(new BigDecimal("1000"));
BalanceBO balance = new BalanceBO();
balance.setAmount(amount);
BankAccountDetailsBO accountDetails = new BankAccountDetailsBO();
accountDetails.setBalances(Collections.singletonList(balance));
String accountCertificateJwt = "invalid-jwt-token"; // Provide an invalid JWT token

when(bankAccountService.getAccountDetailsById(anyString(), any(LocalDateTime.class), anyBoolean()))
.thenReturn(accountDetails);
when(jwtCertValidator.validateJWT(accountCertificateJwt)).thenReturn(false);

// Act
String result = balanceService.getBalance(request, accountCertificateJwt);

// Assert
assertEquals("1000", result);
assertEquals("Invalid certificate or JWT. Account creation failed", result, "Should return error message for invalid JWT.");
}
}