From 2b1f702e6d0ece681ae63b3f4dce0ccbcf737455 Mon Sep 17 00:00:00 2001 From: Anurag Mittal <1321012+anurag4DSB@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:56:51 +0200 Subject: [PATCH] OSIS-165: cap getCredentials access-denied recovery to a single retry --- .../service/impl/ScalityOsisServiceImpl.java | 46 ++++++++++++------- .../impl/ScalityOsisServiceMiscTests.java | 21 ++++++++- 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/osis-core/src/main/java/com/scality/osis/service/impl/ScalityOsisServiceImpl.java b/osis-core/src/main/java/com/scality/osis/service/impl/ScalityOsisServiceImpl.java index 2d87dae..998679f 100644 --- a/osis-core/src/main/java/com/scality/osis/service/impl/ScalityOsisServiceImpl.java +++ b/osis-core/src/main/java/com/scality/osis/service/impl/ScalityOsisServiceImpl.java @@ -1262,30 +1262,44 @@ public AnonymousUser getAnonymousUser() { * @return the credentials */ public Credentials getCredentials(String accountID) { - Credentials credentials = null; try { - AssumeRoleRequest assumeRoleRequest = ScalityModelConverter.getAssumeRoleRequestForAccount(accountID, - appEnv.getAssumeRoleName()); - logger.debug("[Vault] Assume Role request:{}", assumeRoleRequest); - credentials = vaultAdmin.getTempAccountCredentials(assumeRoleRequest); - logger.debug("[Vault] Assume Role response received with access key:{}, expiration:{}", - credentials.getAccessKeyId(), credentials.getExpiration()); + return assumeRole(accountID); } catch (VaultServiceException e) { + if (!isAccessDenied(e)) { + throw e; + } + // if access denied, the osis role is not provisioned yet: set it up and retry once + logger.error(e.getReason() + ". Recreating the role"); + // Call get Account with Account ID to retrieve account name + AccountData account = vaultAdmin.getAccount(ScalityModelConverter.toGetAccountRequestWithID(accountID)); + asyncScalityOsisService.setupAssumeRole(accountID, account.getName()); - if (!StringUtils.isNullOrEmpty(e.getErrorCode()) && - ACCESS_DENIED.equals(e.getErrorCode())) { - // if access denied, invoke setupAssumeRole - logger.error(e.getReason() + ". Recreating the role"); - // Call get Account with Account ID to retrieve account name - AccountData account = vaultAdmin.getAccount(ScalityModelConverter.toGetAccountRequestWithID(accountID)); - asyncScalityOsisService.setupAssumeRole(accountID, account.getName()); - return getCredentials(accountID); + try { + return assumeRole(accountID); + } catch (VaultServiceException retryError) { + if (isAccessDenied(retryError)) { + logger.error("Assume role still access-denied for account {} after recreating the role; " + + "giving up to avoid unbounded retries", accountID); + } + throw retryError; } - throw e; } + } + + private Credentials assumeRole(String accountID) { + AssumeRoleRequest assumeRoleRequest = ScalityModelConverter.getAssumeRoleRequestForAccount(accountID, + appEnv.getAssumeRoleName()); + logger.debug("[Vault] Assume Role request:{}", assumeRoleRequest); + Credentials credentials = vaultAdmin.getTempAccountCredentials(assumeRoleRequest); + logger.debug("[Vault] Assume Role response received with access key:{}, expiration:{}", + credentials.getAccessKeyId(), credentials.getExpiration()); return credentials; } + private boolean isAccessDenied(VaultServiceException e) { + return !StringUtils.isNullOrEmpty(e.getErrorCode()) && ACCESS_DENIED.equals(e.getErrorCode()); + } + /** * Create osis credential osis s 3 credential. * diff --git a/osis-core/src/test/java/com/scality/osis/service/impl/ScalityOsisServiceMiscTests.java b/osis-core/src/test/java/com/scality/osis/service/impl/ScalityOsisServiceMiscTests.java index f51c071..df90799 100644 --- a/osis-core/src/test/java/com/scality/osis/service/impl/ScalityOsisServiceMiscTests.java +++ b/osis-core/src/test/java/com/scality/osis/service/impl/ScalityOsisServiceMiscTests.java @@ -26,7 +26,7 @@ import java.util.List; import java.util.Optional; -import static com.scality.osis.utils.ScalityConstants.IAM_PREFIX; +import static com.scality.osis.utils.ScalityConstants.*; import static com.scality.osis.utils.ScalityTestUtils.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; @@ -471,6 +471,25 @@ void testGetCredentials400() { // Verify the results } + @Test + void testGetCredentialsStopsRetryingWhenAccessDeniedPersists() { + // Setup: every assume-role attempt returns access-denied (persistent Vault misconfiguration) + when(vaultAdminMock.getTempAccountCredentials(any(AssumeRoleRequest.class))) + .thenThrow(new VaultServiceException(HttpStatus.FORBIDDEN, ACCESS_DENIED, + "User: backbeat is not allowed to assume role")); + + // Run the test: persistent access-denied must surface as a VaultServiceException, not recurse forever + final VaultServiceException error = assertThrows(VaultServiceException.class, + () -> scalityOsisServiceUnderTest.getCredentials(TEST_TENANT_ID)); + + // Verify the results + assertEquals(ACCESS_DENIED, error.getErrorCode(), "Invalid Error Code"); + // recovery routine (setupAssumeRole) is reached exactly once: it looks up the account name once + verify(vaultAdminMock, times(1)).getAccount(any(GetAccountRequestDTO.class)); + // exactly one recovery means one original attempt plus one retry, no unbounded recursion + verify(vaultAdminMock, times(2)).getTempAccountCredentials(any(AssumeRoleRequest.class)); + } + @Test void testSetupAssumeRoleAsync() {