diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/ssa/jwtssa/SsaGetJwtClient.java b/jans-auth-server/client/src/main/java/io/jans/as/client/ssa/jwtssa/SsaGetJwtClient.java
new file mode 100644
index 00000000000..a08386a3118
--- /dev/null
+++ b/jans-auth-server/client/src/main/java/io/jans/as/client/ssa/jwtssa/SsaGetJwtClient.java
@@ -0,0 +1,64 @@
+/*
+ * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text.
+ *
+ * Copyright (c) 2020, Janssen Project
+ */
+
+package io.jans.as.client.ssa.jwtssa;
+
+import io.jans.as.client.BaseClient;
+import io.jans.as.model.config.Constants;
+import jakarta.ws.rs.HttpMethod;
+import jakarta.ws.rs.client.Invocation.Builder;
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+import org.jetbrains.annotations.NotNull;
+
+public class SsaGetJwtClient extends BaseClient
@@ -28,16 +34,12 @@ public class SsaContextBuilder {
* it internally call {@link io.jans.service.cdi.util.CdiUtil} and cannot be mocked
*
@@ -93,4 +94,22 @@ public Response validate(String jti) {
public Response revoke(String jti, Long orgId, HttpServletRequest httpRequest) {
return ssaRevokeAction.revoke(jti, orgId, httpRequest);
}
+
+ /**
+ * Get JWT from existing active SSA based on "jti".
+ *
+ *
+ * Method will return the following exceptions:
+ * - {@link WebApplicationException} with status {@code 401} if this functionality is not enabled, request has to have at least scope "ssa.admin".
+ * - {@link WebApplicationException} with status {@code 422} if the SSA does not exist, is expired or used.
+ * - {@link WebApplicationException} with status {@code 500} in case an uncontrolled error occurs when processing the method.
+ *
+ * This method returns {@link WebApplicationException} with status 422 if the SSA does not exist or if it is in
+ * state (expired, used or revoked).
+ * Otherwise it will return the valid SSA
+ *
+ * Method throws an {@link RuntimeException} if it fails to create the jwt + *
+ *+ * Method executes a postProcessor in case it has been sent in the execution context parameter. + *
+ * + * @param ssa Ssa + * @return Jwt with SSA structure + */ + public Jwt generateJwt(Ssa ssa) { try { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.fromString(appConfiguration.getSsaConfiguration().getSsaSigningAlg()); JwtSigner jwtSigner = new JwtSigner(appConfiguration, webKeysConfiguration, signatureAlgorithm, null, null, cryptoProvider); @@ -152,11 +180,7 @@ public Jwt generateJwt(Ssa ssa, ExecutionContext executionContext, WebKeysConfig jwt.getClaims().setClaim(SOFTWARE_ROLES.getName(), ssa.getAttributes().getSoftwareRoles()); jwt.getClaims().setClaim(GRANT_TYPES.getName(), ssa.getAttributes().getGrantTypes()); - Jwt jwr = jwtSigner.sign(); - if (executionContext.getPostProcessor() != null) { - executionContext.getPostProcessor().apply(jwr); - } - return jwr; + return jwtSigner.sign(); } catch (Exception e) { if (log.isErrorEnabled()) log.error("Failed to sign session jwt! " + e.getMessage(), e); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateAction.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateAction.java index 57bec1f8a7e..86f69416c30 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateAction.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateAction.java @@ -10,13 +10,11 @@ import io.jans.as.common.model.registration.Client; import io.jans.as.common.model.ssa.Ssa; import io.jans.as.common.model.ssa.SsaState; -import io.jans.as.common.service.AttributeService; import io.jans.as.common.service.common.InumService; import io.jans.as.model.common.CreatorType; import io.jans.as.model.common.FeatureFlagType; import io.jans.as.model.config.Constants; import io.jans.as.model.config.StaticConfiguration; -import io.jans.as.model.config.WebKeysConfiguration; import io.jans.as.model.configuration.AppConfiguration; import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.jwt.Jwt; @@ -73,18 +71,12 @@ public class SsaCreateAction { @Inject private AppConfiguration appConfiguration; - @Inject - private AttributeService attributeService; - @Inject private ModifySsaResponseService modifySsaResponseService; @Inject private SsaRestWebServiceValidator ssaRestWebServiceValidator; - @Inject - private WebKeysConfiguration webKeysConfiguration; - @Inject private SsaContextBuilder ssaContextBuilder; @@ -149,12 +141,12 @@ public Response create(String requestParams, HttpServletRequest httpRequest) { ssaService.persist(ssa); log.info("Ssa created: {}", ssa); - ModifySsaResponseContext context = ssaContextBuilder.buildModifySsaResponseContext(httpRequest, null, client, appConfiguration, attributeService); + ModifySsaResponseContext context = ssaContextBuilder.buildModifySsaResponseContext(httpRequest, client); Function+ * Method will return the following exceptions: + * - {@link WebApplicationException} with status {@code 401} if this functionality is not enabled, request has to have at least scope "ssa.admin". + * - {@link WebApplicationException} with status {@code 422} if the SSA does not exist, is expired or used. + * - {@link WebApplicationException} with status {@code 500} in case an uncontrolled error occurs when processing the method. + *
+ * + * @param jti Unique identifier + * @return {@link Response} with status {@code 200 (Ok)} and the body containing JWT of SSA. + */ + public Response getJwtSsa(String jti) { + log.debug("Attempting to get JWT of SSA, jti: {}", jti); + + errorResponseFactory.validateFeatureEnabled(FeatureFlagType.SSA); + Response.ResponseBuilder builder = Response.ok(); + try { + final Client client = ssaRestWebServiceValidator.getClientFromSession(); + ssaRestWebServiceValidator.checkScopesPolicy(client, SsaScopeType.SSA_ADMIN.getValue()); + + Ssa ssa = ssaRestWebServiceValidator.getValidSsaByJti(jti); + + Jwt jwt = ssaService.generateJwt(ssa); + JSONObject jsonResponse = ssaJsonService.getJSONObject(jwt.toString()); + builder.entity(ssaJsonService.jsonObjectToString(jsonResponse)); + + } catch (WebApplicationException e) { + if (log.isErrorEnabled()) { + log.error(e.getMessage(), e); + } + throw e; + + } catch (Exception e) { + log.error(e.getMessage(), e); + throw errorResponseFactory.createWebApplicationException(Response.Status.INTERNAL_SERVER_ERROR, SsaErrorResponseType.UNKNOWN_ERROR, "Unknown error"); + } + + builder.cacheControl(ServerUtil.cacheControl(true, false)); + builder.header(Constants.PRAGMA, Constants.NO_CACHE); + builder.type(MediaType.APPLICATION_JSON_TYPE); + return builder.build(); + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeAction.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeAction.java index c1d67f78075..9f5030a6e4b 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeAction.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeAction.java @@ -11,11 +11,9 @@ import io.jans.as.common.model.ssa.SsaState; import io.jans.as.model.common.FeatureFlagType; import io.jans.as.model.config.Constants; -import io.jans.as.model.configuration.AppConfiguration; import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.ssa.SsaErrorResponseType; import io.jans.as.model.ssa.SsaScopeType; -import io.jans.as.server.service.AttributeService; import io.jans.as.server.service.external.ModifySsaResponseService; import io.jans.as.server.service.external.context.ModifySsaResponseContext; import io.jans.as.server.ssa.ws.rs.SsaContextBuilder; @@ -59,12 +57,6 @@ public class SsaRevokeAction { @Inject private SsaContextBuilder ssaContextBuilder; - @Inject - private AppConfiguration appConfiguration; - - @Inject - private AttributeService attributeService; - /** * Revoked existing active SSA based on "jti" or "org_id". * @@ -113,7 +105,7 @@ public Response revoke(String jti, Long orgId, HttpServletRequest httpRequest) { log.info("Ssa jti: '{}' updated status to '{}'", ssa.getId(), ssa.getState().getValue()); } - ModifySsaResponseContext context = ssaContextBuilder.buildModifySsaResponseContext(httpRequest, null, client, appConfiguration, attributeService); + ModifySsaResponseContext context = ssaContextBuilder.buildModifySsaResponseContext(httpRequest, client); modifySsaResponseService.revoke(ssaList, context); } catch (WebApplicationException e) { @@ -136,7 +128,7 @@ public Response revoke(String jti, Long orgId, HttpServletRequest httpRequest) { /** * Validate "jti" or "org_id" parameters * - * @param jti Unique identifier + * @param jti Unique identifier * @param orgId Organization ID * @return true if the parameters are valid or false otherwise. */ diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateAction.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateAction.java index 18ec93e01d6..7a1b6e57769 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateAction.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateAction.java @@ -12,6 +12,7 @@ import io.jans.as.model.config.Constants; import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.ssa.SsaErrorResponseType; +import io.jans.as.server.ssa.ws.rs.SsaRestWebServiceValidator; import io.jans.as.server.ssa.ws.rs.SsaService; import io.jans.as.server.util.ServerUtil; import jakarta.ejb.Stateless; @@ -22,9 +23,6 @@ import jakarta.ws.rs.core.Response; import org.slf4j.Logger; -import java.util.Calendar; -import java.util.TimeZone; - /** * Provides the method to validate an existing SSA considering certain conditions. */ @@ -41,6 +39,9 @@ public class SsaValidateAction { @Inject private SsaService ssaService; + @Inject + private SsaRestWebServiceValidator ssaRestWebServiceValidator; + /** * Validates an existing SSA for a given "jti". * @@ -60,19 +61,18 @@ public Response validate(String jti) { errorResponseFactory.validateFeatureEnabled(FeatureFlagType.SSA); Response.ResponseBuilder builder = Response.ok(); try { - Ssa ssa = ssaService.findSsaByJti(jti); - if (ssa == null || - Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime().after(ssa.getExpirationDate()) || - !ssa.getState().equals(SsaState.ACTIVE)) { - log.warn("Ssa jti: '{}' is null or status (expired, used or revoked)", jti); - return ssaService.createUnprocessableEntityResponse().build(); - } + Ssa ssa = ssaRestWebServiceValidator.getValidSsaByJti(jti); if (ssa.getAttributes().getOneTimeUse()) { ssa.setState(SsaState.USED); ssaService.merge(ssa); log.info("Ssa jti: '{}', updated with status: {}", ssa.getId(), ssa.getState()); } + } catch (WebApplicationException e) { + if (log.isErrorEnabled()) { + log.error(e.getMessage(), e); + } + throw e; } catch (Exception e) { log.error(e.getMessage(), e); throw errorResponseFactory.createWebApplicationException(Response.Status.INTERNAL_SERVER_ERROR, SsaErrorResponseType.UNKNOWN_ERROR, "Unknown error"); diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidatorTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidatorTest.java index 297b647ebd3..bd7f14dc159 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidatorTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidatorTest.java @@ -1,6 +1,8 @@ package io.jans.as.server.ssa.ws.rs; import io.jans.as.common.model.registration.Client; +import io.jans.as.common.model.ssa.Ssa; +import io.jans.as.common.model.ssa.SsaState; import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.ssa.SsaErrorResponseType; import io.jans.as.model.ssa.SsaScopeType; @@ -17,8 +19,10 @@ import org.testng.annotations.Listeners; import org.testng.annotations.Test; +import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import static org.mockito.ArgumentMatchers.anyString; @@ -43,6 +47,9 @@ public class SsaRestWebServiceValidatorTest { @Mock private ScopeService scopeService; + @Mock + private SsaService ssaService; + @Test public void getClientFromSession_sessionClient_validClient() { SessionClient sessionClient = new SessionClient(); @@ -197,4 +204,52 @@ public void checkScopesPolicyListScope_clientAndScopeNotContains_unauthorizedRes verify(errorResponseFactory).createWebApplicationException(any(), any(), anyString()); verifyNoMoreInteractions(errorResponseFactory); } + + @Test + public void getValidSsaByJti_validJti_validSsa() { + String jti = "test-jti"; + Ssa ssa = new Ssa(); + ssa.setExpirationDate(Date.from(ZonedDateTime.now().plusHours(24).toInstant())); + ssa.setState(SsaState.ACTIVE); + when(ssaService.findSsaByJti(jti)).thenReturn(ssa); + + Ssa result = ssaRestWebServiceValidator.getValidSsaByJti(jti); + assertNotNull(result, "ssa is null"); + verifyNoInteractions(log); + } + + @Test + public void getValidSsaByJti_ssaNull_422Status() { + String jti = "test-jti"; + when(ssaService.findSsaByJti(jti)).thenReturn(null); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaRestWebServiceValidator.getValidSsaByJti(jti)); + assertEquals(ex.getResponse().getStatus(), 422); + verify(log).warn(anyString(), eq(jti)); + } + + @Test + public void getValidSsaByJti_ssaExpired_422Status() { + String jti = "test-jti"; + Ssa ssa = new Ssa(); + ssa.setExpirationDate(Date.from(ZonedDateTime.now().minusHours(24).toInstant())); + when(ssaService.findSsaByJti(jti)).thenReturn(ssa); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaRestWebServiceValidator.getValidSsaByJti(jti)); + assertEquals(ex.getResponse().getStatus(), 422); + verify(log).warn(anyString(), eq(jti)); + } + + @Test + public void getValidSsaByJti_ssaWithUsedStatus_422Status() { + String jti = "test-jti"; + Ssa ssa = new Ssa(); + ssa.setExpirationDate(Date.from(ZonedDateTime.now().plusHours(24).toInstant())); + ssa.setState(SsaState.USED); + when(ssaService.findSsaByJti(jti)).thenReturn(ssa); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaRestWebServiceValidator.getValidSsaByJti(jti)); + assertEquals(ex.getResponse().getStatus(), 422); + verify(log).warn(anyString(), eq(jti)); + } } \ No newline at end of file diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaServiceTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaServiceTest.java index 061959b2804..62bfc14ba0b 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaServiceTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaServiceTest.java @@ -1,8 +1,5 @@ package io.jans.as.server.ssa.ws.rs; -import com.nimbusds.jose.JOSEException; -import com.nimbusds.jose.jwk.JWK; -import com.nimbusds.jose.jwk.RSAKey; import io.jans.as.common.model.ssa.Ssa; import io.jans.as.common.model.ssa.SsaState; import io.jans.as.model.config.BaseDnConfiguration; @@ -10,22 +7,16 @@ import io.jans.as.model.config.WebKeysConfiguration; import io.jans.as.model.configuration.AppConfiguration; import io.jans.as.model.crypto.AbstractCryptoProvider; -import io.jans.as.model.crypto.signature.SignatureAlgorithm; -import io.jans.as.model.exception.CryptoProviderException; -import io.jans.as.model.jwk.Algorithm; -import io.jans.as.model.jwk.JSONWebKey; import io.jans.as.model.jwt.Jwt; import io.jans.as.model.jwt.JwtClaims; import io.jans.as.model.jwt.JwtHeader; import io.jans.as.model.ssa.SsaConfiguration; import io.jans.as.model.ssa.SsaScopeType; -import io.jans.as.model.util.Base64Util; import io.jans.as.server.model.common.ExecutionContext; import io.jans.orm.PersistenceEntryManager; import io.jans.orm.exception.EntryPersistenceException; import jakarta.ws.rs.core.Response; import org.apache.http.HttpStatus; -import org.json.JSONObject; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -35,9 +26,6 @@ import org.testng.annotations.Listeners; import org.testng.annotations.Test; -import java.security.*; -import java.security.interfaces.RSAPrivateKey; -import java.text.ParseException; import java.util.*; import static io.jans.as.model.ssa.SsaRequestParam.*; @@ -47,18 +35,6 @@ @Listeners(MockitoTestNGListener.class) public class SsaServiceTest { - private final String senderJwkJson = "{\n" + - " \"kty\": \"RSA\",\n" + - " \"d\": \"iSx-zxihgOITpEhz6WwGiiCZjxx597wqblhSYgFWa_bL9esLY3FT_Kq9sdvGPiI8QmObRxPZuTi4n3BVKYUWcfjVz3swq7VmESxnJJZE-vMI9NTaZ-CT2b4I-c3qwAsejhWagJf899I3MRtPOnyxMimyOw4_5YYvXjBkXkCMfCsbj5TBR3RbtMrUYzDMXsVT1EJ_7H76DPBFJx5JptsEAA17VMtqwvWhRutnPyQOftDGPxD-1aGgpteKOUCv7Lx-mFX-zV6nnPB8vmgTgaMqCbCFKSZI567p714gzWBkwnNdRHleX8wos8yZAGbdwGqqUz5x3iKKdn3c7U9TTU7DAQ\",\n" + - " \"e\": \"AQAB\",\n" + - " \"use\": \"sig\",\n" + - " \"kid\": \"1\",\n" + - " \"alg\": \"RS256\",\n" + - " \"n\": \"i6tdK2fREwykTUU-qkYkiSHgg9B31-8EjVCbH0iyrewY9s7_WYPT7I3argjcmiDkufnVfGGW0FadtO3br-Qgk_N2e9LqGMtjUoGMZKFS3fJhqjnLYDi_E5l2FYU_ilw4EXPsZJY0CaM7BxjwUBoCjopYrgvtdxA9G6gpGoAH4LopAkgX-gkawVLpB4NpLvA09FLF2OlYZL7aaybvM2Lz_IXEPa-LSOwLum80Et-_A1-YMx_Z767Iwl1pGTpgZ87jrDD1vEdMdiLcWFG3UIYAAIxtg6X23cvQVLMaXKpyV0USDCWRJrZYxEDgZngbDRj3Sd2-LnixPkMWAfo_D9lBVQ\"\n" + - "}"; - - private AbstractCryptoProvider cryptoProvider; - @Mock private Logger log; @@ -74,64 +50,16 @@ public class SsaServiceTest { @Mock private StaticConfiguration staticConfiguration; + @Mock + private WebKeysConfiguration webKeysConfiguration; + + @Mock + private AbstractCryptoProvider cryptoProvider; + private Ssa ssa; @BeforeMethod public void setUp() { - Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); - cryptoProvider = new AbstractCryptoProvider() { - - @Override - public JSONObject generateKey(Algorithm algorithm, Long expirationTime) throws CryptoProviderException { - return null; - } - - @Override - public JSONObject generateKey(Algorithm algorithm, Long expirationTime, int keyLength) throws CryptoProviderException { - return null; - } - - @Override - public String sign(String signingInput, String keyId, String sharedSecret, SignatureAlgorithm signatureAlgorithm) throws CryptoProviderException { - try { - RSAPrivateKey privateKey = ((RSAKey) JWK.parse(senderJwkJson)).toRSAPrivateKey(); - Signature signature = Signature.getInstance(signatureAlgorithm.getAlgorithm(), "BC"); - signature.initSign(privateKey); - signature.update(signingInput.getBytes()); - - return Base64Util.base64urlencode(signature.sign()); - } catch (JOSEException | ParseException | NoSuchAlgorithmException | NoSuchProviderException | - InvalidKeyException | SignatureException e) { - throw new CryptoProviderException(e); - } - } - - @Override - public boolean verifySignature(String signingInput, String encodedSignature, String keyId, JSONObject jwks, String sharedSecret, SignatureAlgorithm signatureAlgorithm) throws CryptoProviderException { - return false; - } - - @Override - public boolean deleteKey(String keyId) throws CryptoProviderException { - return false; - } - - @Override - public boolean containsKey(String keyId) { - return false; - } - - @Override - public PrivateKey getPrivateKey(String keyId) throws CryptoProviderException { - return null; - } - - @Override - public PublicKey getPublicKey(String alias) throws CryptoProviderException { - return null; - } - }; - Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); calendar.add(Calendar.HOUR, 24); ssa = new Ssa(); @@ -289,60 +217,65 @@ public void getSsaList_withNullParam_valid() { @Test public void generateJwt_executionContextWithPostProcessorNull_jwtValid() { - JSONWebKey jsonWebKey = JSONWebKey.fromJSONObject(new JSONObject(senderJwkJson)); - WebKeysConfiguration webKeysConfiguration = new WebKeysConfiguration(); - webKeysConfiguration.setKeys(Collections.singletonList(jsonWebKey)); - SsaConfiguration ssaConfiguration = new SsaConfiguration(); String issuer = "https://jans.io"; when(appConfiguration.getSsaConfiguration()).thenReturn(ssaConfiguration); when(appConfiguration.getIssuer()).thenReturn(issuer); - ExecutionContext executionContext = mock(ExecutionContext.class); - Jwt jwt = ssaService.generateJwt(ssa, executionContext, webKeysConfiguration, cryptoProvider); - assertSsaJwt(jsonWebKey, ssaConfiguration.getSsaSigningAlg(), issuer, ssa, jwt); + + Jwt jwt = ssaService.generateJwt(ssa, executionContext); + assertSsaJwt(ssaConfiguration.getSsaSigningAlg(), issuer, ssa, jwt); verify(executionContext).getPostProcessor(); + verifyNoMoreInteractions(executionContext); } @Test public void generateJwt_executionContextWithPostProcessor_jwtValid() { - JSONWebKey jsonWebKey = JSONWebKey.fromJSONObject(new JSONObject(senderJwkJson)); - WebKeysConfiguration webKeysConfiguration = new WebKeysConfiguration(); - webKeysConfiguration.setKeys(Collections.singletonList(jsonWebKey)); - SsaConfiguration ssaConfiguration = new SsaConfiguration(); String issuer = "https://jans.io"; when(appConfiguration.getSsaConfiguration()).thenReturn(ssaConfiguration); when(appConfiguration.getIssuer()).thenReturn(issuer); - ExecutionContext executionContext = mock(ExecutionContext.class); when(executionContext.getPostProcessor()).thenReturn(jsonWebResponse -> null); - Jwt jwt = ssaService.generateJwt(ssa, executionContext, webKeysConfiguration, cryptoProvider); - assertSsaJwt(jsonWebKey, ssaConfiguration.getSsaSigningAlg(), issuer, ssa, jwt); + Jwt jwt = ssaService.generateJwt(ssa, executionContext); + assertSsaJwt(ssaConfiguration.getSsaSigningAlg(), issuer, ssa, jwt); verify(executionContext, times(2)).getPostProcessor(); } @Test - public void generateJwt_exceptionWithIsErrorEnabledFalse_runtimeException() { + public void generateJwt_executionContextNotSsaConfiguration_exception() { + ExecutionContext executionContext = mock(ExecutionContext.class); + assertThrows(Exception.class, () -> ssaService.generateJwt(ssa, executionContext)); + verifyNoInteractions(executionContext); + } + + @Test + public void generateJwt_ssa_jwtValid() { + SsaConfiguration ssaConfiguration = new SsaConfiguration(); + String issuer = "https://jans.io"; + when(appConfiguration.getSsaConfiguration()).thenReturn(ssaConfiguration); + when(appConfiguration.getIssuer()).thenReturn(issuer); + + Jwt jwt = ssaService.generateJwt(ssa); + assertSsaJwt(ssaConfiguration.getSsaSigningAlg(), issuer, ssa, jwt); + verifyNoInteractions(log); + } + + @Test + public void generateJwt_ssaLogErrorEnabledFalse_exception() { when(log.isErrorEnabled()).thenReturn(false); - try { - ssaService.generateJwt(ssa, mock(ExecutionContext.class), mock(WebKeysConfiguration.class), cryptoProvider); - } catch (Exception e) { - assertNotNull(e, "Exception is null"); - } + + assertThrows(Exception.class, () -> ssaService.generateJwt(ssa)); verify(log).isErrorEnabled(); verifyNoMoreInteractions(log); } @Test - public void generateJwt_exceptionWithIsErrorEnabledTrue_runtimeException() { + public void generateJwt_ssaLogErrorEnabledTrue_exception() { when(log.isErrorEnabled()).thenReturn(true); - try { - ssaService.generateJwt(ssa, mock(ExecutionContext.class), mock(WebKeysConfiguration.class), cryptoProvider); - } catch (Exception e) { - assertNotNull(e, "Exception is null"); - } + + assertThrows(Exception.class, () -> ssaService.generateJwt(ssa)); verify(log).isErrorEnabled(); verify(log).error(anyString(), any(Throwable.class)); } @@ -361,14 +294,12 @@ public void createUnprocessableEntityResponse_valid_response() { assertEquals(response.getStatus(), HttpStatus.SC_UNPROCESSABLE_ENTITY); } - private static void assertSsaJwt(JSONWebKey jsonWebKey, String ssaSigningAlg, String issuer, Ssa ssa, Jwt jwt) { + private static void assertSsaJwt(String ssaSigningAlg, String issuer, Ssa ssa, Jwt jwt) { assertNotNull(jwt, "The jwt is null"); JwtHeader jwtHeader = jwt.getHeader(); assertNotNull(jwtHeader.getSignatureAlgorithm().getJwsAlgorithm(), "The alg in jwt is null"); assertEquals(jwtHeader.getSignatureAlgorithm().getJwsAlgorithm().toString(), ssaSigningAlg); - assertNotNull(jwtHeader.getKeyId(), "The kid in jwt is null"); - assertEquals(jwtHeader.getKeyId(), jsonWebKey.getKid()); assertNotNull(jwtHeader.getType(), "The type in jwt is null"); assertEquals(jwtHeader.getType().toString(), "jwt"); diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateActionTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateActionTest.java index c845734ce18..6c22dadb6d5 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateActionTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateActionTest.java @@ -123,10 +123,10 @@ public void create_request_valid() { ExecutionContext executionContext = mock(ExecutionContext.class); ModifySsaResponseContext context = mock(ModifySsaResponseContext.class); - when(ssaContextBuilder.buildModifySsaResponseContext(any(), any(), any(), any(), any())).thenReturn(context); + when(ssaContextBuilder.buildModifySsaResponseContext(any(), any())).thenReturn(context); when(modifySsaResponseService.buildCreateProcessor(any())).thenReturn(jsonWebResponse -> null); when(context.toExecutionContext()).thenReturn(executionContext); - when(ssaService.generateJwt(any(), any(), any(), any())).thenReturn(mock(Jwt.class)); + when(ssaService.generateJwt(any(), any())).thenReturn(mock(Jwt.class)); when(ssaJsonService.getJSONObject(anyString())).thenReturn(mock(JSONObject.class)); when(ssaJsonService.jsonObjectToString(any())).thenReturn("{\"ssa\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"}"); when(appConfiguration.getSsaConfiguration()).thenReturn(new SsaConfiguration()); @@ -143,10 +143,10 @@ public void create_request_valid() { verify(ssaRestWebServiceValidator).checkScopesPolicy(any(), anyString()); verify(ssaService).persist(any()); verify(log).info(anyString(), any(Ssa.class)); - verify(ssaContextBuilder).buildModifySsaResponseContext(any(), any(), any(), any(), any()); + verify(ssaContextBuilder).buildModifySsaResponseContext(any(), any()); verify(modifySsaResponseService).buildCreateProcessor(any()); verify(context).toExecutionContext(); - verify(ssaService).generateJwt(any(), any(), any(), any()); + verify(ssaService).generateJwt(any(), any()); verify(ssaJsonService).getJSONObject(anyString()); verify(ssaJsonService).jsonObjectToString(any()); diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaGetActionTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaGetActionTest.java index ec49b30b04d..bbfb89010e8 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaGetActionTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaGetActionTest.java @@ -63,7 +63,7 @@ public void get_withAllParam_valid() { assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); verify(log).debug(anyString(), any(), any()); verify(errorResponseFactory).validateFeatureEnabled(any()); - verify(ssaContextBuilder).buildModifySsaResponseContext(any(), any(), any(), any(), any()); + verify(ssaContextBuilder).buildModifySsaResponseContext(any(), any()); verify(ssaJsonService).jsonArrayToString(any()); verify(modifySsaResponseService).get(any(), any()); verifyNoMoreInteractions(log, errorResponseFactory); diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaGetJwtActionTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaGetJwtActionTest.java new file mode 100644 index 00000000000..30ca9a0919b --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaGetJwtActionTest.java @@ -0,0 +1,125 @@ +package io.jans.as.server.ssa.ws.rs.action; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.common.model.ssa.Ssa; +import io.jans.as.model.common.FeatureFlagType; +import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.jwt.Jwt; +import io.jans.as.server.ssa.ws.rs.SsaJsonService; +import io.jans.as.server.ssa.ws.rs.SsaRestWebServiceValidator; +import io.jans.as.server.ssa.ws.rs.SsaService; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.Response; +import org.json.JSONObject; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.slf4j.Logger; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; +import static org.testng.Assert.*; + +@Listeners(MockitoTestNGListener.class) +public class SsaGetJwtActionTest { + + @InjectMocks + private SsaGetJwtAction ssaGetJwtAction; + + @Mock + private Logger log; + + @Mock + private ErrorResponseFactory errorResponseFactory; + + @Mock + private SsaService ssaService; + + @Mock + private SsaRestWebServiceValidator ssaRestWebServiceValidator; + + @Mock + private SsaJsonService ssaJsonService; + + @Test + public void testGetJwtSsa_jti_validStatus() { + String jti = "test-jti"; + String jwt = "jwt-test"; + Ssa ssa = new Ssa(); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("ssa", jwt); + when(ssaRestWebServiceValidator.getClientFromSession()).thenReturn(mock(Client.class)); + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenReturn(ssa); + when(ssaService.generateJwt(ssa)).thenReturn(mock(Jwt.class)); + when(ssaJsonService.getJSONObject(any())).thenReturn(jsonObject); + when(ssaJsonService.jsonObjectToString(jsonObject)).thenReturn(jsonObject.toString()); + + Response response = ssaGetJwtAction.getJwtSsa(jti); + assertNotNull(response); + assertEquals(response.getStatus(), 200); + assertNotNull(response.getEntity()); + JSONObject aux = new JSONObject(response.getEntity().toString()); + assertTrue(aux.has("ssa")); + + verify(log).debug(anyString(), eq(jti)); + verify(errorResponseFactory).validateFeatureEnabled(eq(FeatureFlagType.SSA)); + verify(ssaRestWebServiceValidator).checkScopesPolicy(any(), anyString()); + verifyNoMoreInteractions(log, errorResponseFactory); + } + + @Test + public void testGetJwtSsa_jwtWithErrorEnabledFalse_422Status() { + String jti = "test-jti"; + when(ssaRestWebServiceValidator.getClientFromSession()).thenReturn(mock(Client.class)); + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenThrow(new WebApplicationException(Response.status(422).build())); + when(log.isErrorEnabled()).thenReturn(false); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaGetJwtAction.getJwtSsa(jti)); + assertNotNull(ex); + assertEquals(ex.getResponse().getStatus(), 422); + + verify(log).debug(anyString(), eq(jti)); + verify(errorResponseFactory).validateFeatureEnabled(eq(FeatureFlagType.SSA)); + verify(ssaRestWebServiceValidator).checkScopesPolicy(any(), anyString()); + verifyNoInteractions(ssaService, ssaJsonService); + verifyNoMoreInteractions(log, errorResponseFactory); + } + + @Test + public void testGetJwtSsa_jwtWithErrorEnabledTrue_422Status() { + String jti = "test-jti"; + when(ssaRestWebServiceValidator.getClientFromSession()).thenReturn(mock(Client.class)); + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenThrow(new WebApplicationException(Response.status(422).build())); + when(log.isErrorEnabled()).thenReturn(true); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaGetJwtAction.getJwtSsa(jti)); + assertNotNull(ex); + assertEquals(ex.getResponse().getStatus(), 422); + + verify(log).debug(anyString(), eq(jti)); + verify(errorResponseFactory).validateFeatureEnabled(eq(FeatureFlagType.SSA)); + verify(ssaRestWebServiceValidator).checkScopesPolicy(any(), anyString()); + verifyNoInteractions(ssaService, ssaJsonService); + verify(log).error(anyString(), eq(ex)); + verifyNoMoreInteractions(log, errorResponseFactory); + } + + @Test + public void testGetJwtSsa_jtiNullPointerException_500Status() { + String jti = "test-jti"; + when(ssaRestWebServiceValidator.getClientFromSession()).thenThrow(new NullPointerException("test null message")); + when(errorResponseFactory.createWebApplicationException(any(), any(), anyString())).thenThrow(new WebApplicationException(Response.status(500).build())); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaGetJwtAction.getJwtSsa(jti)); + assertNotNull(ex); + assertEquals(ex.getResponse().getStatus(), 500); + + verify(log).debug(anyString(), eq(jti)); + verify(errorResponseFactory).validateFeatureEnabled(eq(FeatureFlagType.SSA)); + verify(log).error(anyString(), any(Exception.class)); + verifyNoMoreInteractions(ssaRestWebServiceValidator, log); + verifyNoInteractions(ssaService, ssaJsonService); + } +} \ No newline at end of file diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeActionTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeActionTest.java index a641758c69c..56749b6a088 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeActionTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaRevokeActionTest.java @@ -119,7 +119,7 @@ public void revoke_validJti_200Status() { assertEquals(ssa.getState(), SsaState.REVOKED); verify(log).info(anyString(), eq(jti), eq(SsaState.REVOKED.getValue())); - verify(ssaContextBuilder).buildModifySsaResponseContext(any(), any(), any(), any(), any()); + verify(ssaContextBuilder).buildModifySsaResponseContext(any(), any()); verify(modifySsaResponseService).revoke(any(), any()); verifyNoMoreInteractions(ssaService, log, errorResponseFactory); } diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateActionTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateActionTest.java index b676898bac2..c4cf9bd4bd0 100644 --- a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateActionTest.java +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaValidateActionTest.java @@ -3,8 +3,10 @@ import io.jans.as.common.model.ssa.Ssa; import io.jans.as.common.model.ssa.SsaAttributes; import io.jans.as.common.model.ssa.SsaState; +import io.jans.as.model.common.FeatureFlagType; import io.jans.as.model.error.ErrorResponseFactory; import io.jans.as.model.ssa.SsaErrorResponseType; +import io.jans.as.server.ssa.ws.rs.SsaRestWebServiceValidator; import io.jans.as.server.ssa.ws.rs.SsaService; import jakarta.ws.rs.WebApplicationException; import jakarta.ws.rs.core.Response; @@ -16,9 +18,6 @@ import org.testng.annotations.Listeners; import org.testng.annotations.Test; -import java.util.Calendar; -import java.util.TimeZone; - import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import static org.testng.Assert.*; @@ -38,72 +37,37 @@ public class SsaValidateActionTest { @Mock private SsaService ssaService; - @Test - public void validate_ssaNull_422Status() { - String jti = "e1440ecf-4b68-467c-a032-be1c43183e0c"; - when(ssaService.findSsaByJti(jti)).thenReturn(null); - when(ssaService.createUnprocessableEntityResponse()).thenReturn(Response.status(422)); - - Response response = ssaValidateAction.validate(jti); - assertNotNull(response); - assertEquals(response.getStatus(), 422); - verify(log).debug(anyString(), eq(jti)); - verify(errorResponseFactory).validateFeatureEnabled(any()); - verify(log).warn(anyString(), eq(jti)); - verifyNoMoreInteractions(log, ssaService, errorResponseFactory); - } - - @Test - public void validate_ssaExpired_422Status() { - String jti = "e1440ecf-4b68-467c-a032-be1c43183e0c"; - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.add(Calendar.HOUR, -24); - Ssa ssa = new Ssa(); - ssa.setExpirationDate(calendar.getTime()); - when(ssaService.findSsaByJti(jti)).thenReturn(ssa); - when(ssaService.createUnprocessableEntityResponse()).thenReturn(Response.status(422)); - - Response response = ssaValidateAction.validate(jti); - assertNotNull(response); - assertEquals(response.getStatus(), 422); - verify(log).debug(anyString(), eq(jti)); - verify(errorResponseFactory).validateFeatureEnabled(any()); - verify(log).warn(anyString(), eq(jti)); - verifyNoMoreInteractions(log, ssaService, errorResponseFactory); - } + @Mock + private SsaRestWebServiceValidator ssaRestWebServiceValidator; @Test - public void validate_ssaInactive_422Status() { - String jti = "e1440ecf-4b68-467c-a032-be1c43183e0c"; - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.add(Calendar.HOUR, 24); + public void validate_ssaWithOneTimeUseFalse_validStatus() { + String jti = "test-jti"; + SsaAttributes attributes = new SsaAttributes(); + attributes.setOneTimeUse(false); Ssa ssa = new Ssa(); - ssa.setExpirationDate(calendar.getTime()); - ssa.setState(SsaState.USED); - when(ssaService.findSsaByJti(jti)).thenReturn(ssa); - when(ssaService.createUnprocessableEntityResponse()).thenReturn(Response.status(422)); + ssa.setState(SsaState.ACTIVE); + ssa.setAttributes(attributes); + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenReturn(ssa); Response response = ssaValidateAction.validate(jti); assertNotNull(response); - assertEquals(response.getStatus(), 422); + assertEquals(response.getStatus(), 200); verify(log).debug(anyString(), eq(jti)); - verify(errorResponseFactory).validateFeatureEnabled(any()); - verify(log).warn(anyString(), eq(jti)); - verifyNoMoreInteractions(log, ssaService, errorResponseFactory); + verify(errorResponseFactory).validateFeatureEnabled(FeatureFlagType.SSA); + verifyNoInteractions(ssaService); + verifyNoMoreInteractions(log, errorResponseFactory); } @Test public void validate_ssaWithOneTimeUseTrue_validStatus() { - String jti = "e1440ecf-4b68-467c-a032-be1c43183e0c"; - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.add(Calendar.HOUR, 24); + String jti = "test-jti"; SsaAttributes attributes = new SsaAttributes(); attributes.setOneTimeUse(true); Ssa ssa = new Ssa(); - ssa.setExpirationDate(calendar.getTime()); ssa.setState(SsaState.ACTIVE); ssa.setAttributes(attributes); - when(ssaService.findSsaByJti(jti)).thenReturn(ssa); + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenReturn(ssa); Response response = ssaValidateAction.validate(jti); assertNotNull(response); @@ -122,16 +86,42 @@ public void validate_ssaWithOneTimeUseTrue_validStatus() { assertEquals(ssaAux.getState(), SsaState.USED); } + @Test + public void validate_ssaInvalidWithErrorEnabledFalse_422Status() { + String jti = "test-jti"; + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenThrow(new WebApplicationException(Response.status(422).build())); + when(log.isErrorEnabled()).thenReturn(false); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaValidateAction.validate(jti)); + assertNotNull(ex); + assertEquals(ex.getResponse().getStatus(), 422); + verify(log).debug(anyString(), eq(jti)); + verify(errorResponseFactory).validateFeatureEnabled(eq(FeatureFlagType.SSA)); + verifyNoMoreInteractions(ssaRestWebServiceValidator, log, errorResponseFactory, ssaService); + } + + @Test + public void validate_ssaInvalidWithErrorEnabledTrue_422Status() { + String jti = "test-jti"; + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenThrow(new WebApplicationException(Response.status(422).build())); + when(log.isErrorEnabled()).thenReturn(true); + + WebApplicationException ex = expectThrows(WebApplicationException.class, () -> ssaValidateAction.validate(jti)); + assertNotNull(ex); + assertEquals(ex.getResponse().getStatus(), 422); + verify(log).debug(anyString(), eq(jti)); + verify(errorResponseFactory).validateFeatureEnabled(eq(FeatureFlagType.SSA)); + verify(log).error(anyString(), eq(ex)); + verifyNoMoreInteractions(log, errorResponseFactory, ssaService); + } + @Test public void validate_ssaAttributesNullInternalServerError_badRequestResponse() { - String jti = "e1440ecf-4b68-467c-a032-be1c43183e0c"; - Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - calendar.add(Calendar.HOUR, 24); + String jti = "test-jti"; Ssa ssa = new Ssa(); - ssa.setExpirationDate(calendar.getTime()); ssa.setState(SsaState.ACTIVE); ssa.setAttributes(null); - when(ssaService.findSsaByJti(jti)).thenReturn(ssa); + when(ssaRestWebServiceValidator.getValidSsaByJti(jti)).thenReturn(ssa); WebApplicationException error = new WebApplicationException( Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Unknown error")