diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/BaseClient.java b/jans-auth-server/client/src/main/java/io/jans/as/client/BaseClient.java index 42661dba886..50e548a598a 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/BaseClient.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/BaseClient.java @@ -142,6 +142,10 @@ public String getRequestAsString() { String accessToken = ((UserInfoRequest) request).getAccessToken(); sb.append("\n"); sb.append(Constants.AUTHORIZATION_BEARER).append(accessToken); + } else if (request.getAuthorizationMethod() == AuthorizationMethod.AUTHORIZATION_REQUEST_HEADER_FIELD && request instanceof SsaRequest) { + String accessToken = ((SsaRequest) request).getAccessToken(); + sb.append("\n"); + sb.append(Constants.AUTHORIZATION_BEARER).append(accessToken); } sb.append("\n"); diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java index a192ada8faa..f2ea460d9c7 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/BaseRequest.java @@ -10,6 +10,7 @@ import io.jans.as.model.common.AuthorizationMethod; import io.jans.as.model.util.Util; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.StringUtils; import org.json.JSONException; import org.json.JSONObject; @@ -18,6 +19,8 @@ import java.util.HashMap; import java.util.Map; +import static io.jans.as.model.ciba.PushTokenDeliveryRequestParam.AUTHORIZATION_REQUEST_ID; + /** * @author Javier Rojas Blum * @version April 25. 2022 diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationClient.java b/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationClient.java index 27331f83fe5..c56181d402c 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationClient.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationClient.java @@ -191,6 +191,11 @@ public static void parse(String json, OpenIdConfigurationResponse response) { if (jsonObj.has(BACKCHANNEL_USER_CODE_PAREMETER_SUPPORTED)) { response.setBackchannelUserCodeParameterSupported(jsonObj.getBoolean(BACKCHANNEL_USER_CODE_PAREMETER_SUPPORTED)); } + + // SSA + if (jsonObj.has(SSA_ENDPOINT)) { + response.setSsaEndpoint(jsonObj.optString(SSA_ENDPOINT)); + } } public static OpenIdConfigurationResponse parse(String json) { diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationResponse.java b/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationResponse.java index 37f31d6b6ac..799954b8d49 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationResponse.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/OpenIdConfigurationResponse.java @@ -89,6 +89,9 @@ public class OpenIdConfigurationResponse extends BaseResponse implements Seriali private List backchannelAuthenticationRequestSigningAlgValuesSupported; private Boolean backchannelUserCodeParameterSupported; + // SSA + private String ssaEndpoint; + public OpenIdConfigurationResponse() { } @@ -1186,6 +1189,14 @@ public void setMltsAliases(Map mltsAliases) { this.mltsAliases = mltsAliases; } + public String getSsaEndpoint() { + return ssaEndpoint; + } + + public void setSsaEndpoint(String ssaEndpoint) { + this.ssaEndpoint = ssaEndpoint; + } + @Override public String toString() { return "OpenIdConfigurationResponse{" + @@ -1246,6 +1257,7 @@ public String toString() { ", backchannelAuthenticationRequestSigningAlgValuesSupported=" + backchannelAuthenticationRequestSigningAlgValuesSupported + '\'' + ", backchannelUserCodeParameterSupported=" + backchannelUserCodeParameterSupported + '\'' + ", mltsAliases=" + mltsAliases + '\'' + + ", ssaEndpoint=" + ssaEndpoint + '\'' + '}'; } } diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/SsaClient.java b/jans-auth-server/client/src/main/java/io/jans/as/client/SsaClient.java new file mode 100644 index 00000000000..d5b6aa3487a --- /dev/null +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/SsaClient.java @@ -0,0 +1,71 @@ +/* + * 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; + +import io.jans.as.model.config.Constants; +import jakarta.ws.rs.HttpMethod; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation.Builder; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.json.JSONObject; + +import java.util.List; + +public class SsaClient extends BaseClient { + + private static final Logger LOG = Logger.getLogger(SsaClient.class); + + public SsaClient(String url) { + super(url); + } + + @Override + public String getHttpMethod() { + return HttpMethod.POST; + } + + public SsaResponse execSsaCreate(String accessToken, Long orgId, Long expirationDate, String description, String softwareId, List softwareRoles, List grantTypes) { + setRequest(new SsaRequest()); + getRequest().setAccessToken(accessToken); + getRequest().setOrgId(orgId); + getRequest().setExpiration(expirationDate); + getRequest().setDescription(description); + getRequest().setSoftwareId(softwareId); + getRequest().setSoftwareRoles(softwareRoles); + getRequest().setGrantTypes(grantTypes); + return exec(); + } + + public SsaResponse exec() { + try { + initClient(); + + Builder clientRequest = webTarget.request(); + applyCookies(clientRequest); + + clientRequest.header("Content-Type", request.getContentType()); + if (StringUtils.isNotBlank(request.getAccessToken())) { + clientRequest.header(Constants.AUTHORIZATION, "Bearer ".concat(request.getAccessToken())); + } + + JSONObject requestBody = getRequest().getJSONParameters(); + clientResponse = clientRequest.buildPost(Entity.json(requestBody.toString(4))).invoke(); + final SsaResponse ssaResponse = new SsaResponse(clientResponse); + ssaResponse.injectDataFromJson(); + setResponse(ssaResponse); + + } catch (Exception e) { + LOG.error(e.getMessage(), e); + } finally { + closeConnection(); + } + + return getResponse(); + } +} + diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/SsaRequest.java b/jans-auth-server/client/src/main/java/io/jans/as/client/SsaRequest.java new file mode 100644 index 00000000000..2bf0c149955 --- /dev/null +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/SsaRequest.java @@ -0,0 +1,188 @@ +/* + * 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; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import io.jans.as.client.util.ClientUtil; +import io.jans.as.model.common.AuthorizationMethod; +import io.jans.as.model.json.JsonApplier; +import jakarta.ws.rs.core.MediaType; +import org.apache.log4j.Logger; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +import static io.jans.as.client.util.ClientUtil.*; +import static io.jans.as.model.ssa.SsaRequestParam.*; + +public class SsaRequest extends BaseRequest { + + private static final Logger log = Logger.getLogger(SsaRequest.class); + + @JsonProperty(value = "org_id") + private Long orgId; + + private Long expiration; + + private String description; + + @JsonProperty(value = "software_id") + private String softwareId; + + @JsonProperty(value = "software_roles") + private List softwareRoles; + + @JsonProperty(value = "grant_types") + private List grantTypes; + + @JsonProperty(value = "one_time_use") + private Boolean oneTimeUse; + + @JsonProperty(value = "rotate_ssa") + private Boolean rotateSsa; + + private String accessToken; + + public SsaRequest() { + setContentType(MediaType.APPLICATION_JSON); + setMediaType(MediaType.APPLICATION_JSON); + setAuthorizationMethod(AuthorizationMethod.AUTHORIZATION_REQUEST_HEADER_FIELD); + this.softwareRoles = new ArrayList<>(); + } + + public Long getOrgId() { + return orgId; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public Long getExpiration() { + return expiration; + } + + public void setExpiration(Long expiration) { + this.expiration = expiration; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getSoftwareId() { + return softwareId; + } + + public void setSoftwareId(String softwareId) { + this.softwareId = softwareId; + } + + public List getSoftwareRoles() { + return softwareRoles; + } + + public void setSoftwareRoles(List softwareRoles) { + this.softwareRoles = softwareRoles; + } + + public List getGrantTypes() { + return grantTypes; + } + + public void setGrantTypes(List grantTypes) { + this.grantTypes = grantTypes; + } + + public Boolean getOneTimeUse() { + return oneTimeUse; + } + + public void setOneTimeUse(Boolean oneTimeUse) { + this.oneTimeUse = oneTimeUse; + } + + public Boolean getRotateSsa() { + return rotateSsa; + } + + public void setRotateSsa(Boolean rotateSsa) { + this.rotateSsa = rotateSsa; + } + + public String getAccessToken() { + return accessToken; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public static SsaRequest fromJson(String json) throws JSONException { + return fromJson(new JSONObject(json)); + } + + public static SsaRequest fromJson(JSONObject requestObject) throws JSONException { + final SsaRequest result = new SsaRequest(); + JsonApplier.getInstance().apply(requestObject, result); + result.setOrgId(requestObject.getLong(ORG_ID.toString())); + result.setExpiration(longOrNull(requestObject, EXPIRATION.toString())); + result.setDescription(requestObject.optString(DESCRIPTION.toString())); + result.setSoftwareId(requestObject.optString(SOFTWARE_ID.toString())); + result.setSoftwareRoles(extractListByKey(requestObject, SOFTWARE_ROLES.toString())); + result.setGrantTypes(extractListByKey(requestObject, GRANT_TYPES.toString())); + result.setOneTimeUse(booleanOrNull(requestObject, ONE_TIME_USE.toString())); + result.setRotateSsa(booleanOrNull(requestObject, ROTATE_SSA.toString())); + return result; + } + + @Override + public String getQueryString() { + try { + return ClientUtil.toPrettyJson(getJSONParameters()).replace("\\/", "/"); + } catch (JSONException | JsonProcessingException e) { + log.error(e.getMessage(), e); + return null; + } + } + + @Override + public JSONObject getJSONParameters() throws JSONException { + JSONObject parameters = new JSONObject(); + parameters.put(ORG_ID.getName(), orgId); + parameters.put(EXPIRATION.getName(), expiration); + parameters.put(DESCRIPTION.getName(), description); + parameters.put(SOFTWARE_ID.getName(), softwareId); + parameters.put(SOFTWARE_ROLES.getName(), softwareRoles); + parameters.put(GRANT_TYPES.getName(), grantTypes); + parameters.put(ONE_TIME_USE.getName(), oneTimeUse); + parameters.put(ROTATE_SSA.getName(), rotateSsa); + return parameters; + } + + @Override + public String toString() { + return "SsaRequest{" + + "orgId=" + orgId + + ", expiration=" + expiration + + ", description='" + description + '\'' + + ", softwareId='" + softwareId + '\'' + + ", softwareRoles=" + softwareRoles + + ", grantTypes=" + grantTypes + + ", oneTimeUse=" + oneTimeUse + + ", rotateSsa=" + rotateSsa + + ", accessToken='" + accessToken + '\'' + + '}'; + } +} diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/SsaResponse.java b/jans-auth-server/client/src/main/java/io/jans/as/client/SsaResponse.java new file mode 100644 index 00000000000..b5551a4c130 --- /dev/null +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/SsaResponse.java @@ -0,0 +1,59 @@ +/* + * 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; + +import io.jans.as.model.ssa.SsaErrorResponseType; +import jakarta.ws.rs.core.Response; +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.json.JSONException; +import org.json.JSONObject; + +public class SsaResponse extends BaseResponseWithErrors { + + private static final Logger LOG = Logger.getLogger(SsaResponse.class); + + private String ssa; + + public SsaResponse() { + } + + public SsaResponse(Response clientResponse) { + super(clientResponse); + } + + @Override + public SsaErrorResponseType fromString(String p_str) { + return SsaErrorResponseType.fromString(p_str); + } + + public void injectDataFromJson() { + injectDataFromJson(entity); + } + + @Override + public void injectDataFromJson(String json) { + if (StringUtils.isNotBlank(entity)) { + try { + JSONObject jsonObj = new JSONObject(entity); + if (jsonObj.has("ssa")) { + setSsa(jsonObj.getString("ssa")); + } + } catch (JSONException e) { + LOG.error(e.getMessage(), e); + } + } + } + + public String getSsa() { + return ssa; + } + + public void setSsa(String ssa) { + this.ssa = ssa; + } +} \ No newline at end of file diff --git a/jans-auth-server/client/src/main/java/io/jans/as/client/util/ClientUtil.java b/jans-auth-server/client/src/main/java/io/jans/as/client/util/ClientUtil.java index 1dd0b5c813d..a97f27bb88c 100644 --- a/jans-auth-server/client/src/main/java/io/jans/as/client/util/ClientUtil.java +++ b/jans-auth-server/client/src/main/java/io/jans/as/client/util/ClientUtil.java @@ -109,6 +109,14 @@ public static Boolean booleanOrNull(JSONObject jsonObject, String key) { return jsonObject.has(key) ? jsonObject.optBoolean(key) : null; } + public static Long longOrNull(JSONObject jsonObject, String key) { + return jsonObject.has(key) ? jsonObject.optLong(key) : null; + } + + public static String stringOrNull(JSONObject jsonObject, String key) { + return jsonObject.has(key) ? jsonObject.optString(key) : null; + } + /** * Creates a special SSLContext using a custom TLS version and a set of ciphers enabled to process SSL connections. * diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/BaseTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/BaseTest.java index 157a7d97ba6..115535c4d43 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/BaseTest.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/BaseTest.java @@ -101,6 +101,7 @@ public abstract class BaseTest { protected String backchannelAuthenticationEndpoint; protected String revokeSessionEndpoint; protected String parEndpoint; + protected String ssaEndpoint; protected Map> scopeToClaimsMapping; protected String issuer; protected String sharedKey; @@ -237,6 +238,14 @@ public void setParEndpoint(String parEndpoint) { this.parEndpoint = parEndpoint; } + public String getSsaEndpoint() { + return ssaEndpoint; + } + + public void setSsaEndpoint(String ssaEndpoint) { + this.ssaEndpoint = ssaEndpoint; + } + public String getBackchannelAuthenticationEndpoint() { return backchannelAuthenticationEndpoint; } @@ -835,6 +844,7 @@ public void discovery(ITestContext context) throws Exception { scopeToClaimsMapping = response.getScopeToClaimsMapping(); gluuConfigurationEndpoint = determineGluuConfigurationEndpoint(openIdConnectDiscoveryResponse.getLinks().get(0).getHref()); issuer = response.getIssuer(); + ssaEndpoint = response.getSsaEndpoint(); } else { showTitle("Loading configuration endpoints from properties file"); @@ -854,6 +864,7 @@ public void discovery(ITestContext context) throws Exception { revokeSessionEndpoint = context.getCurrentXmlTest().getParameter("revokeSessionEndpoint"); scopeToClaimsMapping = new HashMap>(); issuer = context.getCurrentXmlTest().getParameter("issuer"); + ssaEndpoint = context.getCurrentXmlTest().getParameter("ssaEndpoint"); } authorizationPageEndpoint = determineAuthorizationPageEndpoint(authorizationEndpoint); diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java b/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java index 15fc3503f90..0c8640c9dd9 100644 --- a/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/client/AssertBuilder.java @@ -55,4 +55,7 @@ public static JweAssertBuilder jwe(Jwe jwe) { return new JweAssertBuilder(jwe); } + public static SsaResponseAssertBuilder ssaResponse(SsaRequest request, SsaResponse response) { + return new SsaResponseAssertBuilder(request, response); + } } diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/SsaResponseAssertBuilder.java b/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/SsaResponseAssertBuilder.java new file mode 100644 index 00000000000..0d5759591a0 --- /dev/null +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/client/assertbuilders/SsaResponseAssertBuilder.java @@ -0,0 +1,86 @@ +package io.jans.as.client.client.assertbuilders; + +import io.jans.as.client.SsaRequest; +import io.jans.as.client.SsaResponse; +import io.jans.as.model.jwt.Jwt; +import io.jans.as.model.jwt.JwtClaims; +import io.jans.as.model.ssa.SsaErrorResponseType; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +public class SsaResponseAssertBuilder extends BaseAssertBuilder { + + private final SsaRequest request; + private final SsaResponse response; + private int status = 200; + private SsaErrorResponseType errorResponseType; + + public SsaResponseAssertBuilder(SsaRequest request, SsaResponse response) { + this.request = request; + this.response = response; + } + + public SsaResponseAssertBuilder ok() { + this.status = 200; + return this; + } + + public SsaResponseAssertBuilder created() { + this.status = 201; + return this; + } + + public SsaResponseAssertBuilder bad(SsaErrorResponseType responseType) { + this.status = 400; + this.errorResponseType = responseType; + return this; + } + + public SsaResponseAssertBuilder status(int status) { + this.status = status; + return this; + } + + public SsaResponseAssertBuilder errorResponseType(SsaErrorResponseType errorResponseType) { + this.errorResponseType = errorResponseType; + return this; + } + + @Override + public void check() { + assertNotNull(response, "SsaResponse is null"); + if (status == 200 || status == 201) { + assertEquals(response.getStatus(), status, "Unexpected response code: " + response.getStatus()); + assertNotNull(response.getEntity(), "The entity is null"); + assertNotNull(response.getSsa(), "The ssa token is null"); + + Jwt jwt = Jwt.parseSilently(response.getSsa()); + assertNotNull(jwt, "The jwt is null"); + JwtClaims jwtClaims = jwt.getClaims(); + assertNotNull(jwtClaims.getClaim("org_id"), "The org_id in jwt is null"); + assertEquals(jwtClaims.getClaimAsLong("org_id"), request.getOrgId()); + assertNotNull(jwtClaims.getClaim("software_id"), "The software_id in jwt is null"); + assertEquals(jwtClaims.getClaimAsString("software_id"), request.getSoftwareId()); + assertNotNull(jwtClaims.getClaim("software_roles"), "The software_roles in jwt is null"); + assertEquals(jwtClaims.getClaimAsStringList("software_roles"), request.getSoftwareRoles()); + assertNotNull(jwtClaims.getClaim("grant_types"), "The grant_types in jwt is null"); + assertEquals(jwtClaims.getClaimAsStringList("grant_types"), request.getGrantTypes()); + + assertNotNull(jwtClaims.getClaim("jti"), "The jti in jwt is null"); + assertNotNull(jwtClaims.getClaim("iss"), "The iss in jwt is null"); + assertNotNull(jwtClaims.getClaim("iat"), "The iat in jwt is null"); + assertNotNull(jwtClaims.getClaim("exp"), "The exp in jwt is null"); + if (request.getExpiration() != null) { + assertEquals(jwtClaims.getClaimAsLong("exp"), request.getExpiration()); + } + } else { + assertEquals(response.getStatus(), status, "Unexpected HTTP status response: " + response.getEntity()); + assertNotNull(response.getEntity(), "The entity is null"); + if (errorResponseType != null) { + assertEquals(response.getErrorType(), errorResponseType, "Unexpected error type, should be " + errorResponseType.getParameter()); + } + assertNotNull(response.getErrorDescription()); + } + } +} diff --git a/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/SsaRestWebServiceHttpTest.java b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/SsaRestWebServiceHttpTest.java new file mode 100644 index 00000000000..92eb027b91a --- /dev/null +++ b/jans-auth-server/client/src/test/java/io/jans/as/client/ws/rs/SsaRestWebServiceHttpTest.java @@ -0,0 +1,221 @@ +/* + * 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.ws.rs; + +import io.jans.as.client.*; +import io.jans.as.client.client.AssertBuilder; +import io.jans.as.model.common.GrantType; +import io.jans.as.model.common.ResponseType; +import io.jans.as.model.register.ApplicationType; +import io.jans.as.model.ssa.SsaScopeType; +import io.jans.as.model.util.StringUtils; +import jakarta.ws.rs.core.Response; +import org.testng.annotations.Parameters; +import org.testng.annotations.Test; + +import java.util.*; + +import static io.jans.as.client.client.Asserter.assertRegisterResponseClaimsNotNull; +import static io.jans.as.model.register.RegisterRequestParam.*; + +public class SsaRestWebServiceHttpTest extends BaseTest { + + @Parameters({"redirectUris", "sectorIdentifierUri"}) + @Test + public void createSsaValid(final String redirectUris, final String sectorIdentifierUri) { + showTitle("createSsaValid"); + + List responseTypes = Collections.singletonList(ResponseType.CODE); + List grantTypes = Collections.singletonList(GrantType.CLIENT_CREDENTIALS); + List scopes = Collections.singletonList(SsaScopeType.SSA_ADMIN.getValue()); + + // 1. Register client + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "ssa client test app", StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setGrantTypes(grantTypes); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + registerRequest.setScope(scopes); + + RegisterClient registerClient = new RegisterClient(registrationEndpoint); + registerClient.setRequest(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + AssertBuilder.registerResponse(registerResponse).created().check(); + + String clientId = registerResponse.getClientId(); + String registrationAccessToken = registerResponse.getRegistrationAccessToken(); + String registrationClientUri = registerResponse.getRegistrationClientUri(); + String clientSecret = registerResponse.getClientSecret(); + + // 2. Client read + RegisterRequest readClientRequest = new RegisterRequest(registrationAccessToken); + + RegisterClient readClient = new RegisterClient(registrationClientUri); + readClient.setRequest(readClientRequest); + RegisterResponse readClientResponse = readClient.exec(); + + showClient(readClient); + AssertBuilder.registerResponse(readClientResponse).ok().check(); + + assertRegisterResponseClaimsNotNull(readClientResponse, RESPONSE_TYPES, REDIRECT_URIS, APPLICATION_TYPE, ID_TOKEN_SIGNED_RESPONSE_ALG, SCOPE); + + // 3. access token + String scope = SsaScopeType.SSA_ADMIN.getValue(); + TokenClient tokenClient = new TokenClient(tokenEndpoint); + TokenResponse tokenResponse = tokenClient.execClientCredentialsGrant(scope, clientId, clientSecret); + + showClient(tokenClient); + AssertBuilder.tokenResponse(tokenResponse).ok().check(); + + String accessToken = tokenResponse.getAccessToken(); + + // 4. Ssa create + SsaClient ssaClient = new SsaClient(ssaEndpoint); + Long orgId = 1L; + Calendar calendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.add(Calendar.HOUR, 24); + Long expirationDate = calendar.getTime().getTime() / 1000L; + String description = "test description"; + String softwareId = "gluu-scan-api"; + List softwareRoles = Collections.singletonList("passwurd"); + List ssaGrantTypes = Collections.singletonList("client_credentials"); + SsaResponse ssaResponse = ssaClient.execSsaCreate(accessToken, orgId, expirationDate, description, softwareId, softwareRoles, ssaGrantTypes); + + showClient(ssaClient); + AssertBuilder.ssaResponse(ssaClient.getRequest(), ssaResponse).created().check(); + } + + @Parameters({"redirectUris", "sectorIdentifierUri"}) + @Test + public void createSsaInvalidWithoutScopeAdmin(final String redirectUris, final String sectorIdentifierUri) { + showTitle("createSsaInvalidWithoutScopeAdmin"); + + List responseTypes = Collections.singletonList(ResponseType.CODE); + List grantTypes = Collections.singletonList(GrantType.CLIENT_CREDENTIALS); + List scopes = Collections.singletonList("openid"); + + // 1. Register client + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "ssa client test app", StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setGrantTypes(grantTypes); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + registerRequest.setScope(scopes); + + RegisterClient registerClient = new RegisterClient(registrationEndpoint); + registerClient.setRequest(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + AssertBuilder.registerResponse(registerResponse).created().check(); + + String clientId = registerResponse.getClientId(); + String registrationAccessToken = registerResponse.getRegistrationAccessToken(); + String registrationClientUri = registerResponse.getRegistrationClientUri(); + String clientSecret = registerResponse.getClientSecret(); + + // 2. Client read + RegisterRequest readClientRequest = new RegisterRequest(registrationAccessToken); + + RegisterClient readClient = new RegisterClient(registrationClientUri); + readClient.setRequest(readClientRequest); + RegisterResponse readClientResponse = readClient.exec(); + + showClient(readClient); + AssertBuilder.registerResponse(readClientResponse).ok().check(); + + assertRegisterResponseClaimsNotNull(readClientResponse, RESPONSE_TYPES, REDIRECT_URIS, APPLICATION_TYPE, ID_TOKEN_SIGNED_RESPONSE_ALG, SCOPE); + + // 3. access token + String scope = ""; + TokenClient tokenClient = new TokenClient(tokenEndpoint); + TokenResponse tokenResponse = tokenClient.execClientCredentialsGrant(scope, clientId, clientSecret); + + showClient(tokenClient); + AssertBuilder.tokenResponse(tokenResponse).ok().check(); + + String accessToken = tokenResponse.getAccessToken(); + + // 4. Ssa create + SsaClient ssaClient = new SsaClient(ssaEndpoint); + Long orgId = 1L; + Calendar calendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC")); + calendar.add(Calendar.HOUR, 24); + Long expirationDate = calendar.getTime().getTime() / 1000L; + String description = "test description"; + String softwareId = "gluu-scan-api"; + List softwareRoles = Collections.singletonList("passwurd"); + List ssaGrantTypes = Collections.singletonList("client_credentials"); + SsaResponse ssaResponse = ssaClient.execSsaCreate(accessToken, orgId, expirationDate, description, softwareId, softwareRoles, ssaGrantTypes); + + showClient(ssaClient); + AssertBuilder.ssaResponse(ssaClient.getRequest(), ssaResponse).status(Response.Status.UNAUTHORIZED.getStatusCode()).check(); + } + + @Parameters({"redirectUris", "sectorIdentifierUri"}) + @Test + public void createSsaValidWithoutExpiration(final String redirectUris, final String sectorIdentifierUri) { + showTitle("createSsaValidWithoutExpiration"); + + List responseTypes = Collections.singletonList(ResponseType.CODE); + List grantTypes = Collections.singletonList(GrantType.CLIENT_CREDENTIALS); + List scopes = Collections.singletonList(SsaScopeType.SSA_ADMIN.getValue()); + + // 1. Register client + RegisterRequest registerRequest = new RegisterRequest(ApplicationType.WEB, "ssa client test app", StringUtils.spaceSeparatedToList(redirectUris)); + registerRequest.setGrantTypes(grantTypes); + registerRequest.setResponseTypes(responseTypes); + registerRequest.setSectorIdentifierUri(sectorIdentifierUri); + registerRequest.setScope(scopes); + + RegisterClient registerClient = new RegisterClient(registrationEndpoint); + registerClient.setRequest(registerRequest); + RegisterResponse registerResponse = registerClient.exec(); + + showClient(registerClient); + AssertBuilder.registerResponse(registerResponse).created().check(); + + String clientId = registerResponse.getClientId(); + String registrationAccessToken = registerResponse.getRegistrationAccessToken(); + String registrationClientUri = registerResponse.getRegistrationClientUri(); + String clientSecret = registerResponse.getClientSecret(); + + // 2. Client read + RegisterRequest readClientRequest = new RegisterRequest(registrationAccessToken); + + RegisterClient readClient = new RegisterClient(registrationClientUri); + readClient.setRequest(readClientRequest); + RegisterResponse readClientResponse = readClient.exec(); + + showClient(readClient); + AssertBuilder.registerResponse(readClientResponse).ok().check(); + + assertRegisterResponseClaimsNotNull(readClientResponse, RESPONSE_TYPES, REDIRECT_URIS, APPLICATION_TYPE, ID_TOKEN_SIGNED_RESPONSE_ALG, SCOPE); + + // 3. access token + String scope = SsaScopeType.SSA_ADMIN.getValue(); + TokenClient tokenClient = new TokenClient(tokenEndpoint); + TokenResponse tokenResponse = tokenClient.execClientCredentialsGrant(scope, clientId, clientSecret); + + showClient(tokenClient); + AssertBuilder.tokenResponse(tokenResponse).ok().check(); + + String accessToken = tokenResponse.getAccessToken(); + + // 4. Ssa create + SsaClient ssaClient = new SsaClient(ssaEndpoint); + Long orgId = 1L; + String description = "test description"; + String softwareId = "gluu-scan-api"; + List softwareRoles = Collections.singletonList("passwurd"); + List ssaGrantTypes = Collections.singletonList("client_credentials"); + SsaResponse ssaResponse = ssaClient.execSsaCreate(accessToken, orgId, null, description, softwareId, softwareRoles, ssaGrantTypes); + + showClient(ssaClient); + AssertBuilder.ssaResponse(ssaClient.getRequest(), ssaResponse).created().check(); + } +} \ No newline at end of file diff --git a/jans-auth-server/client/src/test/resources/testng.xml b/jans-auth-server/client/src/test/resources/testng.xml index f4d83b75e36..22511197a5e 100644 --- a/jans-auth-server/client/src/test/resources/testng.xml +++ b/jans-auth-server/client/src/test/resources/testng.xml @@ -1167,4 +1167,11 @@ + + + + + + + diff --git a/jans-auth-server/common/src/main/java/io/jans/as/common/model/ssa/Ssa.java b/jans-auth-server/common/src/main/java/io/jans/as/common/model/ssa/Ssa.java new file mode 100644 index 00000000000..5895196e2dc --- /dev/null +++ b/jans-auth-server/common/src/main/java/io/jans/as/common/model/ssa/Ssa.java @@ -0,0 +1,180 @@ +/* + * 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.common.model.ssa; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.jans.orm.annotation.AttributeName; +import io.jans.orm.annotation.DataEntry; +import io.jans.orm.annotation.JsonObject; +import io.jans.orm.annotation.ObjectClass; +import io.jans.orm.model.base.DeletableEntity; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.Map; + +@DataEntry(sortBy = {"creationDate"}) +@ObjectClass(value = "jansSsa") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Ssa extends DeletableEntity implements Serializable { + + private static final long serialVersionUID = -6832496019942067971L; + + @JsonProperty("inum") + @AttributeName(name = "inum", ignoreDuringUpdate = true) + private String id; + + @AttributeName(name = "orgId") + private Long orgId; + + @AttributeName(name = "expiration") + private Date expiration; + + @AttributeName(name = "description") + private String description; + + @AttributeName(name = "softwareId") + private String softwareId; + + @AttributeName(name = "softwareRoles") + private List softwareRoles; + + @AttributeName(name = "grantTypes") + private List grantTypes; + + @JsonObject + @AttributeName(name = "customAttributes") + private Map customAttributes; + + @AttributeName(name = "creationDate") + private Date creationDate = new Date(); + + @AttributeName(name = "clientDn") + private String clientDn; + + @AttributeName(name = "oneTimeUse") + private Boolean oneTimeUse; + + @AttributeName(name = "rotateSsa") + private Boolean rotateSsa; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Long getOrgId() { + return orgId; + } + + public void setOrgId(Long orgId) { + this.orgId = orgId; + } + + public Date getExpiration() { + return expiration; + } + + public void setExpiration(Date expiration) { + this.expiration = expiration; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getSoftwareId() { + return softwareId; + } + + public void setSoftwareId(String softwareId) { + this.softwareId = softwareId; + } + + public List getSoftwareRoles() { + return softwareRoles; + } + + public void setSoftwareRoles(List softwareRoles) { + this.softwareRoles = softwareRoles; + } + + public List getGrantTypes() { + return grantTypes; + } + + public void setGrantTypes(List grantTypes) { + this.grantTypes = grantTypes; + } + + public Map getCustomAttributes() { + return customAttributes; + } + + public void setCustomAttributes(Map customAttributes) { + this.customAttributes = customAttributes; + } + + public Date getCreationDate() { + return creationDate; + } + + public void setCreationDate(Date creationDate) { + this.creationDate = creationDate; + } + + public String getClientDn() { + return clientDn; + } + + public void setClientDn(String clientDn) { + this.clientDn = clientDn; + } + + public Boolean getOneTimeUse() { + return oneTimeUse; + } + + public void setOneTimeUse(Boolean oneTimeUse) { + this.oneTimeUse = oneTimeUse; + } + + public Boolean getRotateSsa() { + return rotateSsa; + } + + public void setRotateSsa(Boolean rotateSsa) { + this.rotateSsa = rotateSsa; + } + + @Override + public String toString() { + return "Ssa{" + + "id='" + id + '\'' + + ", orgId=" + orgId + + ", expiration=" + expiration + + ", description='" + description + '\'' + + ", softwareId='" + softwareId + '\'' + + ", softwareRoles=" + softwareRoles + + ", grantTypes=" + grantTypes + + ", customAttributes=" + customAttributes + + ", creationDate=" + creationDate + + ", clientDn='" + clientDn + '\'' + + ", oneTimeUse=" + oneTimeUse + + ", rotateSsa=" + rotateSsa + + '}'; + } +} \ No newline at end of file diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/common/FeatureFlagType.java b/jans-auth-server/model/src/main/java/io/jans/as/model/common/FeatureFlagType.java index d1f71617985..f714a68a02c 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/common/FeatureFlagType.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/common/FeatureFlagType.java @@ -30,7 +30,8 @@ public enum FeatureFlagType { DEVICE_AUTHZ("device_authz"), METRIC("metric"), STAT("stat"), - PAR("par"); + PAR("par"), + SSA("ssa"); private final String value; diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/config/BaseDnConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/config/BaseDnConfiguration.java index 57766cf8a2f..c8d1228c008 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/config/BaseDnConfiguration.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/config/BaseDnConfiguration.java @@ -59,6 +59,8 @@ public class BaseDnConfiguration { private String stat; @XmlElement(name = "par") private String par; + @XmlElement(name = "ssa") + private String ssa; public String getStat() { return stat; @@ -203,4 +205,12 @@ public String getCiba() { public void setCiba(String ciba) { this.ciba = ciba; } + + public String getSsa() { + return ssa; + } + + public void setSsa(String ssa) { + this.ssa = ssa; + } } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java index a200ee40913..152fa7c63fa 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/AppConfiguration.java @@ -13,6 +13,7 @@ import io.jans.as.model.common.*; import io.jans.as.model.error.ErrorHandlingMethod; import io.jans.as.model.jwk.KeySelectionStrategy; +import io.jans.as.model.ssa.SsaConfiguration; import java.util.ArrayList; import java.util.HashSet; @@ -324,6 +325,8 @@ public class AppConfiguration implements Configuration { private EngineConfig agamaConfiguration; + private SsaConfiguration ssaConfiguration; + public Boolean getRequireRequestObjectEncryption() { if (requireRequestObjectEncryption == null) requireRequestObjectEncryption = false; return requireRequestObjectEncryption; @@ -2570,4 +2573,12 @@ public EngineConfig getAgamaConfiguration() { public void setAgamaConfiguration(EngineConfig agamaConfiguration) { this.agamaConfiguration = agamaConfiguration; } + + public SsaConfiguration getSsaConfiguration() { + return ssaConfiguration; + } + + public void setSsaConfiguration(SsaConfiguration ssaConfiguration) { + this.ssaConfiguration = ssaConfiguration; + } } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/ConfigurationResponseClaim.java b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/ConfigurationResponseClaim.java index e18d8156fa2..fa3b70ba357 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/ConfigurationResponseClaim.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/configuration/ConfigurationResponseClaim.java @@ -83,4 +83,7 @@ private ConfigurationResponseClaim() { public static final String BACKCHANNEL_TOKEN_DELIVERY_MODES_SUPPORTED = "backchannel_token_delivery_modes_supported"; public static final String BACKCHANNEL_AUTHENTICATION_REQUEST_SIGNING_ALG_VALUES_SUPPORTED = "backchannel_authentication_request_signing_alg_values_supported"; public static final String BACKCHANNEL_USER_CODE_PAREMETER_SUPPORTED = "backchannel_user_code_parameter_supported"; + + // SSA + public static final String SSA_ENDPOINT = "ssa_endpoint"; } diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorMessages.java b/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorMessages.java index b26c1a9d69e..3d5322b56b1 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorMessages.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorMessages.java @@ -58,6 +58,10 @@ public class ErrorMessages { @XmlElement(name = "error") private List backchannelAuthentication; + @XmlElementWrapper(name = "ssa") + @XmlElement(name = "error") + private List ssa; + public List getAuthorize() { return authorize; } @@ -137,4 +141,12 @@ public List getBackchannelAuthentication() { public void setBackchannelAuthentication(List backchannelAuthentication) { this.backchannelAuthentication = backchannelAuthentication; } + + public List getSsa() { + return ssa; + } + + public void setSsa(List ssa) { + this.ssa = ssa; + } } \ No newline at end of file diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorResponseFactory.java b/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorResponseFactory.java index f38ff49c748..1df096d599d 100644 --- a/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorResponseFactory.java +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/error/ErrorResponseFactory.java @@ -18,6 +18,7 @@ import io.jans.as.model.fido.u2f.U2fErrorResponseType; import io.jans.as.model.register.RegisterErrorResponseType; import io.jans.as.model.session.EndSessionErrorResponseType; +import io.jans.as.model.ssa.SsaErrorResponseType; import io.jans.as.model.token.TokenErrorResponseType; import io.jans.as.model.token.TokenRevocationErrorResponseType; import io.jans.as.model.uma.UmaErrorResponseType; @@ -190,6 +191,8 @@ public DefaultErrorResponse getErrorResponse(IErrorType type) { list = messages.getFido(); } else if (type instanceof BackchannelAuthenticationErrorResponseType) { list = messages.getBackchannelAuthentication(); + } else if (type instanceof SsaErrorResponseType) { + list = messages.getSsa(); } if (list != null) { diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaConfiguration.java b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaConfiguration.java new file mode 100644 index 00000000000..628f25cfe60 --- /dev/null +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaConfiguration.java @@ -0,0 +1,52 @@ +package io.jans.as.model.ssa; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import java.util.ArrayList; +import java.util.List; + +@JsonIgnoreProperties( + ignoreUnknown = true +) +public class SsaConfiguration { + + private String ssaEndpoint; + + private List ssaCustomAttributes = new ArrayList<>(); + + private String ssaSigningAlg = "RS256"; + + private Integer ssaExpirationInDays = 30; + + public String getSsaEndpoint() { + return ssaEndpoint; + } + + public void setSsaEndpoint(String ssaEndpoint) { + this.ssaEndpoint = ssaEndpoint; + } + + public List getSsaCustomAttributes() { + return ssaCustomAttributes; + } + + public void setSsaCustomAttributes(List ssaCustomAttributes) { + this.ssaCustomAttributes = ssaCustomAttributes; + } + + public String getSsaSigningAlg() { + return ssaSigningAlg; + } + + public void setSsaSigningAlg(String ssaSigningAlg) { + this.ssaSigningAlg = ssaSigningAlg; + } + + public Integer getSsaExpirationInDays() { + return ssaExpirationInDays; + } + + public void setSsaExpirationInDays(Integer ssaExpirationInDays) { + this.ssaExpirationInDays = ssaExpirationInDays; + } +} diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaErrorResponseType.java b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaErrorResponseType.java new file mode 100644 index 00000000000..089712f3289 --- /dev/null +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaErrorResponseType.java @@ -0,0 +1,55 @@ +/* + * 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.model.ssa; + +import io.jans.as.model.error.IErrorType; + +public enum SsaErrorResponseType implements IErrorType { + + /** + * If the client does not have the ssa.admin scope enabled. + */ + UNAUTHORIZED_CLIENT("unauthorized_client"), + + /** + * If there is no valid client in the request. + */ + INVALID_CLIENT("invalid_client"), + + /** + * When creating an ssa, if you get an internal error. + */ + UNKNOWN_ERROR("unknown_error"); + + private final String paramName; + + SsaErrorResponseType(String paramName) { + this.paramName = paramName; + } + + public static SsaErrorResponseType fromString(String param) { + if (param != null) { + for (SsaErrorResponseType err : SsaErrorResponseType + .values()) { + if (param.equals(err.paramName)) { + return err; + } + } + } + return null; + } + + @Override + public String getParameter() { + return paramName; + } + + @Override + public String toString() { + return paramName; + } +} diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaRequestParam.java b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaRequestParam.java new file mode 100644 index 00000000000..021836a315d --- /dev/null +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaRequestParam.java @@ -0,0 +1,35 @@ +/* + * 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.model.ssa; + +public enum SsaRequestParam { + + ORG_ID("org_id"), + EXPIRATION("expiration"), + DESCRIPTION("description"), + SOFTWARE_ID("software_id"), + SOFTWARE_ROLES("software_roles"), + GRANT_TYPES("grant_types"), + ONE_TIME_USE("one_time_use"), + ROTATE_SSA("rotate_ssa"), + ; + + private final String name; + + SsaRequestParam(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } +} \ No newline at end of file diff --git a/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaScopeType.java b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaScopeType.java new file mode 100644 index 00000000000..c9f1e5af82e --- /dev/null +++ b/jans-auth-server/model/src/main/java/io/jans/as/model/ssa/SsaScopeType.java @@ -0,0 +1,37 @@ +/* + * 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.model.ssa; + +import java.util.HashMap; +import java.util.Map; + +public enum SsaScopeType { + + SSA_ADMIN("https://jans.io/auth/ssa.admin"); + + private static final Map lookup = new HashMap<>(); + + static { + for (SsaScopeType enumType : values()) { + lookup.put(enumType.getValue(), enumType); + } + } + + private final String value; + + SsaScopeType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static SsaScopeType fromValue(String value) { + return lookup.get(value); + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/auth/AuthenticationFilter.java b/jans-auth-server/server/src/main/java/io/jans/as/server/auth/AuthenticationFilter.java index 2fcd8900672..6ba50b87d91 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/auth/AuthenticationFilter.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/auth/AuthenticationFilter.java @@ -89,7 +89,8 @@ "/restv1/bc-authorize", "/restv1/par", "/restv1/device_authorization", - "/restv1/register" + "/restv1/register", + "/restv1/ssa" }, displayName = "oxAuth") public class AuthenticationFilter implements Filter { @@ -163,6 +164,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo boolean deviceAuthorizationEndpoint = requestUrl.endsWith("/device_authorization"); boolean revokeSessionEndpoint = requestUrl.endsWith("/revoke_session"); boolean isParEndpoint = requestUrl.endsWith("/par"); + boolean ssaEndpoint = requestUrl.endsWith("/ssa"); String authorizationHeader = httpRequest.getHeader(Constants.AUTHORIZATION); String dpopHeader = httpRequest.getHeader("DPoP"); @@ -176,7 +178,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo return; } - if (tokenEndpoint || revokeSessionEndpoint || tokenRevocationEndpoint || deviceAuthorizationEndpoint || isParEndpoint) { + if (tokenEndpoint || revokeSessionEndpoint || tokenRevocationEndpoint || deviceAuthorizationEndpoint || isParEndpoint || ssaEndpoint) { log.debug("Starting endpoint authentication {}", requestUrl); // #686 : allow authenticated client via user access_token diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/model/audit/Action.java b/jans-auth-server/server/src/main/java/io/jans/as/server/model/audit/Action.java index b48fcadb82e..c13cb00bfa0 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/model/audit/Action.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/model/audit/Action.java @@ -24,7 +24,8 @@ public enum Action { SESSION_UNAUTHENTICATED("SESSION_UNAUTHENTICATED"), SESSION_AUTHENTICATED("SESSION_AUTHENTICATED"), SESSION_DESTROYED("SESSION_DESTROYED"), - DEVICE_CODE_AUTHORIZATION("DEVICE_CODE_AUTHORIZATION"); + DEVICE_CODE_AUTHORIZATION("DEVICE_CODE_AUTHORIZATION"), + SSA_CREATE("SSA_CREATE"); private final String value; diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/ResteasyInitializer.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/ResteasyInitializer.java index cf8eff881da..053d8a1e32b 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/service/ResteasyInitializer.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/ResteasyInitializer.java @@ -21,6 +21,7 @@ import io.jans.as.server.session.ws.rs.CheckSessionStatusRestWebServiceImpl; import io.jans.as.server.session.ws.rs.EndSessionRestWebServiceImpl; import io.jans.as.server.session.ws.rs.SessionRestWebService; +import io.jans.as.server.ssa.ws.rs.SsaRestWebServiceImpl; import io.jans.as.server.token.ws.rs.TokenRestWebServiceImpl; import io.jans.as.server.uma.ws.rs.UmaGatheringWS; import io.jans.as.server.uma.ws.rs.UmaMetadataWS; @@ -81,6 +82,8 @@ public Set> getClasses() { classes.add(StatWS.class); + classes.add(SsaRestWebServiceImpl.class); + return classes; } diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ModifySsaResponseService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ModifySsaResponseService.java new file mode 100644 index 00000000000..6b13fe7ceee --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/ModifySsaResponseService.java @@ -0,0 +1,69 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2021, Janssen Project + */ + +package io.jans.as.server.service.external; + +import io.jans.as.model.token.JsonWebResponse; +import io.jans.as.server.service.external.context.ModifySsaResponseContext; +import io.jans.model.custom.script.CustomScriptType; +import io.jans.model.custom.script.conf.CustomScriptConfiguration; +import io.jans.model.custom.script.type.ssa.ModifySsaResponseType; +import io.jans.service.custom.script.ExternalScriptService; +import jakarta.enterprise.context.ApplicationScoped; + +import java.util.List; +import java.util.function.Function; + +@ApplicationScoped +public class ModifySsaResponseService extends ExternalScriptService { + + private static final long serialVersionUID = -1033475075863270259L; + + public ModifySsaResponseService() { + super(CustomScriptType.MODIFY_SSA_RESPONSE); + } + + public boolean create(CustomScriptConfiguration script, JsonWebResponse jsonWebResponse, ModifySsaResponseContext context) { + try { + log.trace("Executing python modify-ssa-response method, script name: {}, jwt: {}, context: {}", script.getName(), jsonWebResponse, context); + context.setScript(script); + + ModifySsaResponseType modifySsaResponseType = (ModifySsaResponseType) script.getExternalType(); + final boolean result = modifySsaResponseType.create(jsonWebResponse, context); + log.trace("Finished modify-ssa-response method, script name: {}, jwt: {}, context: {}, result: {}", script.getName(), jsonWebResponse, context, result); + + return result; + } catch (Exception ex) { + log.error(ex.getMessage(), ex); + saveScriptError(script.getCustomScript(), ex); + } + + return false; + } + + public boolean create(JsonWebResponse jsonWebResponse, ModifySsaResponseContext context) { + List scripts = customScriptManager.getCustomScriptConfigurationsByScriptType(CustomScriptType.MODIFY_SSA_RESPONSE); + if (scripts.isEmpty()) { + return false; + } + log.trace("Executing {} modify-ssa-response scripts.", scripts.size()); + + for (CustomScriptConfiguration script : scripts) { + if (!create(script, jsonWebResponse, context)) { + return false; + } + } + + return true; + } + + public Function buildCreateProcessor(final ModifySsaResponseContext context) { + return jsonWebResponse -> { + create(jsonWebResponse, context); + return null; + }; + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ModifySsaResponseContext.java b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ModifySsaResponseContext.java new file mode 100644 index 00000000000..bd879ae6d5c --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/service/external/context/ModifySsaResponseContext.java @@ -0,0 +1,94 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2021, Janssen Project + */ + +package io.jans.as.server.service.external.context; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.common.service.AttributeService; +import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.server.model.common.AuthorizationGrant; +import io.jans.as.server.model.common.ExecutionContext; +import io.jans.model.custom.script.conf.CustomScriptConfiguration; +import jakarta.servlet.http.HttpServletRequest; +import org.jetbrains.annotations.Nullable; + +public class ModifySsaResponseContext extends ExternalScriptContext { + + private final Client client; + private final AuthorizationGrant grant; + + private final AppConfiguration appConfiguration; + private final AttributeService attributeService; + + private CustomScriptConfiguration script; + @Nullable + private ExecutionContext executionContext; + + public ModifySsaResponseContext(HttpServletRequest httpRequest, AuthorizationGrant grant, + Client client, AppConfiguration appConfiguration, AttributeService attributeService) { + super(httpRequest); + this.client = client; + this.grant = grant; + this.appConfiguration = appConfiguration; + this.attributeService = attributeService; + } + + public static ModifySsaResponseContext of(ExecutionContext executionContext) { + ModifySsaResponseContext context = new ModifySsaResponseContext(executionContext.getHttpRequest(), executionContext.getGrant(), executionContext.getClient(), executionContext.getAppConfiguration(), executionContext.getAttributeService()); + context.setExecutionContext(executionContext); + return context; + } + + public ExecutionContext toExecutionContext() { + if (executionContext == null) { + executionContext = createExecutionContext(); + } + return executionContext; + } + + private ExecutionContext createExecutionContext() { + ExecutionContext result = new ExecutionContext(httpRequest, httpResponse); + result.setGrant(grant); + result.setClient(client); + result.setAppConfiguration(appConfiguration); + result.setAttributeService(attributeService); + result.setScript(script); + return result; + } + + public CustomScriptConfiguration getScript() { + return script; + } + + public void setScript(CustomScriptConfiguration script) { + this.script = script; + } + + public Client getClient() { + return client; + } + + public AuthorizationGrant getGrant() { + return grant; + } + + public AppConfiguration getAppConfiguration() { + return appConfiguration; + } + + public AttributeService getAttributeService() { + return attributeService; + } + + @Nullable + public ExecutionContext getExecutionContext() { + return executionContext; + } + + public void setExecutionContext(@Nullable ExecutionContext executionContext) { + this.executionContext = executionContext; + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/servlet/OpenIdConfiguration.java b/jans-auth-server/server/src/main/java/io/jans/as/server/servlet/OpenIdConfiguration.java index 58bb81bee18..d85db91af03 100644 --- a/jans-auth-server/server/src/main/java/io/jans/as/server/servlet/OpenIdConfiguration.java +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/servlet/OpenIdConfiguration.java @@ -283,6 +283,11 @@ protected void processRequest(HttpServletRequest servletRequest, HttpServletResp // CIBA Configuration cibaConfigurationService.processConfiguration(jsonObj); + // SSA + if (appConfiguration.isFeatureEnabled(FeatureFlagType.SSA)) { + jsonObj.put(SSA_ENDPOINT, appConfiguration.getSsaConfiguration().getSsaEndpoint()); + } + filterOutKeys(jsonObj, appConfiguration); localResponseCache.putDiscoveryResponse(jsonObj); diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaContextBuilder.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaContextBuilder.java new file mode 100644 index 00000000000..9357086d8d9 --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaContextBuilder.java @@ -0,0 +1,25 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2022, Janssen Project + */ +package io.jans.as.server.ssa.ws.rs; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.common.service.AttributeService; +import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.server.model.common.AuthorizationGrant; +import io.jans.as.server.service.external.context.ModifySsaResponseContext; +import jakarta.ejb.Stateless; +import jakarta.inject.Named; +import jakarta.servlet.http.HttpServletRequest; + +@Stateless +@Named +public class SsaContextBuilder { + + public ModifySsaResponseContext buildModifySsaResponseContext(HttpServletRequest httpRequest, AuthorizationGrant grant, + Client client, AppConfiguration appConfiguration, AttributeService attributeService) { + return new ModifySsaResponseContext(httpRequest, grant, client, appConfiguration, attributeService); + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaJsonService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaJsonService.java new file mode 100644 index 00000000000..1b613a58f56 --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaJsonService.java @@ -0,0 +1,47 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2022, Janssen Project + */ + +package io.jans.as.server.ssa.ws.rs; + +import io.jans.as.common.model.ssa.Ssa; +import io.jans.as.model.json.JsonApplier; +import io.jans.as.model.util.Util; +import jakarta.ejb.Stateless; +import jakarta.inject.Named; +import org.json.JSONException; +import org.json.JSONObject; + +import static io.jans.as.model.ssa.SsaRequestParam.*; + +@Stateless +@Named +public class SsaJsonService { + + public String jsonObjectToString(JSONObject jsonObject) throws JSONException { + return jsonObject.toString(4).replace("\\/", "/"); + } + + public JSONObject getJSONObject(Ssa ssa) throws JSONException { + JSONObject responseJsonObject = new JSONObject(); + JsonApplier.getInstance().apply(ssa, responseJsonObject); + + Util.addToJSONObjectIfNotNull(responseJsonObject, ORG_ID.toString(), ssa.getOrgId()); + Util.addToJSONObjectIfNotNull(responseJsonObject, EXPIRATION.toString(), ssa.getExpiration()); + Util.addToJSONObjectIfNotNull(responseJsonObject, DESCRIPTION.toString(), ssa.getDescription()); + Util.addToJSONObjectIfNotNull(responseJsonObject, SOFTWARE_ID.toString(), ssa.getSoftwareId()); + Util.addToJSONObjectIfNotNull(responseJsonObject, SOFTWARE_ROLES.toString(), ssa.getSoftwareRoles()); + Util.addToJSONObjectIfNotNull(responseJsonObject, GRANT_TYPES.toString(), ssa.getGrantTypes()); + Util.addToJSONObjectIfNotNull(responseJsonObject, ONE_TIME_USE.toString(), ssa.getOneTimeUse()); + Util.addToJSONObjectIfNotNull(responseJsonObject, ROTATE_SSA.toString(), ssa.getRotateSsa()); + return responseJsonObject; + } + + public JSONObject getJSONObject(String jwt) throws JSONException { + JSONObject responseJsonObject = new JSONObject(); + Util.addToJSONObjectIfNotNull(responseJsonObject, "ssa", jwt); + return responseJsonObject; + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebService.java new file mode 100644 index 00000000000..c776e39e1af --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebService.java @@ -0,0 +1,24 @@ +/* + * 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.server.ssa.ws.rs; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; + +public interface SsaRestWebService { + + @POST + @Path("/ssa") + @Produces({MediaType.APPLICATION_JSON}) + Response create(String requestParams, @Context HttpServletRequest httpRequest, @Context SecurityContext securityContext); +} \ No newline at end of file diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceImpl.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceImpl.java new file mode 100644 index 00000000000..69319df73de --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceImpl.java @@ -0,0 +1,26 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2022, Janssen Project + */ + +package io.jans.as.server.ssa.ws.rs; + +import io.jans.as.server.ssa.ws.rs.action.SsaCreateAction; +import jakarta.inject.Inject; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; + +@Path("/") +public class SsaRestWebServiceImpl implements SsaRestWebService { + + @Inject + private SsaCreateAction ssaCreateAction; + + @Override + public Response create(String requestParams, HttpServletRequest httpRequest, SecurityContext securityContext) { + return ssaCreateAction.create(requestParams, httpRequest, securityContext); + } +} \ No newline at end of file diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidator.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidator.java new file mode 100644 index 00000000000..be6a0f5c641 --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidator.java @@ -0,0 +1,55 @@ +/* + * 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.server.ssa.ws.rs; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.ssa.SsaErrorResponseType; +import io.jans.as.server.model.session.SessionClient; +import io.jans.as.server.security.Identity; +import io.jans.as.server.service.ScopeService; +import jakarta.ejb.Stateless; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.ws.rs.core.Response; +import org.slf4j.Logger; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +@Named +@Stateless +public class SsaRestWebServiceValidator { + + @Inject + private Logger log; + + @Inject + private ErrorResponseFactory errorResponseFactory; + + @Inject + private Identity identity; + + @Inject + private ScopeService scopeService; + + public Client validateClient() { + SessionClient sessionClient = identity.getSessionClient(); + if (sessionClient != null) { + log.debug("Client: {}, obtained from session", sessionClient.getClient().getClientId()); + return sessionClient.getClient(); + } + throw errorResponseFactory.createBadRequestException(SsaErrorResponseType.INVALID_CLIENT, "Invalid client"); + } + + public void checkScopesPolicy(Client client, String scope) { + List scopes = scopeService.getScopeIdsByDns(Arrays.stream(client.getScopes()).collect(Collectors.toList())); + if (!scopes.contains(scope)) + throw errorResponseFactory.createWebApplicationException(Response.Status.UNAUTHORIZED, SsaErrorResponseType.UNAUTHORIZED_CLIENT, "Unauthorized client"); + } +} diff --git a/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaService.java b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaService.java new file mode 100644 index 00000000000..4296e02c8cd --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/SsaService.java @@ -0,0 +1,67 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2022, Janssen Project + */ +package io.jans.as.server.ssa.ws.rs; + +import io.jans.as.common.model.ssa.Ssa; +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.jwt.Jwt; +import io.jans.as.server.model.common.ExecutionContext; +import io.jans.as.server.model.token.JwtSigner; +import io.jans.orm.PersistenceEntryManager; +import jakarta.ejb.Stateless; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import org.slf4j.Logger; + +@Stateless +@Named +public class SsaService { + + @Inject + private Logger log; + + @Inject + private PersistenceEntryManager persistenceEntryManager; + + @Inject + private AppConfiguration appConfiguration; + + public void persist(Ssa ssa) { + persistenceEntryManager.persist(ssa); + } + + public void merge(Ssa ssa) { + persistenceEntryManager.merge(ssa); + } + + public Jwt generateJwt(Ssa ssa, ExecutionContext executionContext, WebKeysConfiguration webKeysConfiguration, AbstractCryptoProvider cryptoProvider) { + try { + SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.fromString(appConfiguration.getSsaConfiguration().getSsaSigningAlg()); + JwtSigner jwtSigner = new JwtSigner(appConfiguration, webKeysConfiguration, signatureAlgorithm, null, null, cryptoProvider); + Jwt jwt = jwtSigner.newJwt(); + jwt.getClaims().setJwtId(ssa.getId()); + jwt.getClaims().setIssuedAt(ssa.getCreationDate()); + jwt.getClaims().setExpirationTime(ssa.getExpiration()); + jwt.getClaims().setClaim("software_id", ssa.getSoftwareId()); + jwt.getClaims().setClaim("org_id", ssa.getOrgId()); + jwt.getClaims().setClaim("software_roles", ssa.getSoftwareRoles()); + jwt.getClaims().setClaim("grant_types", ssa.getGrantTypes()); + + Jwt jwr = jwtSigner.sign(); + if (executionContext.getPostProcessor() != null) { + executionContext.getPostProcessor().apply(jwr); + } + return jwr; + } catch (Exception e) { + if (log.isErrorEnabled()) + log.error("Failed to sign session jwt! " + e.getMessage(), e); + throw new RuntimeException(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 new file mode 100644 index 00000000000..b31ee1ae603 --- /dev/null +++ b/jans-auth-server/server/src/main/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateAction.java @@ -0,0 +1,182 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2022, Janssen Project + */ + +package io.jans.as.server.ssa.ws.rs.action; + +import io.jans.as.client.SsaRequest; +import io.jans.as.common.model.registration.Client; +import io.jans.as.common.model.ssa.Ssa; +import io.jans.as.common.service.AttributeService; +import io.jans.as.common.service.common.InumService; +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; +import io.jans.as.model.ssa.SsaErrorResponseType; +import io.jans.as.model.ssa.SsaRequestParam; +import io.jans.as.model.ssa.SsaScopeType; +import io.jans.as.model.token.JsonWebResponse; +import io.jans.as.server.model.common.ExecutionContext; +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; +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 io.jans.as.server.util.ServerUtil; +import jakarta.ejb.Stateless; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import org.json.JSONObject; +import org.slf4j.Logger; + +import java.util.*; +import java.util.function.Function; + +@Stateless +@Named +public class SsaCreateAction { + + @Inject + private Logger log; + + @Inject + private ErrorResponseFactory errorResponseFactory; + + @Inject + private InumService inumService; + + @Inject + private StaticConfiguration staticConfiguration; + + @Inject + private SsaJsonService ssaJsonService; + + @Inject + private SsaService ssaService; + + @Inject + private AppConfiguration appConfiguration; + + @Inject + private AttributeService attributeService; + + @Inject + private ModifySsaResponseService modifySsaResponseService; + + @Inject + private SsaRestWebServiceValidator ssaRestWebServiceValidator; + + @Inject + private WebKeysConfiguration webKeysConfiguration; + + @Inject + private SsaContextBuilder ssaContextBuilder; + + public Response create(String requestParams, HttpServletRequest httpRequest, SecurityContext securityContext) { + errorResponseFactory.validateFeatureEnabled(FeatureFlagType.SSA); + Response.ResponseBuilder builder = Response.status(Response.Status.CREATED); + try { + JSONObject jsonRequest = new JSONObject(requestParams); + final SsaRequest ssaRequest = SsaRequest.fromJson(jsonRequest); + log.debug("Attempting to create ssa: {}", ssaRequest); + + String ssaBaseDN = staticConfiguration.getBaseDn().getSsa(); + String inum = inumService.generateDefaultId(); + Client client = ssaRestWebServiceValidator.validateClient(); + ssaRestWebServiceValidator.checkScopesPolicy(client, SsaScopeType.SSA_ADMIN.getValue()); + + final Ssa ssa = new Ssa(); + ssa.setDn("inum=" + inum + "," + ssaBaseDN); + ssa.setId(inum); + ssa.setDeletable(true); + ssa.setOrgId(ssaRequest.getOrgId()); + ssa.setExpiration(getExpiration(ssaRequest)); + ssa.setDescription(ssaRequest.getDescription()); + ssa.setSoftwareId(ssaRequest.getSoftwareId()); + ssa.setSoftwareRoles(ssaRequest.getSoftwareRoles()); + ssa.setGrantTypes(ssaRequest.getGrantTypes()); + ssa.setCustomAttributes(getCustomAttributes(jsonRequest)); + ssa.setClientDn(client.getDn()); + ssa.setOneTimeUse(ssaRequest.getOneTimeUse()); + ssa.setRotateSsa(ssaRequest.getRotateSsa()); + ssaService.persist(ssa); + log.info("Ssa created: {}", ssa); + + ModifySsaResponseContext context = ssaContextBuilder.buildModifySsaResponseContext(httpRequest, null, client, appConfiguration, attributeService); + Function postProcessor = modifySsaResponseService.buildCreateProcessor(context); + final ExecutionContext executionContext = context.toExecutionContext(); + executionContext.setPostProcessor(postProcessor); + + Jwt jwt = ssaService.generateJwt(ssa, executionContext, webKeysConfiguration, null); + 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(); + } + + private Map getCustomAttributes(JSONObject jsonRequest) { + if (appConfiguration.getSsaConfiguration().getSsaCustomAttributes().isEmpty()) + return new HashMap<>(); + + Map customAttributes = new HashMap<>(); + appConfiguration.getSsaConfiguration().getSsaCustomAttributes().forEach(customAttrKey -> { + if (jsonRequest.has(customAttrKey)) { + customAttributes.put(customAttrKey, jsonRequest.getString(customAttrKey)); + } else { + log.warn("Field: {} is not found in request", customAttrKey); + } + }); + + List ssaFields = new ArrayList<>(); + ssaFields.add(SsaRequestParam.DESCRIPTION.getName()); + ssaFields.add(SsaRequestParam.GRANT_TYPES.getName()); + ssaFields.add(SsaRequestParam.SOFTWARE_ROLES.getName()); + ssaFields.add(SsaRequestParam.ORG_ID.getName()); + ssaFields.add(SsaRequestParam.EXPIRATION.getName()); + ssaFields.add(SsaRequestParam.SOFTWARE_ID.getName()); + ssaFields.add(SsaRequestParam.ONE_TIME_USE.getName()); + ssaFields.add(SsaRequestParam.ROTATE_SSA.getName()); + ssaFields.addAll(appConfiguration.getSsaConfiguration().getSsaCustomAttributes()); + jsonRequest.toMap().forEach((k, v) -> { + if (!ssaFields.contains(k)) log.warn("Field: {} is not defined", k); + }); + + return customAttributes; + } + + private Date getExpiration(SsaRequest ssaRequest) { + Calendar calendar = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC")); + if (ssaRequest.getExpiration() != null && ssaRequest.getExpiration() > 0) { + calendar.setTimeInMillis(ssaRequest.getExpiration() * 1000L); + return calendar.getTime(); + } + calendar.add(Calendar.DATE, appConfiguration.getSsaConfiguration().getSsaExpirationInDays()); + return calendar.getTime(); + } +} diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaJsonServiceTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaJsonServiceTest.java new file mode 100644 index 00000000000..ff107873d5d --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaJsonServiceTest.java @@ -0,0 +1,100 @@ +package io.jans.as.server.ssa.ws.rs; + +import io.jans.as.common.model.ssa.Ssa; +import org.json.JSONObject; +import org.mockito.InjectMocks; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.*; + +import static io.jans.as.model.ssa.SsaRequestParam.*; +import static org.testng.Assert.*; + +@Listeners(MockitoTestNGListener.class) +public class SsaJsonServiceTest { + + @InjectMocks + private SsaJsonService ssaJsonService; + + @Test + public void jsonObjectToString_jsonObject_validJsonString() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("a", "val1"); + jsonObject.put("b", "val2"); + + String json = ssaJsonService.jsonObjectToString(jsonObject); + assertNotNull(json, "json response is null"); + JSONObject jsonResponse = new JSONObject(json); + assertTrue(jsonResponse.has("a")); + assertTrue(jsonResponse.has("b")); + assertEquals(jsonResponse.getString("a"), "val1"); + assertEquals(jsonResponse.getString("b"), "val2"); + } + + @Test + public void getJSONObject_ssa_validJsonObject() { + Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + calendar.add(Calendar.HOUR, 24); + + Ssa ssa = new Ssa(); + ssa.setId(UUID.randomUUID().toString()); + ssa.setOrgId(1L); + ssa.setExpiration(calendar.getTime()); + ssa.setDescription("Test description"); + ssa.setSoftwareId("scan-api-test"); + ssa.setSoftwareRoles(Collections.singletonList("passwurd")); + ssa.setGrantTypes(Collections.singletonList("client_credentials")); + ssa.setOneTimeUse(true); + ssa.setRotateSsa(true); + + JSONObject jsonObject = ssaJsonService.getJSONObject(ssa); + assertNotNull(jsonObject, "jsonObject response is null"); + assertTrue(jsonObject.has(ORG_ID.toString())); + assertEquals(jsonObject.get(ORG_ID.toString()), ssa.getOrgId()); + assertTrue(jsonObject.has(EXPIRATION.toString())); + assertEquals(jsonObject.get(EXPIRATION.toString()), ssa.getExpiration()); + assertTrue(jsonObject.has(DESCRIPTION.toString())); + assertEquals(jsonObject.get(DESCRIPTION.toString()), ssa.getDescription()); + assertTrue(jsonObject.has(SOFTWARE_ID.toString())); + assertEquals(jsonObject.get(SOFTWARE_ID.toString()), ssa.getSoftwareId()); + assertTrue(jsonObject.has(SOFTWARE_ROLES.toString())); + assertEquals(jsonObject.get(SOFTWARE_ROLES.toString()), ssa.getSoftwareRoles()); + assertTrue(jsonObject.has(GRANT_TYPES.toString())); + assertEquals(jsonObject.get(GRANT_TYPES.toString()), ssa.getGrantTypes()); + assertTrue(jsonObject.has(ONE_TIME_USE.toString())); + assertEquals(jsonObject.get(ONE_TIME_USE.toString()), ssa.getOneTimeUse()); + assertTrue(jsonObject.has(ROTATE_SSA.toString())); + assertEquals(jsonObject.get(ROTATE_SSA.toString()), ssa.getRotateSsa()); + } + + @Test + public void getJSONObject_jwt_validJsonObject() { + String jwt = "eyJraWQiOiIxOGZlOTQ2YS1mMjkyLTQ1MTgtYWRmYi00ZTA1ZDAzODM0MDBfc2lnX3JzNTEyIiwidHlwIjoiand0IiwiYWxnIjoiUlM1MTIiLCJjdXN0b21faGVhZGVyX25hbWUiOiJjdXN0b21faGVhZGVyX3ZhbHVlIn0.eyJzb2Z0d2FyZV9pZCI6ImdsdXUtc2Nhbi1hcGkiLCJncmFudF90eXBlcyI6WyJjbGllbnRfY3JlZGVudGlhbHMiXSwib3JnX2lkIjoxLCJpc3MiOiJodHRwczovL2phbnMubG9jYWxob3N0Iiwic29mdHdhcmVfcm9sZXMiOlsicGFzc3d1cmQiXSwiZXhwIjoxNjY2MTM3MjUzLCJpYXQiOjE2NjM1NDUyNTMsImN1c3RvbV9jbGFpbV9uYW1lIjoiY3VzdG9tX2NsYWltX3ZhbHVlIiwianRpIjoiY2M4OTQ0MjItMzRlOC00MzUxLTkzZWEtMDkzYmEzN2RjOTIyIn0.02GCtFMpX_srmQs5neNv92Du4bsHsxzROQpx4Zf8XMnv7F3AYw_czkBrGsVHoJLRFttl4esHgfY4vhCp9uYhNxaM6C8tscpIT7c26C2F378inEACC_gh3_v-AEogH_KhUHeDxyD9ZVCWVSHXoc-jN8BAPqIqyK1ndmWO-l8cFsSyjuCJYTJDcYa-E1lsRlUjHQaLQXIRaTWm_rA-GFeaacQQ6AXyIwVNO5jcMXpgS0p0QY9F1jPSpEus44inQ88NInYHHzPZKDgjeP8py6K9TFVU_ABh4QR6JI62ZNnddEt676I2AYuvekV0PtJ8hTUJKPcBBiAen05w2abwh3rHRg"; + + JSONObject jsonObject = ssaJsonService.getJSONObject(jwt); + assertNotNull(jsonObject, "jsonObject response is null"); + assertTrue(jsonObject.has("ssa")); + assertEquals(jsonObject.get("ssa"), jwt); + } + + @Test + public void getJSONObject_jwtNull_emptyJsonObject() { + String jwt = null; + + JSONObject jsonObject = ssaJsonService.getJSONObject(jwt); + assertNotNull(jsonObject, "jsonObject response is null"); + assertTrue(jsonObject.isEmpty()); + } + + @Test + public void getJSONObject_jwtBlank_validWithBlankValue() { + String jwt = ""; + + JSONObject jsonObject = ssaJsonService.getJSONObject(jwt); + assertNotNull(jsonObject, "jsonObject response is null"); + assertTrue(jsonObject.has("ssa")); + assertEquals(jsonObject.get("ssa"), ""); + } +} \ No newline at end of file diff --git a/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceImplTest.java b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceImplTest.java new file mode 100644 index 00000000000..d8546f6e28a --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceImplTest.java @@ -0,0 +1,36 @@ +package io.jans.as.server.ssa.ws.rs; + +import io.jans.as.server.ssa.ws.rs.action.SsaCreateAction; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.testng.Assert.assertNotNull; + +@Listeners(MockitoTestNGListener.class) +public class SsaRestWebServiceImplTest { + + @InjectMocks + private SsaRestWebServiceImpl ssaRestWebService; + + @Mock + private SsaCreateAction ssaCreateAction; + + @Test + public void create_validParams_validResponse() { + when(ssaCreateAction.create(anyString(), any(), any())).thenReturn(mock(Response.class)); + Response response = ssaRestWebService.create("test request", mock(HttpServletRequest.class), mock(SecurityContext.class)); + assertNotNull(response, "response is null"); + + verify(ssaCreateAction).create(anyString(), any(), any()); + verifyNoMoreInteractions(ssaCreateAction); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000000..848bdae92e6 --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaRestWebServiceValidatorTest.java @@ -0,0 +1,108 @@ +package io.jans.as.server.ssa.ws.rs; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.ssa.SsaErrorResponseType; +import io.jans.as.server.model.session.SessionClient; +import io.jans.as.server.security.Identity; +import io.jans.as.server.service.ScopeService; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +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 java.util.Collections; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.testng.Assert.assertNotNull; + +@Listeners(MockitoTestNGListener.class) +public class SsaRestWebServiceValidatorTest { + + @InjectMocks + private SsaRestWebServiceValidator ssaRestWebServiceValidator; + + @Mock + private Identity identity; + + @Mock + private Logger log; + + @Mock + private ErrorResponseFactory errorResponseFactory; + + @Mock + private ScopeService scopeService; + + @Test + public void validateClient_sessionClient_validClient() { + SessionClient sessionClient = new SessionClient(); + Client client = new Client(); + client.setClientId("test_id"); + sessionClient.setClient(client); + doReturn(sessionClient).when(identity).getSessionClient(); + + Client clientAux = ssaRestWebServiceValidator.validateClient(); + assertNotNull(clientAux, "client is null"); + verify(log).debug(anyString(), anyString()); + } + + @Test + public void validateClient_sessionClientNull_invalidClientResponse() { + WebApplicationException error = new WebApplicationException(Response + .status(Response.Status.BAD_REQUEST) + .entity("Invalid client") + .type(MediaType.APPLICATION_JSON_TYPE) + .build()); + + when(identity.getSessionClient()).thenReturn(null); + when(errorResponseFactory.createBadRequestException(eq(SsaErrorResponseType.INVALID_CLIENT), anyString())).thenThrow(error); + + try { + ssaRestWebServiceValidator.validateClient(); + } catch (WebApplicationException e) { + assertNotNull(e, "WebApplicationException is null"); + assertNotNull(e.getResponse(), "WebApplicationException Response is null"); + } + verify(identity).getSessionClient(); + verifyNoInteractions(log); + } + + @Test + public void checkScopesPolicy_clientAndScopeConstains_validScope() { + String scope = "test_id"; + Client client = new Client(); + client.setScopes(new String[]{}); + when(scopeService.getScopeIdsByDns(anyList())).thenReturn(Collections.singletonList("test_id")); + + ssaRestWebServiceValidator.checkScopesPolicy(client, scope); + verifyNoInteractions(errorResponseFactory); + } + + @Test + public void checkScopesPolicy_clientAndScopeNotConstains_unauthorizedResponse() { + String scope = "test_id"; + Client client = new Client(); + client.setScopes(new String[]{}); + WebApplicationException error = new WebApplicationException(Response + .status(Response.Status.UNAUTHORIZED) + .entity("Invalid client") + .type(MediaType.APPLICATION_JSON_TYPE) + .build()); + when(scopeService.getScopeIdsByDns(anyList())).thenReturn(Collections.singletonList("test_id_fail")); + when(errorResponseFactory.createWebApplicationException(eq(Response.Status.UNAUTHORIZED), eq(SsaErrorResponseType.UNAUTHORIZED_CLIENT), anyString())).thenThrow(error); + + try { + ssaRestWebServiceValidator.checkScopesPolicy(client, scope); + } catch (WebApplicationException e) { + assertNotNull(e, "WebApplicationException is null"); + assertNotNull(e.getResponse(), "WebApplicationException Response is null"); + } + } +} \ 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 new file mode 100644 index 00000000000..76103eb5e64 --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/SsaServiceTest.java @@ -0,0 +1,275 @@ +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.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.util.Base64Util; +import io.jans.as.server.model.common.ExecutionContext; +import io.jans.orm.PersistenceEntryManager; +import org.json.JSONObject; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.slf4j.Logger; +import org.testng.annotations.BeforeMethod; +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 org.mockito.Mockito.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +@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; + + @InjectMocks + private SsaService ssaService; + + @Mock + private AppConfiguration appConfiguration; + + @Mock + private PersistenceEntryManager persistenceEntryManager; + + 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(); + ssa.setId(UUID.randomUUID().toString()); + ssa.setOrgId(1L); + ssa.setExpiration(calendar.getTime()); + ssa.setDescription("Test description"); + ssa.setSoftwareId("scan-api-test"); + ssa.setSoftwareRoles(Collections.singletonList("passwurd")); + ssa.setGrantTypes(Collections.singletonList("client_credentials")); + ssa.setOneTimeUse(true); + ssa.setRotateSsa(true); + } + + @Test + public void persist_ssa_valid() { + ssaService.persist(ssa); + + verify(persistenceEntryManager).persist(any(Ssa.class)); + verifyNoInteractions(log); + + ArgumentCaptor ssaArgumentCaptor = ArgumentCaptor.forClass(Ssa.class); + verify(persistenceEntryManager).persist(ssaArgumentCaptor.capture()); + assertSsaWithAux(ssa, ssaArgumentCaptor.getValue()); + } + + @Test + public void merge_ssa_valid() { + ssaService.merge(ssa); + + verify(persistenceEntryManager).merge(any(Ssa.class)); + verifyNoInteractions(log); + + ArgumentCaptor ssaArgumentCaptor = ArgumentCaptor.forClass(Ssa.class); + verify(persistenceEntryManager).merge(ssaArgumentCaptor.capture()); + assertSsaWithAux(ssa, ssaArgumentCaptor.getValue()); + } + + @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); + verify(executionContext).getPostProcessor(); + } + + @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); + verify(executionContext, times(2)).getPostProcessor(); + } + + @Test + public void generateJwt_exceptionWithIsErrorEnabledFalse_runtimeException() { + when(log.isErrorEnabled()).thenReturn(false); + try { + ssaService.generateJwt(ssa, mock(ExecutionContext.class), mock(WebKeysConfiguration.class), cryptoProvider); + } catch (Exception e) { + assertNotNull(e, "Exception is null"); + } + verify(log).isErrorEnabled(); + verifyNoMoreInteractions(log); + } + + @Test + public void generateJwt_exceptionWithIsErrorEnabledTrue_runtimeException() { + when(log.isErrorEnabled()).thenReturn(true); + try { + ssaService.generateJwt(ssa, mock(ExecutionContext.class), mock(WebKeysConfiguration.class), cryptoProvider); + } catch (Exception e) { + assertNotNull(e, "Exception is null"); + } + verify(log).isErrorEnabled(); + verify(log).error(anyString(), any(Throwable.class)); + } + + private static void assertSsaJwt(JSONWebKey jsonWebKey, 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"); + + JwtClaims jwtClaims = jwt.getClaims(); + assertNotNull(jwtClaims.getClaim("org_id"), "The org_id in jwt is null"); + assertEquals(jwtClaims.getClaim("org_id"), ssa.getOrgId()); + assertNotNull(jwtClaims.getClaim("software_id"), "The software_id in jwt is null"); + assertEquals(jwtClaims.getClaim("software_id"), ssa.getSoftwareId()); + assertNotNull(jwtClaims.getClaim("software_roles"), "The software_roles in jwt is null"); + assertEquals(jwtClaims.getClaim("software_roles"), ssa.getSoftwareRoles()); + assertNotNull(jwtClaims.getClaim("grant_types"), "The grant_types in jwt is null"); + assertEquals(jwtClaims.getClaim("grant_types"), ssa.getGrantTypes()); + + assertNotNull(jwtClaims.getClaim("jti"), "The jti in jwt is null"); + assertEquals(jwtClaims.getClaim("jti"), ssa.getId()); + assertNotNull(jwtClaims.getClaim("iss"), "The iss in jwt is null"); + assertEquals(jwtClaims.getClaim("iss"), issuer); + assertNotNull(jwtClaims.getClaim("iat"), "The iat in jwt is null"); + assertEquals(jwtClaims.getClaim("iat"), ssa.getCreationDate()); + assertNotNull(jwtClaims.getClaim("exp"), "The exp in jwt is null"); + assertEquals(jwtClaims.getClaim("exp"), ssa.getExpiration()); + } + + private static void assertSsaWithAux(Ssa ssa, Ssa ssaAux) { + assertNotNull(ssaAux, "ssa is null"); + assertNotNull(ssaAux.getId(), "ssa id is null"); + assertEquals(ssaAux.getId(), ssa.getId()); + assertNotNull(ssaAux.getOrgId(), "ssa org_id is null"); + assertEquals(ssaAux.getOrgId(), ssa.getOrgId()); + assertNotNull(ssaAux.getExpiration(), "ssa expiration is null"); + assertEquals(ssaAux.getExpiration(), ssa.getExpiration()); + assertNotNull(ssaAux.getDescription(), "ssa description is null"); + assertEquals(ssaAux.getDescription(), ssa.getDescription()); + assertNotNull(ssaAux.getSoftwareId(), "ssa software_id is null"); + assertEquals(ssaAux.getSoftwareId(), ssa.getSoftwareId()); + assertNotNull(ssaAux.getSoftwareRoles(), "ssa software_roles is null"); + assertEquals(ssaAux.getSoftwareRoles(), ssa.getSoftwareRoles()); + assertNotNull(ssaAux.getGrantTypes(), "ssa grant_types is null"); + assertEquals(ssaAux.getGrantTypes(), ssa.getGrantTypes()); + assertNotNull(ssaAux.getOneTimeUse(), "ssa one_time_use is null"); + assertEquals(ssaAux.getOneTimeUse(), ssa.getOneTimeUse()); + assertNotNull(ssaAux.getRotateSsa(), "ssa rotate_ssa is null"); + assertEquals(ssaAux.getRotateSsa(), ssa.getRotateSsa()); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000000..b1122eb3e43 --- /dev/null +++ b/jans-auth-server/server/src/test/java/io/jans/as/server/ssa/ws/rs/action/SsaCreateActionTest.java @@ -0,0 +1,271 @@ +package io.jans.as.server.ssa.ws.rs.action; + +import io.jans.as.client.SsaRequest; +import io.jans.as.common.model.registration.Client; +import io.jans.as.common.model.ssa.Ssa; +import io.jans.as.common.service.AttributeService; +import io.jans.as.common.service.common.InumService; +import io.jans.as.model.config.BaseDnConfiguration; +import io.jans.as.model.config.StaticConfiguration; +import io.jans.as.model.configuration.AppConfiguration; +import io.jans.as.model.error.ErrorResponseFactory; +import io.jans.as.model.jwt.Jwt; +import io.jans.as.model.ssa.SsaConfiguration; +import io.jans.as.model.ssa.SsaErrorResponseType; +import io.jans.as.server.model.common.ExecutionContext; +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; +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.servlet.http.HttpServletRequest; +import jakarta.ws.rs.WebApplicationException; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.SecurityContext; +import org.json.JSONObject; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.testng.MockitoTestNGListener; +import org.slf4j.Logger; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Listeners; +import org.testng.annotations.Test; + +import java.util.*; + +import static io.jans.as.model.ssa.SsaRequestParam.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +@Listeners(MockitoTestNGListener.class) +public class SsaCreateActionTest { + + @InjectMocks + private SsaCreateAction ssaCreateAction; + + @Mock + private AppConfiguration appConfiguration; + + @Mock + private ErrorResponseFactory errorResponseFactory; + + @Mock + private StaticConfiguration staticConfiguration; + + @Mock + private InumService inumService; + + @Mock + private SsaRestWebServiceValidator ssaRestWebServiceValidator; + + @Mock + private SsaService ssaService; + + @Mock + private AttributeService attributeService; + + @Mock + private Logger log; + + @Mock + private SsaContextBuilder ssaContextBuilder; + + @Mock + private ModifySsaResponseService modifySsaResponseService; + + @Mock + private SsaJsonService ssaJsonService; + + private Ssa ssa; + private JSONObject requestJson; + + @BeforeMethod + public void setUp() { + Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + calendar.add(Calendar.HOUR, 24); + ssa = new Ssa(); + ssa.setOrgId(1L); + ssa.setExpiration(calendar.getTime()); + ssa.setDescription("test description"); + ssa.setSoftwareId("gluu-scan-api"); + ssa.setSoftwareRoles(Collections.singletonList("passwurd")); + ssa.setGrantTypes(Collections.singletonList("client_credentials")); + ssa.setOneTimeUse(true); + ssa.setRotateSsa(true); + + requestJson = new JSONObject(); + requestJson.put(ORG_ID.toString(), ssa.getOrgId()); + requestJson.put(EXPIRATION.toString(), ssa.getExpiration().getTime() / 1000L); + requestJson.put(DESCRIPTION.toString(), ssa.getDescription()); + requestJson.put(SOFTWARE_ID.toString(), ssa.getSoftwareId()); + requestJson.put(SOFTWARE_ROLES.toString(), ssa.getSoftwareRoles()); + requestJson.put(GRANT_TYPES.toString(), ssa.getGrantTypes()); + requestJson.put(ONE_TIME_USE.toString(), ssa.getOneTimeUse()); + requestJson.put(ROTATE_SSA.toString(), ssa.getRotateSsa()); + } + + @Test + public void create_request_valid() { + BaseDnConfiguration baseDnConfiguration = new BaseDnConfiguration(); + baseDnConfiguration.setSsa("ou=ssa,o=jans"); + when(staticConfiguration.getBaseDn()).thenReturn(baseDnConfiguration); + when(inumService.generateDefaultId()).thenReturn(UUID.randomUUID().toString()); + Client client = new Client(); + client.setDn("inum=0000,ou=clients,o=jans"); + when(ssaRestWebServiceValidator.validateClient()).thenReturn(client); + + HttpServletRequest httpRequest = mock(HttpServletRequest.class); + SecurityContext securityContext = mock(SecurityContext.class); + + ExecutionContext executionContext = mock(ExecutionContext.class); + ModifySsaResponseContext context = mock(ModifySsaResponseContext.class); + when(ssaContextBuilder.buildModifySsaResponseContext(any(), any(), any(), 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(ssaJsonService.getJSONObject(anyString())).thenReturn(mock(JSONObject.class)); + when(ssaJsonService.jsonObjectToString(any())).thenReturn("{\"ssa\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c\"}"); + when(appConfiguration.getSsaConfiguration()).thenReturn(new SsaConfiguration()); + + Response response = ssaCreateAction.create(requestJson.toString(), httpRequest, securityContext); + assertNotNull(response, "response is null"); + assertNotNull(response.getEntity(), "response entity is null"); + assertEquals(response.getStatus(), Response.Status.CREATED.getStatusCode()); + + verify(errorResponseFactory).validateFeatureEnabled(any()); + verify(log).debug(anyString(), any(SsaRequest.class)); + verify(staticConfiguration).getBaseDn(); + verify(ssaRestWebServiceValidator).validateClient(); + 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(modifySsaResponseService).buildCreateProcessor(any()); + verify(context).toExecutionContext(); + verify(ssaService).generateJwt(any(), any(), any(), any()); + verify(ssaJsonService).getJSONObject(anyString()); + verify(ssaJsonService).jsonObjectToString(any()); + + verifyNoInteractions(attributeService); + verify(appConfiguration).getSsaConfiguration(); + + ArgumentCaptor ssaCaptor = ArgumentCaptor.forClass(Ssa.class); + verify(ssaService).persist(ssaCaptor.capture()); + Ssa ssaAux = ssaCaptor.getValue(); + assertNotNull(ssaAux, "ssa is null"); + assertNotNull(ssaAux.getOrgId(), "ssa org_id is null"); + assertEquals(ssaAux.getOrgId(), ssa.getOrgId()); + assertNotNull(ssaAux.getDescription(), "ssa description is null"); + assertEquals(ssaAux.getDescription(), ssa.getDescription()); + assertNotNull(ssaAux.getSoftwareId(), "ssa software_id is null"); + assertEquals(ssaAux.getSoftwareId(), ssa.getSoftwareId()); + assertNotNull(ssaAux.getSoftwareRoles(), "ssa software_roles is null"); + assertEquals(ssaAux.getSoftwareRoles(), ssa.getSoftwareRoles()); + assertNotNull(ssaAux.getGrantTypes(), "ssa grant_types is null"); + assertEquals(ssaAux.getGrantTypes(), ssa.getGrantTypes()); + assertNotNull(ssaAux.getOneTimeUse(), "ssa one_time_use is null"); + assertEquals(ssaAux.getOneTimeUse(), ssa.getOneTimeUse()); + assertNotNull(ssaAux.getRotateSsa(), "ssa rotate_ssa is null"); + assertEquals(ssaAux.getRotateSsa(), ssa.getRotateSsa()); + } + + @Test + public void create_disabledSsaComponent_forbiddenResponse() { + WebApplicationException error = new WebApplicationException( + Response.status(Response.Status.FORBIDDEN) + .entity("Component is disabled on server.") + .type(MediaType.APPLICATION_JSON_TYPE) + .build()); + doThrow(error).when(errorResponseFactory).validateFeatureEnabled(any()); + + try { + ssaCreateAction.create(requestJson.toString(), mock(HttpServletRequest.class), mock(SecurityContext.class)); + } catch (WebApplicationException e) { + assertNotNull(e, "Exception is null"); + assertNotNull(e.getResponse(), "Exception Response is null"); + } + verifyNoInteractions(log, staticConfiguration, inumService, ssaRestWebServiceValidator, ssaService, ssaContextBuilder, + modifySsaResponseService, ssaJsonService, appConfiguration, attributeService); + } + + @Test + public void create_invalidClientAndIsErrorEnabledFalse_badRequestResponse() { + BaseDnConfiguration baseDnConfiguration = new BaseDnConfiguration(); + baseDnConfiguration.setSsa("ou=ssa,o=jans"); + when(staticConfiguration.getBaseDn()).thenReturn(baseDnConfiguration); + when(inumService.generateDefaultId()).thenReturn(UUID.randomUUID().toString()); + WebApplicationException error = new WebApplicationException( + Response.status(Response.Status.BAD_REQUEST) + .entity("Invalid client") + .build()); + doThrow(error).when(ssaRestWebServiceValidator).validateClient(); + when(log.isErrorEnabled()).thenReturn(Boolean.FALSE); + + try { + ssaCreateAction.create(requestJson.toString(), mock(HttpServletRequest.class), mock(SecurityContext.class)); + } catch (WebApplicationException e) { + assertNotNull(e, "Exception is null"); + assertNotNull(e.getResponse(), "Exception Response is null"); + } + verify(ssaRestWebServiceValidator).validateClient(); + verify(log).isErrorEnabled(); + verify(log, never()).error(anyString(), any(WebApplicationException.class)); + verify(ssaRestWebServiceValidator, never()).checkScopesPolicy(any(), anyString()); + verify(ssaService, never()).persist(any(Ssa.class)); + verifyNoInteractions(ssaService, ssaContextBuilder, modifySsaResponseService, ssaJsonService, appConfiguration, attributeService); + } + + @Test + public void create_invalidClientAndIsErrorEnabledTrue_badRequestResponse() { + BaseDnConfiguration baseDnConfiguration = new BaseDnConfiguration(); + baseDnConfiguration.setSsa("ou=ssa,o=jans"); + when(staticConfiguration.getBaseDn()).thenReturn(baseDnConfiguration); + when(inumService.generateDefaultId()).thenReturn(UUID.randomUUID().toString()); + WebApplicationException error = new WebApplicationException( + Response.status(Response.Status.BAD_REQUEST) + .entity("Invalid client") + .build()); + doThrow(error).when(ssaRestWebServiceValidator).validateClient(); + when(log.isErrorEnabled()).thenReturn(Boolean.TRUE); + + try { + ssaCreateAction.create(requestJson.toString(), mock(HttpServletRequest.class), mock(SecurityContext.class)); + } catch (WebApplicationException e) { + assertNotNull(e, "Exception is null"); + assertNotNull(e.getResponse(), "Exception Response is null"); + } + verify(ssaRestWebServiceValidator).validateClient(); + verify(log).isErrorEnabled(); + verify(log).error(anyString(), any(WebApplicationException.class)); + verify(ssaRestWebServiceValidator, never()).checkScopesPolicy(any(), anyString()); + verify(ssaService, never()).persist(any(Ssa.class)); + verifyNoInteractions(ssaService, ssaContextBuilder, modifySsaResponseService, ssaJsonService, appConfiguration, attributeService); + } + + @Test + public void create_invalidClient_internalServerResponse() { + WebApplicationException error = new WebApplicationException( + Response.status(Response.Status.INTERNAL_SERVER_ERROR) + .entity("Unknown error") + .build()); + when(errorResponseFactory.createWebApplicationException(any(Response.Status.class), any(SsaErrorResponseType.class), anyString())).thenThrow(error); + + try { + ssaCreateAction.create(requestJson.toString(), mock(HttpServletRequest.class), mock(SecurityContext.class)); + } catch (WebApplicationException e) { + assertNotNull(e, "Exception is null"); + } + verify(staticConfiguration).getBaseDn(); + verify(log).debug(anyString(), any(SsaRequest.class)); + verify(log).error(eq(null), any(NullPointerException.class)); + verify(ssaRestWebServiceValidator, never()).checkScopesPolicy(any(), anyString()); + verify(ssaService, never()).persist(any(Ssa.class)); + verifyNoInteractions(ssaService, ssaContextBuilder, modifySsaResponseService, ssaJsonService, appConfiguration, attributeService); + } +} \ No newline at end of file diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java b/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java index b18014e8be4..6e0c15293ee 100644 --- a/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/CustomScriptType.java @@ -48,6 +48,8 @@ import io.jans.model.custom.script.type.session.DummyApplicationSessionType; import io.jans.model.custom.script.type.spontaneous.DummySpontaneousScopeType; import io.jans.model.custom.script.type.spontaneous.SpontaneousScopeType; +import io.jans.model.custom.script.type.ssa.DummyModifySsaResponseType; +import io.jans.model.custom.script.type.ssa.ModifySsaResponseType; import io.jans.model.custom.script.type.token.DummyUpdateTokenType; import io.jans.model.custom.script.type.token.UpdateTokenType; import io.jans.model.custom.script.type.uma.UmaClaimsGatheringType; @@ -99,7 +101,9 @@ public enum CustomScriptType implements AttributeEnum { IDP("idp", "Idp Extension", IdpType.class, CustomScript.class, "IdpExtension", new DummyIdpType()), DISCOVERY("discovery", "Discovery", DiscoveryType.class, CustomScript.class, "Discovery", new DummyDiscoveryType()), UPDATE_TOKEN("update_token", "Update Token", UpdateTokenType.class, CustomScript.class, "UpdateToken", new DummyUpdateTokenType()), - CONFIG_API("config_api_auth", "Config Api Auth", ConfigApiType.class, CustomScript.class,"ConfigApiAuthorization", new DummyConfigApiType()); + CONFIG_API("config_api_auth", "Config Api Auth", ConfigApiType.class, CustomScript.class,"ConfigApiAuthorization", new DummyConfigApiType()), + MODIFY_SSA_RESPONSE("modify_ssa_response", "Modify SSA Response", ModifySsaResponseType.class, CustomScript.class, "ModifySsaResponse", new DummyModifySsaResponseType()), + ; private String value; private String displayName; diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/type/ssa/DummyModifySsaResponseType.java b/jans-core/script/src/main/java/io/jans/model/custom/script/type/ssa/DummyModifySsaResponseType.java new file mode 100644 index 00000000000..15cd89fc846 --- /dev/null +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/type/ssa/DummyModifySsaResponseType.java @@ -0,0 +1,40 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2021, Janssen Project + */ + +package io.jans.model.custom.script.type.ssa; + +import io.jans.model.SimpleCustomProperty; +import io.jans.model.custom.script.model.CustomScript; + +import java.util.Map; + +public class DummyModifySsaResponseType implements ModifySsaResponseType { + + @Override + public boolean init(Map configurationAttributes) { + return true; + } + + @Override + public boolean init(CustomScript customScript, Map configurationAttributes) { + return true; + } + + @Override + public boolean destroy(Map configurationAttributes) { + return true; + } + + @Override + public int getApiVersion() { + return 1; + } + + @Override + public boolean create(Object jwr, Object tokenContext) { + return false; + } +} \ No newline at end of file diff --git a/jans-core/script/src/main/java/io/jans/model/custom/script/type/ssa/ModifySsaResponseType.java b/jans-core/script/src/main/java/io/jans/model/custom/script/type/ssa/ModifySsaResponseType.java new file mode 100644 index 00000000000..7bd67126152 --- /dev/null +++ b/jans-core/script/src/main/java/io/jans/model/custom/script/type/ssa/ModifySsaResponseType.java @@ -0,0 +1,14 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2021, Janssen Project + */ + +package io.jans.model.custom.script.type.ssa; + +import io.jans.model.custom.script.type.BaseExternalType; + +public interface ModifySsaResponseType extends BaseExternalType { + + boolean create(Object jsonWebResponse, Object tokenContext); +}