diff --git a/pom.xml b/pom.xml index 5d875682..99a883eb 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.folio mod-login-saml jar - 2.9.0-SNAPSHOT + 2.8.2-SNAPSHOT mod-login-saml @@ -97,7 +97,7 @@ org.apache.logging.log4j - log4j-slf4j-impl + log4j-slf4j2-impl diff --git a/ramls/saml-login.raml b/ramls/saml-login.raml index 3f2c2bf0..0310fda2 100644 --- a/ramls/saml-login.raml +++ b/ramls/saml-login.raml @@ -59,7 +59,7 @@ types: example: "Bad request" /callback: post: - description: Redirect browser to sso-landing page with generated token. Deprecated. + description: Redirect browser to sso-landing page with generated token. body: application/octet-stream: type: string diff --git a/ramls/schemas/SamlConfig.json b/ramls/schemas/SamlConfig.json index 8f6f4508..51ea8c83 100644 --- a/ramls/schemas/SamlConfig.json +++ b/ramls/schemas/SamlConfig.json @@ -43,6 +43,11 @@ "description": "Where the IDP should call back after login is successful. Either callback or callback-with-expiry. Defaults to callback-with-expiry if not present.", "type": "string", "required": false + }, + "useSecureTokens": { + "type": "boolean", + "description": "When present, and true, and when callback is configured with the value 'callback', enables the refresh token payload on the /callback endpoint.", + "required": false } } } diff --git a/ramls/schemas/SamlConfigRequest.json b/ramls/schemas/SamlConfigRequest.json index 9000fb27..5aceaea3 100644 --- a/ramls/schemas/SamlConfigRequest.json +++ b/ramls/schemas/SamlConfigRequest.json @@ -43,6 +43,11 @@ "description": "Where the IDP should call back after login is successful. Either callback or callback-with-expiry. Defaults to callback-with-expiry if not present.", "type": "string", "required": false + }, + "useSecureTokens": { + "type": "boolean", + "description": "When present, and 'true', and when callback is configured with the value 'callback', enables the refresh token payload on the /callback endpoint.", + "required": false } } } diff --git a/src/main/java/org/folio/config/model/SamlConfiguration.java b/src/main/java/org/folio/config/model/SamlConfiguration.java index ff820786..613bcf0a 100644 --- a/src/main/java/org/folio/config/model/SamlConfiguration.java +++ b/src/main/java/org/folio/config/model/SamlConfiguration.java @@ -22,6 +22,7 @@ public class SamlConfiguration { public static final String METADATA_INVALIDATED_CODE = "metadata.invalidated"; public static final String OKAPI_URL= "okapi.url"; public static final String SAML_CALLBACK = "saml.callback"; + public static final String SAML_USE_SECURE_TOKENS = "saml.useSecureTokens"; @JsonProperty(IDP_URL_CODE) private String idpUrl; @@ -45,6 +46,8 @@ public class SamlConfiguration { private String okapiUrl; @JsonProperty(SAML_CALLBACK) private String callback; + @JsonProperty(SAML_USE_SECURE_TOKENS) + private String useSecureTokens; public String getIdpUrl() { @@ -131,4 +134,12 @@ public void setIdpMetadata(String idpMetadata) { public void setCallback(String callback) { this.callback = callback; } + + public String getUseSecureTokens() { + return useSecureTokens; + } + + public void setUseSecureTokens(String useSecureTokens) { + this.useSecureTokens = useSecureTokens; + } } diff --git a/src/main/java/org/folio/rest/impl/SamlAPI.java b/src/main/java/org/folio/rest/impl/SamlAPI.java index 3b8087f3..9135dff9 100644 --- a/src/main/java/org/folio/rest/impl/SamlAPI.java +++ b/src/main/java/org/folio/rest/impl/SamlAPI.java @@ -199,17 +199,17 @@ private String getRelayState(RoutingContext routingContext, String body) { @Override public void postSamlCallback(String body, RoutingContext routingContext, Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { - doPostSamlCallback(body, routingContext, okapiHeaders, asyncResultHandler, vertxContext, TOKEN_SIGN_ENDPOINT_LEGACY); + doPostSamlCallback(body, routingContext, okapiHeaders, asyncResultHandler, vertxContext); } @Override public void postSamlCallbackWithExpiry(String body, RoutingContext routingContext, Map okapiHeaders, Handler> asyncResultHandler, Context vertxContext) { - doPostSamlCallback(body, routingContext, okapiHeaders, asyncResultHandler, vertxContext, TOKEN_SIGN_ENDPOINT); + doPostSamlCallback(body, routingContext, okapiHeaders, asyncResultHandler, vertxContext); } private void doPostSamlCallback(String body, RoutingContext routingContext, Map okapiHeaders, - Handler> asyncResultHandler, Context vertxContext, String tokenSignEndpoint) { + Handler> asyncResultHandler, Context vertxContext) { registerFakeSession(routingContext); @@ -251,8 +251,9 @@ private void doPostSamlCallback(String body, RoutingContext routingContext, Map< JsonObject payload = new JsonObject().put("payload", new JsonObject().put("sub", userObject.getString(USERNAME)).put("user_id", userId)); + var tokenSignEndpoint = getTokenSignEndpoint(configuration); return fetchToken(webClient, payload, parsedHeaders, tokenSignEndpoint).map(jsonResponse -> { - if (isLegacyResponse(tokenSignEndpoint)) { + if (isLegacyResponse(configuration)) { return redirectResponseLegacy(jsonResponse, stripesBaseUrl, originalUrl); } else { return redirectResponse(jsonResponse, stripesBaseUrl, originalUrl); @@ -281,8 +282,15 @@ private PostSamlCallbackResponse failCallbackResponse(Throwable cause, RoutingCo return response; } - private boolean isLegacyResponse(String endpoint) { - return endpoint.equals(TOKEN_SIGN_ENDPOINT_LEGACY); + private boolean isLegacyResponse(SamlConfiguration configuration) { + return "callback".equals(configuration.getCallback()) && ! "true".equals(configuration.getUseSecureTokens()); + } + + private String getTokenSignEndpoint(SamlConfiguration configuration) { + if (isLegacyResponse(configuration)) { + return TOKEN_SIGN_ENDPOINT_LEGACY; + } + return TOKEN_SIGN_ENDPOINT; } private Future fetchToken(WebClient client, JsonObject payload, OkapiHeaders parsedHeaders, String endpoint) { @@ -469,6 +477,9 @@ public void putSamlConfiguration(SamlConfigRequest updatedConfig, RoutingContext ConfigEntryUtil.valueChanged(config.getCallback(), updatedConfig.getCallback(), callback -> updateEntries.put(SamlConfiguration.SAML_CALLBACK, callback)); + ConfigEntryUtil.valueChanged(config.getUseSecureTokens(), updatedConfig.getUseSecureTokens(), useSecureTokens -> + updateEntries.put(SamlConfiguration.SAML_USE_SECURE_TOKENS, useSecureTokens)); + return storeConfigEntries(rc, parsedHeaders, updateEntries, vertxContext); }) .onFailure(cause -> { @@ -481,6 +492,8 @@ public void putSamlConfiguration(SamlConfigRequest updatedConfig, RoutingContext }); } + + private Future storeConfigEntries(RoutingContext rc, OkapiHeaders parsedHeaders, Map updateEntries, Context vertxContext) { @@ -608,6 +621,7 @@ private SamlConfig configToDto(SamlConfiguration config) { .withSamlAttribute(config.getSamlAttribute()) .withUserProperty(config.getUserProperty()) .withCallback(config.getCallback()) + .withUseSecureTokens(Boolean.valueOf(config.getUseSecureTokens())) .withMetadataInvalidated(Boolean.valueOf(config.getMetadataInvalidated())); try { URI uri = URI.create(config.getOkapiUrl()); diff --git a/src/main/java/org/folio/util/ConfigEntryUtil.java b/src/main/java/org/folio/util/ConfigEntryUtil.java index 1144b80c..b49edcbd 100644 --- a/src/main/java/org/folio/util/ConfigEntryUtil.java +++ b/src/main/java/org/folio/util/ConfigEntryUtil.java @@ -33,7 +33,14 @@ public static void valueChanged(String oldValue, String newValue, Consumer onChanged) { + String newValueString = (newValue == null) ? null : newValue.toString(); + + valueChanged(oldValue, newValueString, onChanged); + } } diff --git a/src/test/java/org/folio/rest/impl/IdpCallbackTest.java b/src/test/java/org/folio/rest/impl/IdpCallbackTest.java new file mode 100644 index 00000000..7aa6bea8 --- /dev/null +++ b/src/test/java/org/folio/rest/impl/IdpCallbackTest.java @@ -0,0 +1,137 @@ +package org.folio.rest.impl; + +import io.restassured.RestAssured; +import io.vertx.core.DeploymentOptions; +import io.vertx.core.Vertx; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.unit.TestContext; +import io.vertx.ext.unit.junit.VertxUnitRunner; +import org.folio.config.SamlConfigHolder; +import org.folio.rest.RestVerticle; +import org.folio.util.MockJson; +import org.folio.util.SamlTestHelper; +import org.junit.*; +import org.junit.runner.RunWith; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * Test against a real IDP: https://simplesamlphp.org/ running in a Docker container. + */ +@RunWith(VertxUnitRunner.class) +public class IdpCallbackTest { + private static final org.slf4j.Logger logger = LoggerFactory.getLogger(IdpTest.class); + private static final boolean DEBUG = false; + private static final ImageFromDockerfile simplesamlphp = + new ImageFromDockerfile().withFileFromPath(".", Path.of("src/test/resources/simplesamlphp/")); + + private static final String TENANT = "diku"; + + private static final int MODULE_PORT = 9231; + private static final int OKAPI_PORT = 9230; + private static final String OKAPI_URL = "http://localhost:" + OKAPI_PORT; + private static final String CALLBACK = "callback"; + + private static int IDP_PORT; + private static String IDP_BASE_URL; + private static MockJson OKAPI; + + private static Vertx VERTX; + + @ClassRule + public static final GenericContainer IDP = new GenericContainer<>(simplesamlphp) + .withExposedPorts(8080) + .withEnv("SIMPLESAMLPHP_SP_ENTITY_ID", OKAPI_URL + "/_/invoke/tenant/diku/saml/callback") + .withEnv("SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE", + OKAPI_URL + "/_/invoke/tenant/diku/saml/callback"); + + @BeforeClass + public static void setupOnce(TestContext context) { + RestAssured.port = MODULE_PORT; + RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); + VERTX = Vertx.vertx(); + + if (DEBUG) { + IDP.followOutput(new Slf4jLogConsumer(logger).withSeparateOutputStreams()); + } + IDP_PORT = IDP.getFirstMappedPort(); + IDP_BASE_URL = "http://" + IDP.getHost() + ":" + IDP_PORT + "/simplesaml/"; + String baseurlpath = IDP_BASE_URL.replace("/", "\\/"); + exec("sed", "-i", "s/'baseurlpath' =>.*/'baseurlpath' => '" + baseurlpath + "',/", + "/var/www/simplesamlphp/config/config.php"); + exec("sed", "-i", "s/'auth' =>.*/'auth' => 'example-static',/", + "/var/www/simplesamlphp/metadata/saml20-idp-hosted.php"); + + DeploymentOptions moduleOptions = new DeploymentOptions() + .setConfig(new JsonObject().put("http.port", MODULE_PORT) + .put("mock", true)); // to use SAML2ClientMock + + OKAPI = new MockJson(); + DeploymentOptions okapiOptions = new DeploymentOptions() + .setConfig(new JsonObject().put("http.port", OKAPI_PORT)); + + VERTX.deployVerticle(new RestVerticle(), moduleOptions) + .compose(x -> VERTX.deployVerticle(OKAPI, okapiOptions)) + .onComplete(context.asyncAssertSuccess()); + } + + @AfterClass + public static void tearDownOnce(TestContext context) { + VERTX.close() + .onComplete(context.asyncAssertSuccess()); + } + + @After + public void after() { + SamlConfigHolder.getInstance().removeClient(TENANT); + } + + @Test + public void postCallback() { + setIdpBinding("POST"); + setOkapi("mock_idptest_post_secure_tokens.json"); + + for (int i = 0; i < 2; i++) { + SamlTestHelper.testPost(CALLBACK); + } + } + + @Test + public void redirectCallback() { + setIdpBinding("Redirect"); + setOkapi("mock_idptest_redirect_secure_tokens.json"); + + for (int i = 0; i < 2; i++) { + SamlTestHelper.testRedirect(CALLBACK); + } + } + + private void setIdpBinding(String binding) { + // append entry at end, last entry wins + exec("sed", "-i", + "s/];/'SingleSignOnServiceBinding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-" + binding + "',\\n];/", + "/var/www/simplesamlphp/metadata/saml20-idp-hosted.php"); + } + + private static void exec(String... command) { + try { + var result = IDP.execInContainer(command); + if (result.getExitCode() > 0) { + System.out.println(result.getStdout()); + System.err.println(result.getStderr()); + throw new RuntimeException("failure in IDP.execInContainer"); + } + } catch (UnsupportedOperationException | IOException | InterruptedException e) { + throw new RuntimeException(e); + } + } + + private void setOkapi(String resource) { + OKAPI.setMockContent(resource, s -> s.replace("http://localhost:8888/simplesaml/", IDP_BASE_URL)); + } +} diff --git a/src/test/java/org/folio/rest/impl/IdpLegacyTest.java b/src/test/java/org/folio/rest/impl/IdpLegacyTest.java index 4b99ce5d..43a411a6 100644 --- a/src/test/java/org/folio/rest/impl/IdpLegacyTest.java +++ b/src/test/java/org/folio/rest/impl/IdpLegacyTest.java @@ -71,7 +71,7 @@ public class IdpLegacyTest { OKAPI_URL + "/_/invoke/tenant/diku/saml/callback"); @BeforeClass - public static void setupOnce(TestContext context) throws Exception { + public static void setupOnce(TestContext context) { RestAssured.port = MODULE_PORT; RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); VERTX = Vertx.vertx(); @@ -248,7 +248,7 @@ private static void exec(String... command) { if (result.getExitCode() > 0) { System.out.println(result.getStdout()); System.err.println(result.getStderr()); - throw new RuntimeException("failure in IDP.execInContainer"); + throw new RuntimeException("failure in IDP.execInContainer"); } } catch (UnsupportedOperationException | IOException | InterruptedException e) { throw new RuntimeException(e); diff --git a/src/test/java/org/folio/rest/impl/IdpTest.java b/src/test/java/org/folio/rest/impl/IdpTest.java index afd3090d..5ff6c7ec 100644 --- a/src/test/java/org/folio/rest/impl/IdpTest.java +++ b/src/test/java/org/folio/rest/impl/IdpTest.java @@ -1,13 +1,8 @@ package org.folio.rest.impl; import io.restassured.RestAssured; -import io.restassured.http.Cookie; -import io.restassured.http.Header; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; -import io.vertx.core.http.CookieSameSite; import io.vertx.core.json.JsonObject; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; @@ -15,7 +10,6 @@ import org.folio.rest.RestVerticle; import org.folio.util.MockJson; import org.folio.util.SamlTestHelper; -import org.folio.util.StringUtil; import org.junit.*; import org.junit.runner.RunWith; import org.slf4j.LoggerFactory; @@ -24,14 +18,7 @@ import org.testcontainers.images.builder.ImageFromDockerfile; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; import java.nio.file.Path; -import java.util.regex.Pattern; - -import static io.restassured.RestAssured.given; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; /** * Test against a real IDP: https://simplesamlphp.org/ running in a Docker container. @@ -41,24 +28,17 @@ public class IdpTest { private static final org.slf4j.Logger logger = LoggerFactory.getLogger(IdpTest.class); private static final boolean DEBUG = false; private static final ImageFromDockerfile simplesamlphp = - new ImageFromDockerfile().withFileFromPath(".", Path.of("src/test/resources/simplesamlphp/")); + new ImageFromDockerfile().withFileFromPath(".", Path.of("src/test/resources/simplesamlphp/")); private static final String TENANT = "diku"; - private static final Header TENANT_HEADER = new Header("X-Okapi-Tenant", TENANT); - private static final Header TOKEN_HEADER = new Header("X-Okapi-Token", "mytoken"); - private static final Header JSON_CONTENT_TYPE_HEADER = new Header("Content-Type", "application/json"); - private static final String STRIPES_URL = "http://localhost:3000"; private static final int MODULE_PORT = 9231; - private static final String MODULE_URL = "http://localhost:" + MODULE_PORT; private static final int OKAPI_PORT = 9230; private static final String OKAPI_URL = "http://localhost:" + OKAPI_PORT; - - private static final String TEST_PATH = "/test/path"; + private static final String CALLBACK_WITH_EXPIRY = "callback-with-expiry"; private static int IDP_PORT; private static String IDP_BASE_URL; - private static final Header OKAPI_URL_HEADER = new Header("X-Okapi-Url", OKAPI_URL); private static MockJson OKAPI; private static Vertx VERTX; @@ -71,7 +51,7 @@ public class IdpTest { OKAPI_URL + "/_/invoke/tenant/diku/saml/callback-with-expiry"); @BeforeClass - public static void setupOnce(TestContext context) throws Exception { + public static void setupOnce(TestContext context) { RestAssured.port = MODULE_PORT; RestAssured.enableLoggingOfRequestAndResponseIfValidationFails(); VERTX = Vertx.vertx(); @@ -88,16 +68,16 @@ public static void setupOnce(TestContext context) throws Exception { "/var/www/simplesamlphp/metadata/saml20-idp-hosted.php"); DeploymentOptions moduleOptions = new DeploymentOptions() - .setConfig(new JsonObject().put("http.port", MODULE_PORT) - .put("mock", true)); // to use SAML2ClientMock + .setConfig(new JsonObject().put("http.port", MODULE_PORT) + .put("mock", true)); // to use SAML2ClientMock OKAPI = new MockJson(); DeploymentOptions okapiOptions = new DeploymentOptions() - .setConfig(new JsonObject().put("http.port", OKAPI_PORT)); + .setConfig(new JsonObject().put("http.port", OKAPI_PORT)); VERTX.deployVerticle(new RestVerticle(), moduleOptions) - .compose(x -> VERTX.deployVerticle(OKAPI, okapiOptions)) - .onComplete(context.asyncAssertSuccess()); + .compose(x -> VERTX.deployVerticle(OKAPI, okapiOptions)) + .onComplete(context.asyncAssertSuccess()); } @AfterClass @@ -117,100 +97,18 @@ public void post() { setOkapi("mock_idptest_post.json"); for (int i = 0; i < 2; i++) { - post0(); + SamlTestHelper.testPost(CALLBACK_WITH_EXPIRY); } } - private void post0() { - ExtractableResponse resp = given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .header(JSON_CONTENT_TYPE_HEADER) - .body(jsonEncode("stripesUrl", STRIPES_URL + TEST_PATH)) - .post("/saml/login") - .then() - .statusCode(200) - .body("bindingMethod", is("POST")) - .extract(); - - String location = resp.body().jsonPath().getString("location"); - String samlRequest = resp.body().jsonPath().getString("samlRequest"); - String relayState = resp.body().jsonPath().getString(SamlAPI.RELAY_STATE); - Cookie cookie = resp.detailedCookie(SamlAPI.RELAY_STATE); - assertThat(cookie.getValue(), is(relayState)); - - String body = given() - .formParams("RelayState", relayState) - .formParams("SAMLRequest", samlRequest) - .post(location) - .then() - .statusCode(200) - .body(containsString("
")) - .extract().asString(); - - var matcher = Pattern.compile("name=\"SAMLResponse\" value=\"([^\"]+)").matcher(body); - assertThat(matcher.find(), is(true)); - - SamlTestHelper.testCookieResponse(cookie, relayState, TEST_PATH, CookieSameSite.LAX.toString(), - matcher.group(1), TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER); - } - @Test public void redirect() { setIdpBinding("Redirect"); setOkapi("mock_idptest_redirect.json"); for (int i = 0; i < 2; i++) { - redirect0(); - } - } - - private void redirect0() { - ExtractableResponse resp = given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .header(JSON_CONTENT_TYPE_HEADER) - .body(jsonEncode("stripesUrl", STRIPES_URL + TEST_PATH)) - .when() - .post("/saml/login") - .then() - .statusCode(200) - .body("bindingMethod", is("GET")) - .body("location", containsString("/simplesaml/saml2/idp/SSOService.php?")) - .extract(); - - Cookie cookie = resp.detailedCookie(SamlAPI.RELAY_STATE); - String location = resp.body().jsonPath().getString("location"); - URL url; - try { - url = new URL(location); - } catch (MalformedURLException e) { - throw new RuntimeException(e); + SamlTestHelper.testRedirect(CALLBACK_WITH_EXPIRY); } - String [] parameters = StringUtil.urlDecode(url.getQuery()).split("&", 2); - String [] samlRequest = parameters[0].split("=", 2); - String [] relayState = parameters[1].split("=", 2); - location = location.substring(0, location.indexOf("?")); - - String body = given() - .param(samlRequest[0], samlRequest[1]) - .param(relayState[0], relayState[1]) - .when() - .get(location) - .then() - .statusCode(200) - .body(containsString(" method=\"post\" "), - containsString("action=\"" + OKAPI_URL + "/_/invoke/tenant/diku/saml/callback-with-expiry\">")) - .extract().asString(); - - var matcher = Pattern.compile("name=\"SAMLResponse\" value=\"([^\"]+)").matcher(body); - assertThat(matcher.find(), is(true)); - - SamlTestHelper.testCookieResponse(cookie, relayState[1], TEST_PATH, CookieSameSite.LAX.toString(), - matcher.group(1), TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER); } private void setIdpBinding(String binding) { @@ -226,7 +124,7 @@ private static void exec(String... command) { if (result.getExitCode() > 0) { System.out.println(result.getStdout()); System.err.println(result.getStderr()); - throw new RuntimeException("failure in IDP.execInContainer"); + throw new RuntimeException("failure in IDP.execInContainer"); } } catch (UnsupportedOperationException | IOException | InterruptedException e) { throw new RuntimeException(e); @@ -236,8 +134,4 @@ private static void exec(String... command) { private void setOkapi(String resource) { OKAPI.setMockContent(resource, s -> s.replace("http://localhost:8888/simplesaml/", IDP_BASE_URL)); } - - private String jsonEncode(String key, String value) { - return new JsonObject().put(key, value).encode(); - } } diff --git a/src/test/java/org/folio/rest/impl/SamlAPITest.java b/src/test/java/org/folio/rest/impl/SamlAPITest.java index 567ec40b..dff39903 100644 --- a/src/test/java/org/folio/rest/impl/SamlAPITest.java +++ b/src/test/java/org/folio/rest/impl/SamlAPITest.java @@ -82,11 +82,13 @@ public class SamlAPITest { private static final Header ACCESS_CONTROL_REQUEST_METHOD_HEADER = new Header( HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD.toString(), "POST"); private static final String STRIPES_URL = "http://localhost:3000"; - public static final int PORT = 8081; public static final int IDP_MOCK_PORT = NetworkUtils.nextFreePort(); + public static final String CALLBACK_URL = "/saml/callback"; + public static final String CALLBACK_WITH_EXPIRY_URL = "/saml/callback-with-expiry"; private static final int JSON_MOCK_PORT = NetworkUtils.nextFreePort(); private static final Header OKAPI_URL_HEADER = new Header("X-Okapi-Url", "http://localhost:" + JSON_MOCK_PORT); + public MockJson mock; private static Vertx mockVertx = Vertx.vertx(); @@ -594,7 +596,11 @@ public void callbackEndpointTests_Legacy() { .header("x-okapi-token", "saml-token") .cookie("ssoToken", "saml-token"); - log.info("=== Test - POST /saml/callback - failure (wrong cookie) ==="); + testCallbackErrorCases(CALLBACK_URL, relayState, cookie); + } + + private void testCallbackErrorCases(String callbackUrl, String relayState, String cookie) { + log.info("=== Test - POST /saml/{} - failure (wrong cookie) ===", callbackUrl); given() .header(TENANT_HEADER) .header(TOKEN_HEADER) @@ -602,12 +608,12 @@ public void callbackEndpointTests_Legacy() { .cookie(SamlAPI.RELAY_STATE, "bad" + cookie) .formParam("SAMLResponse", "saml-response") .formParam("RelayState", relayState) - .post("/saml/callback") + .post(callbackUrl) .then() .statusCode(403) .body(is("CSRF attempt detected")); - log.info("=== Test - POST /saml/callback - failure (wrong relay) ==="); + log.info("=== Test - POST /saml/{} - failure (wrong relay) ===", callbackUrl); given() .header(TENANT_HEADER) .header(TOKEN_HEADER) @@ -615,19 +621,19 @@ public void callbackEndpointTests_Legacy() { .cookie(SamlAPI.RELAY_STATE, cookie) .formParam("SAMLResponse", "saml-response") .formParam("RelayState", relayState.replace("localhost", "^")) - .post("/saml/callback") + .post(callbackUrl) .then() .statusCode(400) .body(containsString("Invalid relay state url")); - log.info("=== Test - POST /saml/callback - failure (no cookie) ==="); + log.info("=== Test - POST /saml/{} - failure (no cookie) ===", callbackUrl); given() .header(TENANT_HEADER) .header(TOKEN_HEADER) .header(OKAPI_URL_HEADER) .formParam("SAMLResponse", "saml-response") .formParam("RelayState", relayState) - .post("/saml/callback") + .post(callbackUrl) .then() .statusCode(403) .body(is("CSRF attempt detected")); @@ -641,7 +647,7 @@ public void callbackEndpointTests_Legacy() { .cookie(SamlAPI.RELAY_STATE, cookie) .formParam("SAMLResponse", "saml-response") .formParam("RelayState", relayState) - .post("/saml/callback") + .post( callbackUrl) .then() .statusCode(500) .body(is("Response status code 404 is not equal to 200")); @@ -654,7 +660,7 @@ public void callbackEndpointTests_Legacy() { .cookie(SamlAPI.RELAY_STATE, cookie) .formParam("SAMLResponse", "saml-response") .formParam("RelayState", relayState) - .post("/saml/callback") + .post(callbackUrl) .then() .statusCode(400) .body(is("No user found by externalSystemId == saml-user-id")); @@ -667,25 +673,10 @@ public void callbackEndpointTests_Legacy() { .cookie(SamlAPI.RELAY_STATE, cookie) .formParam("SAMLResponse", "saml-response") .formParam("RelayState", relayState) - .post("/saml/callback") + .post(callbackUrl) .then() .statusCode(403) .body(is("Inactive user account!")); - - mock.setMockContent("mock_tokenresponse.json"); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .cookie(SamlAPI.RELAY_STATE, cookie) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState) - .post("/saml/callback") - .then() - .statusCode(302) - .header("Location", containsString(PercentCodec.encodeAsString(testPath))) - .header("x-okapi-token", "saml-token") - .cookie("ssoToken", "saml-token"); } @Test @@ -734,9 +725,21 @@ public void callbackForConsortium() { @Test public void callbackEndpointTests() { + // Default. No configuration needed. /saml/callback-with-expiry returns RTR tokens. + testCallback(CALLBACK_WITH_EXPIRY_URL); + } + + @Test + public void callbackEndpointTestsUseSecureTokens() { + // Configuration needed. /saml/callback returns RTR tokens allowing existing metadata to be used. + mock.setMockContent("mock_content_secure_tokens.json"); + testCallback(CALLBACK_URL); + } + + private void testCallback(String callbackUrl) { final String testPath = "/test/path"; - log.info("=== Setup - POST /saml/login - need relayState and cookie ==="); + log.info("=== Setup - POST /saml/login RTR - need relayState and cookie ==="); ExtractableResponse resp = given() .header(TENANT_HEADER) .header(TOKEN_HEADER) @@ -756,105 +759,16 @@ public void callbackEndpointTests() { String relayState = resp.body().jsonPath().getString(SamlAPI.RELAY_STATE); String samlResponse = "saml-response"; - log.info("=== Test - POST /saml/callback-with-expiry - success ==="); + log.info("=== Test - POST /saml/{} RTR - success ===", callbackUrl); SamlTestHelper.testCookieResponse(detailedCookie, relayState, testPath, CookieSameSite.LAX.toString(), - samlResponse, TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER); + samlResponse, TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER, callbackUrl); CookieSameSiteConfig.set(Map.of("LOGIN_COOKIE_SAMESITE", CookieSameSite.NONE.toString())); SamlTestHelper.testCookieResponse(detailedCookie, relayState, testPath, CookieSameSite.NONE.toString(), - samlResponse, TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER); + samlResponse, TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER, callbackUrl); CookieSameSiteConfig.set(Map.of()); - log.info("=== Test - POST /saml/callback-with-expiry - failure (wrong cookie) ==="); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .cookie(SamlAPI.RELAY_STATE, "bad" + cookie) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState) - .post("/saml/callback-with-expiry") - .then() - .statusCode(403) - .body(is("CSRF attempt detected")); - - log.info("=== Test - POST /saml/callback/callback-with-expiry - failure (wrong relay) ==="); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .cookie(SamlAPI.RELAY_STATE, cookie) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState.replace("localhost", "^")) - .post("/saml/callback-with-expiry") - .then() - .statusCode(400) - .body(containsString("Invalid relay state url")); - - log.info("=== Test - POST /saml/callback-with-expiry - failure (no cookie) ==="); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState) - .post("/saml/callback-with-expiry") - .then() - .statusCode(403) - .body(is("CSRF attempt detected")); - - // not found .. - mock.setMockContent("mock_400.json"); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .cookie(SamlAPI.RELAY_STATE, cookie) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState) - .post("/saml/callback-with-expiry") - .then() - .statusCode(500) - .body(is("Response status code 404 is not equal to 200")); - - mock.setMockContent("mock_nouser.json"); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .cookie(SamlAPI.RELAY_STATE, cookie) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState) - .post("/saml/callback-with-expiry") - .then() - .statusCode(400) - .body(is("No user found by externalSystemId == saml-user-id")); - - mock.setMockContent("mock_inactiveuser.json"); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .cookie(SamlAPI.RELAY_STATE, cookie) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState) - .post("/saml/callback-with-expiry") - .then() - .statusCode(403) - .body(is("Inactive user account!")); - - mock.setMockContent("mock_tokenresponse.json"); - given() - .header(TENANT_HEADER) - .header(TOKEN_HEADER) - .header(OKAPI_URL_HEADER) - .cookie(SamlAPI.RELAY_STATE, cookie) - .formParam("SAMLResponse", "saml-response") - .formParam("RelayState", relayState) - .post("/saml/callback-with-expiry") - .then() - .statusCode(302) - .header("Location", containsString(PercentCodec.encodeAsString(testPath))); + testCallbackErrorCases(callbackUrl, relayState, cookie); } void postSamlLogin(int expectedStatus) { @@ -899,7 +813,7 @@ public void getConfigurationEndpoint() { } @Test - public void getConfigurationEndpointLegacy() { + public void getConfigurationEndpoint_Legacy() { mock.setMockContent("mock_content_legacy.json"); given() .header(TENANT_HEADER) @@ -916,6 +830,25 @@ public void getConfigurationEndpointLegacy() { .body("metadataInvalidated", equalTo(Boolean.FALSE)); } + @Test + public void getConfigurationEndpointUseSecureTokens() { + mock.setMockContent("mock_content_secure_tokens.json"); + given() + .header(TENANT_HEADER) + .header(TOKEN_HEADER) + .header(OKAPI_URL_HEADER) + .header(JSON_CONTENT_TYPE_HEADER) + .get("/saml/configuration") + .then() + .statusCode(200) + .body(matchesJsonSchemaInClasspath("ramls/schemas/SamlConfig.json")) + .body("idpUrl", equalTo("https://idp.ssocircle.com")) + .body("samlBinding", equalTo("POST")) + .body("callback", equalTo("callback")) + .body("useSecureTokens", equalTo(Boolean.TRUE)) + .body("metadataInvalidated", equalTo(Boolean.FALSE)); + } + @Test public void putConfigurationEndpoint(TestContext context) { SamlConfigRequest samlConfigRequest = new SamlConfigRequest() @@ -993,6 +926,36 @@ public void putConfiguration_Legacy() { .body(matchesJsonSchemaInClasspath("ramls/schemas/SamlConfig.json")); } + @Test + public void putConfigurationUseSecureTokens() { + mock.setMockContent("mock_content_secure_tokens.json"); + + SamlConfigRequest samlConfigRequest = new SamlConfigRequest() + .withIdpUrl(URI.create("http://localhost:" + IDP_MOCK_PORT + "/xml")) + .withSamlAttribute("UserID") + .withSamlBinding(SamlConfigRequest.SamlBinding.POST) + .withUserProperty("externalSystemId") + .withOkapiUrl(URI.create("http://localhost:9130")) + .withCallback("callback") + .withUseSecureTokens(true); + + String jsonString = Json.encode(samlConfigRequest); + + // PUT + given() + .header(TENANT_HEADER) + .header(TOKEN_HEADER) + .header(OKAPI_URL_HEADER) + .header(JSON_CONTENT_TYPE_HEADER) + .body(jsonString) + .put("/saml/configuration") + .then() + .statusCode(200) + .body("callback", equalTo("callback")) + .body("useSecureTokens", equalTo(true)) + .body(matchesJsonSchemaInClasspath("ramls/schemas/SamlConfig.json")); + } + @Test public void putConfiguration() { mock.setMockContent("mock_content.json"); diff --git a/src/test/java/org/folio/util/SamlTestHelper.java b/src/test/java/org/folio/util/SamlTestHelper.java index c46c394b..4cc3c10b 100644 --- a/src/test/java/org/folio/util/SamlTestHelper.java +++ b/src/test/java/org/folio/util/SamlTestHelper.java @@ -4,16 +4,121 @@ import io.restassured.http.Cookie; import io.restassured.http.Header; import io.restassured.matcher.RestAssuredMatchers; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import io.vertx.core.http.CookieSameSite; +import io.vertx.core.json.JsonObject; import org.folio.rest.impl.SamlAPI; -import static org.hamcrest.Matchers.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.regex.Pattern; + +import static io.restassured.RestAssured.given; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.both; public class SamlTestHelper { + public static final int OKAPI_PORT = 9230; + public static final String OKAPI_URL = "http://localhost:" + OKAPI_PORT; + public static final String TENANT = "diku"; + private static final Header TENANT_HEADER = new Header("X-Okapi-Tenant", TENANT); + private static final Header TOKEN_HEADER = new Header("X-Okapi-Token", "mytoken"); + private static final Header JSON_CONTENT_TYPE_HEADER = new Header("Content-Type", "application/json"); + private static final Header OKAPI_URL_HEADER = new Header("X-Okapi-Url", OKAPI_URL); + private static final String TEST_PATH = "/test/path"; + private static final String STRIPES_URL = "http://localhost:3000"; + + public static void testPost(String callback) { + ExtractableResponse resp = given() + .header(TENANT_HEADER) + .header(TOKEN_HEADER) + .header(OKAPI_URL_HEADER) + .header(JSON_CONTENT_TYPE_HEADER) + .body(jsonEncode("stripesUrl", STRIPES_URL + TEST_PATH)) + .post("/saml/login") + .then() + .statusCode(200) + .body("bindingMethod", is("POST")) + .extract(); + + String location = resp.body().jsonPath().getString("location"); + String samlRequest = resp.body().jsonPath().getString("samlRequest"); + String relayState = resp.body().jsonPath().getString(SamlAPI.RELAY_STATE); + Cookie cookie = resp.detailedCookie(SamlAPI.RELAY_STATE); + assertThat(cookie.getValue(), is(relayState)); + + String body = given() + .formParams("RelayState", relayState) + .formParams("SAMLRequest", samlRequest) + .post(location) + .then() + .statusCode(200) + .body(containsString("", OKAPI_URL, callback))) + .extract().asString(); + + var matcher = Pattern.compile("name=\"SAMLResponse\" value=\"([^\"]+)").matcher(body); + assertThat(matcher.find(), is(true)); + + testCookieResponse(cookie, relayState, TEST_PATH, CookieSameSite.LAX.toString(), + matcher.group(1), TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER, "/saml/" + callback); + } + + public static void testRedirect(String callback) { + ExtractableResponse resp = given() + .header(TENANT_HEADER) + .header(TOKEN_HEADER) + .header(OKAPI_URL_HEADER) + .header(JSON_CONTENT_TYPE_HEADER) + .body(jsonEncode("stripesUrl", STRIPES_URL + TEST_PATH)) + .when() + .post("/saml/login") + .then() + .statusCode(200) + .body("bindingMethod", is("GET")) + .body("location", containsString("/simplesaml/saml2/idp/SSOService.php?")) + .extract(); + + Cookie cookie = resp.detailedCookie(SamlAPI.RELAY_STATE); + String location = resp.body().jsonPath().getString("location"); + URL url; + try { + url = new URL(location); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + String [] parameters = StringUtil.urlDecode(url.getQuery()).split("&", 2); + String [] samlRequest = parameters[0].split("=", 2); + String [] relayState = parameters[1].split("=", 2); + location = location.substring(0, location.indexOf("?")); + + String body = + given() + .param(samlRequest[0], samlRequest[1]) + .param(relayState[0], relayState[1]) + .when() + .get(location) + .then() + .statusCode(200) + .body(containsString(" method=\"post\" "), + containsString(String.format("action=\"%s/_/invoke/tenant/diku/saml/%s\">", OKAPI_URL, callback))) + .extract().asString(); + + var matcher = Pattern.compile("name=\"SAMLResponse\" value=\"([^\"]+)").matcher(body); + assertThat(matcher.find(), is(true)); + + testCookieResponse(cookie, relayState[1], TEST_PATH, CookieSameSite.LAX.toString(), + matcher.group(1), TENANT_HEADER, TOKEN_HEADER, OKAPI_URL_HEADER, + "/saml/" + callback); + } public static void testCookieResponse(Cookie cookie, String relayState, String testPath, String sameSite, String samlResponse, Header tenantHeader, Header tokenHeader, - Header okapiUrlHeader) { + Header okapiUrlHeader, String callbackUrl) { RestAssured.given() .header(tenantHeader) .header(tokenHeader) @@ -21,7 +126,7 @@ public static void testCookieResponse(Cookie cookie, String relayState, String t .cookie(cookie) .formParam("SAMLResponse", samlResponse) .formParam("RelayState", relayState) - .post("/saml/callback-with-expiry") + .post(callbackUrl) .then() .statusCode(302) .cookie("folioRefreshToken", RestAssuredMatchers.detailedCookie() @@ -45,4 +150,8 @@ public static void testCookieResponse(Cookie cookie, String relayState, String t .header("Location", both(containsString(PercentCodec.encodeAsString("2050-10-05T20:19:33Z"))) .and(containsString(PercentCodec.encodeAsString("2050-10-05T20:19:33Z")))); } + + private static String jsonEncode(String key, String value) { + return new JsonObject().put(key, value).encode(); + } } diff --git a/src/test/resources/mock_content.json b/src/test/resources/mock_content.json index 11163429..d8f65810 100644 --- a/src/test/resources/mock_content.json +++ b/src/test/resources/mock_content.json @@ -56,7 +56,7 @@ "value": "http://localhost:9130" } ], - "totalRecords": 6 + "totalRecords": 7 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_content_legacy.json b/src/test/resources/mock_content_legacy.json index 4541fdf1..7ad10c3f 100644 --- a/src/test/resources/mock_content_legacy.json +++ b/src/test/resources/mock_content_legacy.json @@ -63,7 +63,7 @@ "value": "callback" } ], - "totalRecords": 6 + "totalRecords": 9 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_content_secure_tokens.json b/src/test/resources/mock_content_secure_tokens.json new file mode 100644 index 00000000..ce55ee46 --- /dev/null +++ b/src/test/resources/mock_content_secure_tokens.json @@ -0,0 +1,243 @@ +{ + "mocks": [ + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%29", + "method": "get", + "status": 200, + "receivedData": { + "configs": [ + { + "id": "60eead4f-de97-437c-9cb7-09966ce50e49", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "idp.url", + "value": "https://idp.ssocircle.com" + }, + { + "id": "022d8342-fa51-44d1-8b2b-27da36e11f07", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.file", + "value": "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" + }, + { + "id": "6dc15218-ed83-49e0-85ab-bb891e3f42c9", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.password", + "value": "iOzPffanq1xj" + }, + { + "id": "b5662280-81cc-462e-bb84-726e47cb58e4", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.privatekey.password", + "value": "iOzPffanq1xj" + }, + { + "id": "2dd0d26d-3be4-4e80-a631-f7bda5311719", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.binding", + "value": "POST" + }, + { + "id": "717bf1d1-a5a3-460f-a0de-29e6b70a0027", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "metadata.invalidated", + "value": "false" + }, + { + "id": "cb20fa86-affb-4488-8b37-2e8c597fff66", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "okapi.url", + "value": "http://localhost:9130" + }, + { + "id": "81816efc-63b1-11ee-8c99-0242ac120002", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.callback", + "value": "callback" + }, + { + "id": "dcc452cf-f726-423b-91ec-ec89e36f9e98", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.useSecureTokens", + "value": "true" + } + ], + "totalRecords": 9 + }, + "receivedPath": "", + "sendData": {} + }, + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%20AND%20code%3D%3D%20saml.attribute%29", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 0, + "configs": [] + } + }, + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%20AND%20code%3D%3D%20idp.metadata%29", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "configs": [ + { + "id": "60eead4f-de97-437c-9cb7-09966ce50e48", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "idp.metadata", + "value": "\n\n \n \n \n \n \nMIIEYzCCAkugAwIBAgIDIAZmMA0GCSqGSIb3DQEBCwUAMC4xCzAJBgNVBAYTAkRF\nMRIwEAYDVQQKDAlTU09DaXJjbGUxCzAJBgNVBAMMAkNBMB4XDTE2MDgwMzE1MDMy\nM1oXDTI2MDMwNDE1MDMyM1owPTELMAkGA1UEBhMCREUxEjAQBgNVBAoTCVNTT0Np\ncmNsZTEaMBgGA1UEAxMRaWRwLnNzb2NpcmNsZS5jb20wggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQCAwWJyOYhYmWZF2TJvm1VyZccs3ZJ0TsNcoazr2pTW\ncY8WTRbIV9d06zYjngvWibyiylewGXcYONB106ZNUdNgrmFd5194Wsyx6bPvnjZE\nERny9LOfuwQaqDYeKhI6c+veXApnOfsY26u9Lqb9sga9JnCkUGRaoVrAVM3yfghv\n/Cg/QEg+I6SVES75tKdcLDTt/FwmAYDEBV8l52bcMDNF+JWtAuetI9/dWCBe9VTC\nasAr2Fxw1ZYTAiqGI9sW4kWS2ApedbqsgH3qqMlPA7tg9iKy8Yw/deEn0qQIx8Gl\nVnQFpDgzG9k+jwBoebAYfGvMcO/BDXD2pbWTN+DvbURlAgMBAAGjezB5MAkGA1Ud\nEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmlj\nYXRlMB0GA1UdDgQWBBQhAmCewE7aonAvyJfjImCRZDtccTAfBgNVHSMEGDAWgBTA\n1nEA+0za6ppLItkOX5yEp8cQaTANBgkqhkiG9w0BAQsFAAOCAgEAAhC5/WsF9ztJ\nHgo+x9KV9bqVS0MmsgpG26yOAqFYwOSPmUuYmJmHgmKGjKrj1fdCINtzcBHFFBC1\nmaGJ33lMk2bM2THx22/O93f4RFnFab7t23jRFcF0amQUOsDvltfJw7XCal8JdgPU\ng6TNC4Fy9XYv0OAHc3oDp3vl1Yj8/1qBg6Rc39kehmD5v8SKYmpE7yFKxDF1ol9D\nKDG/LvClSvnuVP0b4BWdBAA9aJSFtdNGgEvpEUqGkJ1osLVqCMvSYsUtHmapaX3h\niM9RbX38jsSgsl44Rar5Ioc7KXOOZFGfEKyyUqucYpjWCOXJELAVAzp7XTvA2q55\nu31hO0w8Yx4uEQKlmxDuZmxpMz4EWARyjHSAuDKEW1RJvUr6+5uA9qeOKxLiKN1j\no6eWAcl6Wr9MreXR9kFpS6kHllfdVSrJES4ST0uh1Jp4EYgmiyMmFCbUpKXifpsN\nWCLDenE3hllF0+q3wIdu+4P82RIM71n7qVgnDnK29wnLhHDat9rkC62CIbonpkVY\nmnReX0jze+7twRanJOMCJ+lFg16BDvBcG8u0n/wIDkHHitBI7bU1k6c6DydLQ+69\nh8SCo6sO9YuD+/3xAGKad4ImZ6vTwlB4zDCpu6YgQWocWRXE+VkOb+RBfvP755PU\naLfL63AFVlpOnEpIio5++UjNJRuPuAA=\n \n \n \n \n \n \n \n \nMIIEYzCCAkugAwIBAgIDIAZmMA0GCSqGSIb3DQEBCwUAMC4xCzAJBgNVBAYTAkRF\nMRIwEAYDVQQKDAlTU09DaXJjbGUxCzAJBgNVBAMMAkNBMB4XDTE2MDgwMzE1MDMy\nM1oXDTI2MDMwNDE1MDMyM1owPTELMAkGA1UEBhMCREUxEjAQBgNVBAoTCVNTT0Np\ncmNsZTEaMBgGA1UEAxMRaWRwLnNzb2NpcmNsZS5jb20wggEiMA0GCSqGSIb3DQEB\nAQUAA4IBDwAwggEKAoIBAQCAwWJyOYhYmWZF2TJvm1VyZccs3ZJ0TsNcoazr2pTW\ncY8WTRbIV9d06zYjngvWibyiylewGXcYONB106ZNUdNgrmFd5194Wsyx6bPvnjZE\nERny9LOfuwQaqDYeKhI6c+veXApnOfsY26u9Lqb9sga9JnCkUGRaoVrAVM3yfghv\n/Cg/QEg+I6SVES75tKdcLDTt/FwmAYDEBV8l52bcMDNF+JWtAuetI9/dWCBe9VTC\nasAr2Fxw1ZYTAiqGI9sW4kWS2ApedbqsgH3qqMlPA7tg9iKy8Yw/deEn0qQIx8Gl\nVnQFpDgzG9k+jwBoebAYfGvMcO/BDXD2pbWTN+DvbURlAgMBAAGjezB5MAkGA1Ud\nEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmlj\nYXRlMB0GA1UdDgQWBBQhAmCewE7aonAvyJfjImCRZDtccTAfBgNVHSMEGDAWgBTA\n1nEA+0za6ppLItkOX5yEp8cQaTANBgkqhkiG9w0BAQsFAAOCAgEAAhC5/WsF9ztJ\nHgo+x9KV9bqVS0MmsgpG26yOAqFYwOSPmUuYmJmHgmKGjKrj1fdCINtzcBHFFBC1\nmaGJ33lMk2bM2THx22/O93f4RFnFab7t23jRFcF0amQUOsDvltfJw7XCal8JdgPU\ng6TNC4Fy9XYv0OAHc3oDp3vl1Yj8/1qBg6Rc39kehmD5v8SKYmpE7yFKxDF1ol9D\nKDG/LvClSvnuVP0b4BWdBAA9aJSFtdNGgEvpEUqGkJ1osLVqCMvSYsUtHmapaX3h\niM9RbX38jsSgsl44Rar5Ioc7KXOOZFGfEKyyUqucYpjWCOXJELAVAzp7XTvA2q55\nu31hO0w8Yx4uEQKlmxDuZmxpMz4EWARyjHSAuDKEW1RJvUr6+5uA9qeOKxLiKN1j\no6eWAcl6Wr9MreXR9kFpS6kHllfdVSrJES4ST0uh1Jp4EYgmiyMmFCbUpKXifpsN\nWCLDenE3hllF0+q3wIdu+4P82RIM71n7qVgnDnK29wnLhHDat9rkC62CIbonpkVY\nmnReX0jze+7twRanJOMCJ+lFg16BDvBcG8u0n/wIDkHHitBI7bU1k6c6DydLQ+69\nh8SCo6sO9YuD+/3xAGKad4ImZ6vTwlB4zDCpu6YgQWocWRXE+VkOb+RBfvP755PU\naLfL63AFVlpOnEpIio5++UjNJRuPuAA=\n \n \n \n \n 128\n\n \n \n \n \n \n \n \n \n urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\n urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\n urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\n urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos\n \n \n \n \n \n\n" + } + ] + } + }, + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%20AND%20code%3D%3D%20idp.url%29", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "configs": [ + { + "id": "60eead4f-de97-437c-9cb7-09966ce50e49", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "idp.url", + "value": "https://idp.ssocircle.com" + } + ] + } + }, + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%20AND%20code%3D%3D%20saml.callback%29", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "configs": [ + { + "id": "81816efc-63b1-11ee-8c99-0242ac120002", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.callback", + "value": "callback-b" + } + ] + } + }, + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%20AND%20code%3D%3D%20saml.useSecureTokens%29", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "configs": [ + { + "id": "dcc452cf-f726-423b-91ec-ec89e36f9e98", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.useSecureTokens", + "value": "false" + } + ] + } + }, + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%20AND%20code%3D%3D%20metadata.invalidated%29", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "configs": [ + { + "id": "717bf1d1-a5a3-460f-a0de-29e6b70a0027", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "metadata.invalidated", + "value": "false" + } + ] + } + }, + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%20AND%20code%3D%3D%20user.property%29", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 0, + "configs": [] + } + }, + { + "url": "/configurations/entries", + "method": "post", + "status": 201 + }, + { + "url": "/configurations/entries/60eead4f-de97-437c-9cb7-09966ce50e49", + "method": "put", + "status": 204 + }, + { + "url": "/configurations/entries/60eead4f-de97-437c-9cb7-09966ce50e48", + "method": "put", + "status": 204 + }, + { + "url": "/configurations/entries/717bf1d1-a5a3-460f-a0de-29e6b70a0027", + "method": "put", + "status": 204 + }, + { + "url": "/configurations/entries/81816efc-63b1-11ee-8c99-0242ac120002", + "method": "put", + "status": 204 + }, + { + "url": "/users?query=externalSystemId%3D%3D%22saml-user-id%22", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "users": [ + { + "id": "saml-user", + "username": "samluser", + "active": true + } + ] + } + }, + { + "url": "/token/sign", + "method": "post", + "status": 201, + "receivedData": { + "accessToken": "saml-access-token", + "refreshToken": "saml-refresh-token", + "accessTokenExpiration": "2050-10-05T20:19:33Z", + "refreshTokenExpiration": "2050-10-05T20:19:33Z", + "tenantId": "diku" + } + }, + { + "url": "/user-tenants?externalSystemId=saml-user-id", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 0, + "userTenants": [] + } + } + ] +} diff --git a/src/test/resources/mock_content_with_metadata.json b/src/test/resources/mock_content_with_metadata.json index 94ccf75d..76ecd01b 100644 --- a/src/test/resources/mock_content_with_metadata.json +++ b/src/test/resources/mock_content_with_metadata.json @@ -47,7 +47,8 @@ "configName": "saml", "code": "metadata.invalidated", "value": "false" - },{ + }, + { "id": "cb20fa86-affb-4488-8b37-2e8c597fff66", "module": "LOGIN-SAML", "configName": "saml", @@ -56,7 +57,7 @@ } ], - "totalRecords": 6 + "totalRecords": 7 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_content_with_metadata_legacy.json b/src/test/resources/mock_content_with_metadata_legacy.json index 51a0bdcf..deaa19b2 100644 --- a/src/test/resources/mock_content_with_metadata_legacy.json +++ b/src/test/resources/mock_content_with_metadata_legacy.json @@ -47,16 +47,23 @@ "configName": "saml", "code": "metadata.invalidated", "value": "false" - },{ + }, + { "id": "cb20fa86-affb-4488-8b37-2e8c597fff66", "module": "LOGIN-SAML", "configName": "saml", "code": "okapi.url", "value": "http://localhost:9130" + }, + { + "id": "81816efc-63b1-11ee-8c99-0242ac120002", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.callback", + "value": "callback" } - ], - "totalRecords": 6 + "totalRecords": 7 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_idptest_post.json b/src/test/resources/mock_idptest_post.json index 03c345b9..05d97286 100644 --- a/src/test/resources/mock_idptest_post.json +++ b/src/test/resources/mock_idptest_post.json @@ -47,7 +47,8 @@ "configName": "saml", "code": "metadata.invalidated", "value": "false" - },{ + }, + { "id": "cb20fa86-affb-4488-8b37-2e8c597fff66", "module": "LOGIN-SAML", "configName": "saml", @@ -55,7 +56,7 @@ "value": "http://localhost:9230" } ], - "totalRecords": 6 + "totalRecords": 7 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_idptest_post_legacy.json b/src/test/resources/mock_idptest_post_legacy.json index a4958781..3122c626 100644 --- a/src/test/resources/mock_idptest_post_legacy.json +++ b/src/test/resources/mock_idptest_post_legacy.json @@ -63,7 +63,7 @@ "value": "callback" } ], - "totalRecords": 6 + "totalRecords": 9 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_idptest_post_secure_tokens.json b/src/test/resources/mock_idptest_post_secure_tokens.json new file mode 100644 index 00000000..c3f3c119 --- /dev/null +++ b/src/test/resources/mock_idptest_post_secure_tokens.json @@ -0,0 +1,117 @@ +{ + "mocks": [ + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%29", + "method": "get", + "status": 200, + "receivedData": { + "configs": [ + { + "id": "60eead4f-de97-437c-9cb7-09966ce50e49", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "idp.url", + "value": "http://localhost:8888/simplesaml/saml2/idp/metadata.php" + }, + { + "id": "022d8342-fa51-44d1-8b2b-27da36e11f07", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.file", + "value": "/u3+7QAAAAIAAAABAAAAAQAYc2FtbDJjbGllbnRjb25maWd1cmF0aW9uAAABXgmqUasAAAUCMIIE/jAOBgorBgEEASoCEQEBBQAEggTqAM9d1z/8cEsfS4brWTi+sTcK6/YoGpduJKVnDnFIYlNthuZ6curH714Mj3a79ZFziqbv6EGU/uOXXP7cD8S2oSEzhnkbubIokyJPNo14v9hsvapnP8oz04HDAEFO0+v1RLeoe/08VKdUqEtrdg7W6r3HIjNHfbSHZEpTCfWnyovL+NPVI8lYz38EGksp/SPWJFdOcAmMgN0a2zDI9R9WgQJ2GUYwyy3XzlKgbIj3Xe+lpo1B3nq4+WQ7UUqWBnfdW5t2a6Ld6R2dU7kly9qR906UYP+2wNsk66/ME97hCriyn3JWdhs+fjpts5ReV/Q5X7O5SMjmfYQOfgRzsjD4+Eh9Bqptg9dUvZ+7nJbqu2k+2RFEfPZdBkgQ0QsnpkkCiu9whKqKj5uInhBlNGaAokP39mKOdvYZ1l1VVoIdVY1XLkPJ0BRDrIm4MLW6B4/+cIsL9MS0/bFsgSd8UURfP9fGONzmBbwUapRu2/zBCgKM2PbV82n+TKZT1x/RXN05ARb63xIC2HFTq3LImRWf6V081M9YhMaIQLhA6IPbFnzNNwRO0i1WzHF0FJXVQjLXx1//i8k/nxeUEIIh3TtHlEpTtlmt+GDFFVgXCOt+S78VZyh7FgRmlx4V/5w7KlvzljbqS0hq2DkAGPWPCTu0PyeZcssSTqUTNYmgbwbsHhYzubOFh0pgEuN8hK3dsQf1YGBWfehspsbicwmuvSAMyBcdXd497NRmzIJU4GjCvBj+vmnpCkgkVg2SXK6aFo2SbYXUrRjDzqql3ElQ7/jlIWqnoU7J653TqN6O5uo8ZQGi2BWhldCyC/JKa0LastcRrFNjEeGk19+FRVYprKcDtFGwn4TWQtwqf9BDbXDKBtTQyIoNVFoGUd9dxPryOmUet3Ipxgqy6yoGQwlRvJ4EEyTETQRLx/foGT7JMAAOdNnSWsmyFvPkXLfzHZTSVrtOBHXj83svt5929NgVMx/HJa2aaCdkCZGs/VpRHDzJX35yyr5pzCGau0pVXRZetqKW1Yp+rfWnhwtvgnDZ+kOYLfoFSFPYtKqfMtHZih8byIW/vRLEanNY54l1tG3l0STF+VkH4QudLtZTm8mhbQJDWGOOT76VF+Yf1u1J00spDyl2HLgHHZRV3z7UfLW9kQLnkpFPmiduM8JIBx9z8cfSSuSru9TtrNmmXbXWTgFv5nzsRqZ3czWQyURujOqQJXwgKJTDNNfOnVgY7WZ+GIWAas/JBnS+V3HFCvC63rQxZohj6d+zQF5FDVbc2rceZ3ihGurTnbbl4Ebflgw9XACPipa4CqqbUEuhuNwzgr2/h/l236PBcMW4Y1PPAEZ77x45KYjFmod83mSt9Ibxz/QgZiUkd1ZkaspJCnd/bcSkAtiptso3hyW2jt1W4ftLFdzshU1t872aW7yr+FqgDzynE1Wh9DVTd02Fu5fH2g7qDLwlp5aXRXQclVi3Y7iYSiL4KZ7fJ7bEV4ZD4XyzO9CI8owZc5/HxYcW3BmiZhONMppy7gAc+LHJgPT4GgbqD5BEll1qoxMkqC0WQCbb4pX65ZcnBfzsd5tMhvqEK3Sea3WZk4dGCeegVMzs4ziT7ybGhixM+4f1kpwaTnL0keOWHa6IQm/rLetkZATx4U+gPqgqVw9JVhdgwsn+QAdiC9YPSgWsokmhIIODhQAAAAEABVguNTA5AAACpTCCAqEwggGJoAMCAQICAQEwDQYJKoZIhvcNAQEFBQAwFDESMBAGA1UEAwwJcnNhc3MtbWFjMB4XDTE3MDgyMjExMTgzMFoXDTE4MDgyMjExMTgzMFowFDESMBAGA1UEAwwJcnNhc3MtbWFjMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAymMQp4w6fwYDxofV05joravMyR60aNWYemzWJbb720KuqpH46sXsEe+bWyeC2/HRylduKRRhtZsIQAfsLiYRWq+jG55bt4tizmP8WLZo0/niuxvBVuaV2lUYxay22JdgM0EfUoAdQ925AjDzyRZXslFTJAxCxtuZZ6gdvzJ4DPQyV+q7/m2n3cPlQhMxGezVrQ3ymJkJwIeqBcljBazXAg2OzsCiE5cd98SgYMNglxv1mtATXABlIn1MMVxObmQJ7jMk3+C1m4Kk6YmSPZFWJLMuhUHDVfR0N0pUG08rx6rsA7h2GzZOFoKyzMWeY8HWK/fxTpnyvHrvl4fPcX7lswIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQCT6o0PuH4ZpU4fP4ahSHCi0vob7F+bEy/NDIbH5rz79z7rxFjk2A4fmXoSsl56DCSuQT685FeL8D3yZdCJNrKdCw5Vp3Rv4xX1GOn0DNq9n8qMKRGAWgUKQAekHp/+8EBG5YSICDsslDPrDQTiPnVO8LXN8Rdr4zUFf+Kfpfg1XX4sDIZ0b67jOmJQ/h+s4oiuFcgbCr26DwtVO3SOJoYI4V84HYROaP7KGffDoLMIV2JpfodsbHMvzSrcNYC2jEFzym/RdhMK5RPsd/4P5eYyY0vye6WQzBnKmK7cmTjYtAtaWsJz+jfppvIHM0Tk1DyP34qyM2YYngl49bKkEC1hocbn4gFApIceJxZDg9mQ0EGlUZ0=" + }, + { + "id": "6dc15218-ed83-49e0-85ab-bb891e3f42c9", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.password", + "value": "iOzPffanq1xj" + }, + { + "id": "b5662280-81cc-462e-bb84-726e47cb58e4", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.privatekey.password", + "value": "iOzPffanq1xj" + }, + { + "id": "2dd0d26d-3be4-4e80-a631-f7bda5311719", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.binding", + "value": "POST" + }, + { + "id": "717bf1d1-a5a3-460f-a0de-29e6b70a0027", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "metadata.invalidated", + "value": "false" + }, + { + "id": "cb20fa86-affb-4488-8b37-2e8c597fff66", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "okapi.url", + "value": "http://localhost:9230" + }, + { + "id": "81816efc-63b1-11ee-8c99-0242ac120002", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.callback", + "value": "callback" + }, + { + "id": "dcc452cf-f726-423b-91ec-ec89e36f9e98", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.useSecureTokens", + "value": "true" + } + ], + "totalRecords": 9 + }, + "receivedPath": "", + "sendData": {} + }, + { + "url": "/users?query=externalSystemId%3D%3D%22saml-user-id%22", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "users": [ + { + "id": "saml-user", + "username": "samluser", + "active": true + } + ] + }, + "receivedPath": "", + "sendData": {} + }, + { + "url": "/token/sign", + "method": "post", + "status": 201, + "receivedData": { + "accessToken": "saml-access-token", + "refreshToken": "saml-refresh-token", + "accessTokenExpiration": "2050-10-05T20:19:33Z", + "refreshTokenExpiration": "2050-10-05T20:19:33Z", + "tenantId": "diku" + } + }, + { + "url": "/user-tenants?externalSystemId=saml-user-id", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 0, + "userTenants": [] + } + } + ] +} diff --git a/src/test/resources/mock_idptest_redirect.json b/src/test/resources/mock_idptest_redirect.json index 26812246..537077fd 100644 --- a/src/test/resources/mock_idptest_redirect.json +++ b/src/test/resources/mock_idptest_redirect.json @@ -56,7 +56,7 @@ "value": "http://localhost:9230" } ], - "totalRecords": 6 + "totalRecords": 7 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_idptest_redirect_legacy.json b/src/test/resources/mock_idptest_redirect_legacy.json index b00e4eda..41f9f19d 100644 --- a/src/test/resources/mock_idptest_redirect_legacy.json +++ b/src/test/resources/mock_idptest_redirect_legacy.json @@ -63,7 +63,7 @@ "value": "callback" } ], - "totalRecords": 6 + "totalRecords": 9 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_idptest_redirect_secure_tokens.json b/src/test/resources/mock_idptest_redirect_secure_tokens.json new file mode 100644 index 00000000..a61b9e25 --- /dev/null +++ b/src/test/resources/mock_idptest_redirect_secure_tokens.json @@ -0,0 +1,117 @@ +{ + "mocks": [ + { + "url": "/configurations/entries?query=%28module%3D%3DLOGIN-SAML%20AND%20configName%3D%3Dsaml%29", + "method": "get", + "status": 200, + "receivedData": { + "configs": [ + { + "id": "60eead4f-de97-437c-9cb7-09966ce50e49", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "idp.url", + "value": "http://localhost:8888/simplesaml/saml2/idp/metadata.php" + }, + { + "id": "022d8342-fa51-44d1-8b2b-27da36e11f07", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.file", + "value": "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" + }, + { + "id": "6dc15218-ed83-49e0-85ab-bb891e3f42c9", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.password", + "value": "iOzPffanq1xj" + }, + { + "id": "b5662280-81cc-462e-bb84-726e47cb58e4", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "keystore.privatekey.password", + "value": "iOzPffanq1xj" + }, + { + "id": "2dd0d26d-3be4-4e80-a631-f7bda5311719", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.binding", + "value": "REDIRECT" + }, + { + "id": "717bf1d1-a5a3-460f-a0de-29e6b70a0027", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "metadata.invalidated", + "value": "false" + }, + { + "id": "cb20fa86-affb-4488-8b37-2e8c597fff66", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "okapi.url", + "value": "http://localhost:9230" + }, + { + "id": "81816efc-63b1-11ee-8c99-0242ac120002", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.callback", + "value": "callback" + }, + { + "id": "dcc452cf-f726-423b-91ec-ec89e36f9e98", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.useSecureTokens", + "value": "true" + } + ], + "totalRecords": 9 + }, + "receivedPath": "", + "sendData": {} + }, + { + "url": "/users?query=externalSystemId%3D%3D%22saml-user-id%22", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 1, + "users": [ + { + "id": "saml-user", + "username": "samluser", + "active": true + } + ] + }, + "receivedPath": "", + "sendData": {} + }, + { + "url": "/token/sign", + "method": "post", + "status": 201, + "receivedData": { + "accessToken": "saml-access-token", + "refreshToken": "saml-refresh-token", + "accessTokenExpiration": "2050-10-05T20:19:33Z", + "refreshTokenExpiration": "2050-10-05T20:19:33Z", + "tenantId": "diku" + } + }, + { + "url": "/user-tenants?externalSystemId=saml-user-id", + "method": "get", + "status": 200, + "receivedData": { + "totalRecords": 0, + "userTenants": [] + } + } + ] +} diff --git a/src/test/resources/mock_multiple_user_tenant.json b/src/test/resources/mock_multiple_user_tenant.json index cf916cb6..ca39c38f 100644 --- a/src/test/resources/mock_multiple_user_tenant.json +++ b/src/test/resources/mock_multiple_user_tenant.json @@ -61,10 +61,16 @@ "configName": "saml", "code": "user.property", "value": "username" + }, + { + "id": "81816efc-63b1-11ee-8c99-0242ac120002", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.callback", + "value": "callback" } - ], - "totalRecords": 6 + "totalRecords": 8 }, "receivedPath": "", "sendData": {} diff --git a/src/test/resources/mock_nouser.json b/src/test/resources/mock_nouser.json index 1befbbea..a965817f 100644 --- a/src/test/resources/mock_nouser.json +++ b/src/test/resources/mock_nouser.json @@ -47,14 +47,21 @@ "configName": "saml", "code": "metadata.invalidated", "value": "false" - },{ + }, + { "id": "cb20fa86-affb-4488-8b37-2e8c597fff66", "module": "LOGIN-SAML", "configName": "saml", "code": "okapi.url", "value": "http://localhost:9130" + }, + { + "id": "81816efc-63b1-11ee-8c99-0242ac120002", + "module": "LOGIN-SAML", + "configName": "saml", + "code": "saml.callback", + "value": "callback" } - ], "totalRecords": 6 },