This is an automated email from the ASF dual-hosted git repository. ilgrosso pushed a commit to branch 4_0_X in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/4_0_X by this push: new c6de21359e [SYNCOPE-1898] Reset password with WA if mustChangePassword is true (#1141) c6de21359e is described below commit c6de21359e0611a262fdfe7d9e5d22870800e30f Author: alberto bogi <35602311+alberto-b...@users.noreply.github.com> AuthorDate: Thu Jul 24 17:08:42 2025 +0200 [SYNCOPE-1898] Reset password with WA if mustChangePassword is true (#1141) --- .../src/main/resources/wa-embedded.properties | 2 + .../apache/syncope/fit/ui/AbstractUIITCase.java | 68 +++++++ .../org/apache/syncope/fit/ui/OIDCC4UIITCase.java | 140 +++++++++++++++ .../org/apache/syncope/fit/ui/OpenFGAUIITCase.java | 13 ++ .../apache/syncope/fit/ui/SAML2SP4UIITCase.java | 196 +++++++++++++++++++++ .../wa/bootstrap/WAPropertySourceLocator.java | 3 + 6 files changed, 422 insertions(+) diff --git a/fit/wa-reference/src/main/resources/wa-embedded.properties b/fit/wa-reference/src/main/resources/wa-embedded.properties index 35c7c8b939..d8118ebf77 100644 --- a/fit/wa-reference/src/main/resources/wa-embedded.properties +++ b/fit/wa-reference/src/main/resources/wa-embedded.properties @@ -29,6 +29,8 @@ cas.server.prefix=${cas.server.name}/syncope-wa cas.authn.syncope.url=${cas.server.name}/syncope cas.authn.syncope.name=DefaultSyncopeAuthModule +cas.authn.pm.core.enabled=true + service.discovery.address=https://localhost:9443/syncope-wa/ ## diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java index 6378e72a94..ce61575e56 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/AbstractUIITCase.java @@ -20,19 +20,28 @@ package org.apache.syncope.fit.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import jakarta.ws.rs.core.Response; import java.io.IOException; +import java.util.Optional; +import org.apache.http.HttpStatus; +import org.apache.syncope.common.lib.Attr; import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.policy.AttrReleasePolicyTO; import org.apache.syncope.common.lib.policy.AuthPolicyTO; import org.apache.syncope.common.lib.policy.DefaultAttrReleasePolicyConf; import org.apache.syncope.common.lib.policy.DefaultAuthPolicyConf; +import org.apache.syncope.common.lib.request.UserCR; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.lib.types.PolicyType; import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.fit.AbstractITCase; +import org.jsoup.Connection; +import org.jsoup.Jsoup; +import org.jsoup.nodes.FormElement; import org.junit.jupiter.api.Test; public abstract class AbstractUIITCase extends AbstractITCase { @@ -104,8 +113,23 @@ public abstract class AbstractUIITCase extends AbstractITCase { }); } + protected static String extractWACSRF(final String body) { + FormElement form = (FormElement) Jsoup.parse(body).body().getElementsByTag("form").first(); + assertNotNull(form); + + Optional<String> execution = form.formData().stream(). + filter(keyval -> "_csrf".equals(keyval.key())). + map(Connection.KeyVal::value). + findFirst(); + assertTrue(execution.isPresent()); + + return execution.get(); + } + protected abstract void sso(String baseURL, String username, String password) throws IOException; + protected abstract void passwordManagement(String baseURL, String username, String password) throws IOException; + @Test public void sso2Console() throws IOException { sso(CONSOLE_ADDRESS, "bellini", "password"); @@ -116,6 +140,50 @@ public abstract class AbstractUIITCase extends AbstractITCase { sso(ENDUSER_ADDRESS, "bellini", "password"); } + @Test + public void passwordManagementConsole() throws IOException { + String key = null; + try { + UserCR userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "mustChangePassword").mustChangePassword( + Boolean.TRUE).password("Password123!") + .plainAttr(new Attr.Builder("fullname").value("mustChangePassword").build()) + .plainAttr(new Attr.Builder("userId").value("mustchangepassw...@syncope.org").build()) + .plainAttr(new Attr.Builder("surname").value("mustChangePassword").build()).build(); + + Response response = USER_SERVICE.create(userCR); + assertEquals(HttpStatus.SC_CREATED, response.getStatus()); + key = response.getHeaderString(RESTHeaders.RESOURCE_KEY); + + passwordManagement(CONSOLE_ADDRESS, "mustChangePassword", "Password123!"); + } finally { + if (key != null) { + USER_SERVICE.delete(key); + } + } + } + + @Test + public void passwordManagementEnduser() throws IOException { + String key = null; + try { + UserCR userCR = new UserCR.Builder(SyncopeConstants.ROOT_REALM, "mustChangePassword").mustChangePassword( + Boolean.TRUE).password("Password123!") + .plainAttr(new Attr.Builder("fullname").value("mustChangePassword").build()) + .plainAttr(new Attr.Builder("userId").value("mustchangepassw...@syncope.org").build()) + .plainAttr(new Attr.Builder("surname").value("mustChangePassword").build()).build(); + + Response response = USER_SERVICE.create(userCR); + assertEquals(HttpStatus.SC_CREATED, response.getStatus()); + key = response.getHeaderString(RESTHeaders.RESOURCE_KEY); + + passwordManagement(ENDUSER_ADDRESS, "mustChangePassword", "Password123!"); + } finally { + if (key != null) { + USER_SERVICE.delete(key); + } + } + } + @Test public void createUnmatching() throws IOException { try { diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java index 841804acb1..b4c4047b12 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OIDCC4UIITCase.java @@ -53,6 +53,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.to.OIDCC4UIProviderTO; import org.apache.syncope.common.lib.to.OIDCRPClientAppTO; +import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.ClientAppType; import org.apache.syncope.common.lib.types.OIDCResponseType; import org.apache.syncope.common.lib.types.OIDCSubjectType; @@ -289,6 +290,145 @@ public class OIDCC4UIITCase extends AbstractUIITCase { httpclient.execute(get, context); } + @Override + protected void passwordManagement(final String baseURL, final String username, final String password) + throws IOException { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpClientContext context = HttpClientContext.create(); + context.setCookieStore(new BasicCookieStore()); + + // 1. fetch login page + HttpGet get = new HttpGet(baseURL); + CloseableHttpResponse response = httpclient.execute(get, context); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + // 2. click on the OpenID Connect Provider + get = new HttpGet(baseURL + OIDCC4UIConstants.URL_CONTEXT + "/login?op=" + getAppName(baseURL)); + get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + response = httpclient.execute(get, context); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + // 2. redirected to WA login screen + String responseBody = EntityUtils.toString(response.getEntity()); + response = authenticateToWA(username, password, responseBody, httpclient, context); + + assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode()); + + // 3. redirected to WA reset password screen + responseBody = EntityUtils.toString(response.getEntity()); + + // check WA reset password screen + assertTrue(responseBody.contains("password")); + assertTrue(responseBody.contains("confirmedPassword")); + assertTrue(responseBody.contains("execution")); + + String execution = extractWAExecution(responseBody); + + // 3a. change password request + List<NameValuePair> form = new ArrayList<>(); + form.add(new BasicNameValuePair("_eventId", "submit")); + form.add(new BasicNameValuePair("execution", execution)); + form.add(new BasicNameValuePair("password", "PasswordChanged123!")); + form.add(new BasicNameValuePair("confirmedPassword", "PasswordChanged123!")); + + HttpPost post = new HttpPost(WA_ADDRESS + "/login"); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8)); + response = httpclient.execute(post, context); + + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + UserTO userTO = USER_SERVICE.read("mustChangePassword"); + + assertNotNull(userTO); + assertEquals(userTO.isMustChangePassword(), Boolean.FALSE); + + responseBody = EntityUtils.toString(response.getEntity()); + + assertTrue(responseBody.contains("execution")); + assertTrue(responseBody.contains("_csrf")); + + // 4. go to WA login screen + execution = extractWAExecution(responseBody); + String csrf = extractWACSRF(responseBody); + form.clear(); + form.add(new BasicNameValuePair("_eventId", "proceed")); + form.add(new BasicNameValuePair("_csrf", csrf)); + form.add(new BasicNameValuePair("execution", execution)); + + post = new HttpPost(WA_ADDRESS + "/login"); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8)); + response = httpclient.execute(post, context); + + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + responseBody = EntityUtils.toString(response.getEntity()); + + assertTrue(responseBody.contains("username")); + assertTrue(responseBody.contains("password")); + + response = authenticateToWA(username, "PasswordChanged123!", responseBody, httpclient, context); + + // 4a. WA attribute consent screen + responseBody = EntityUtils.toString(response.getEntity()); + + // check attribute repository + assertTrue(responseBody.contains("identifier")); + assertTrue(responseBody.contains("[value1]")); + + execution = extractWAExecution(responseBody); + + form.clear(); + form = new ArrayList<>(); + form.add(new BasicNameValuePair("_eventId", "confirm")); + form.add(new BasicNameValuePair("execution", execution)); + form.add(new BasicNameValuePair("option", "1")); + form.add(new BasicNameValuePair("reminder", "30")); + form.add(new BasicNameValuePair("reminderTimeUnit", "days")); + + post = new HttpPost(WA_ADDRESS + "/login"); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8)); + response = httpclient.execute(post, context); + + assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode()); + + // 4b. WA scope consent screen + get = new HttpGet(response.getLastHeader(HttpHeaders.LOCATION).getValue()); + get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + response = httpclient.execute(get, context); + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + responseBody = EntityUtils.toString(response.getEntity()); + + String allow = Jsoup.parse(responseBody).body(). + getElementsByTag("a").select("a[name=allow]").first(). + attr("href"); + assertNotNull(allow); + + // 4c. finally get requested content + get = new HttpGet(allow); + get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + response = httpclient.execute(get, context); + + // 4d. verify that user is now authenticated + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + assertTrue(EntityUtils.toString(response.getEntity()).contains(username)); + + // 5. logout + get = new HttpGet(CONSOLE_ADDRESS.equals(baseURL) + ? baseURL + "wicket/bookmarkable/org.apache.syncope.client.console.pages.Logout" + : baseURL + "wicket/bookmarkable/org.apache.syncope.client.enduser.pages.Logout"); + httpclient.execute(get, context); + } + @Override protected void doSelfReg(final Runnable runnable) { runnable.run(); diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java index 0f6533985a..20984326ad 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/OpenFGAUIITCase.java @@ -47,6 +47,7 @@ import org.apache.syncope.common.rest.api.RESTHeaders; import org.apache.syncope.common.rest.api.beans.ExecSpecs; import org.apache.syncope.common.rest.api.service.wa.WAConfigService; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; public class OpenFGAUIITCase extends OIDCC4UIITCase { @@ -154,4 +155,16 @@ public class OpenFGAUIITCase extends OIDCC4UIITCase { public void createUnmatching() throws IOException { assumeFalse(true); } + + @Disabled + @Override + public void passwordManagementConsole() { + // ignore + } + + @Disabled + @Override + public void passwordManagementEnduser() { + // ignore + } } diff --git a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java index 7462cf9485..d3fd41dc15 100644 --- a/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java +++ b/fit/wa-reference/src/test/java/org/apache/syncope/fit/ui/SAML2SP4UIITCase.java @@ -20,6 +20,7 @@ package org.apache.syncope.fit.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -53,6 +54,7 @@ import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.to.Item; import org.apache.syncope.common.lib.to.SAML2SP4UIIdPTO; import org.apache.syncope.common.lib.to.SAML2SPClientAppTO; +import org.apache.syncope.common.lib.to.UserTO; import org.apache.syncope.common.lib.types.ClientAppType; import org.apache.syncope.common.lib.types.SAML2SPNameId; import org.apache.syncope.common.rest.api.RESTHeaders; @@ -302,6 +304,200 @@ public class SAML2SP4UIITCase extends AbstractUIITCase { httpclient.execute(get, context); } + @Override + protected void passwordManagement(final String baseURL, final String username, final String password) + throws IOException { + CloseableHttpClient httpclient = HttpClients.createDefault(); + HttpClientContext context = HttpClientContext.create(); + context.setCookieStore(new BasicCookieStore()); + + // 1. fetch login page + HttpGet get = new HttpGet(baseURL); + try (CloseableHttpResponse response = httpclient.execute(get, context)) { + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + } + + // 2. click on the SAML 2.0 IdP + get = new HttpGet(baseURL + SAML2SP4UIConstants.URL_CONTEXT + + "/login?idp=https%3A//localhost%3A9443/syncope-wa/saml"); + String responseBody; + try (CloseableHttpResponse response = httpclient.execute(get, context)) { + responseBody = EntityUtils.toString(response.getEntity()); + } + Triple<String, String, String> parsed = parseSAMLRequestForm(responseBody); + + // 2a. post SAML request + HttpPost post = new HttpPost(parsed.getLeft()); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity( + List.of(new BasicNameValuePair("RelayState", parsed.getMiddle()), + new BasicNameValuePair("SAMLRequest", parsed.getRight())), Consts.UTF_8)); + String location; + try (CloseableHttpResponse response = httpclient.execute(post, context)) { + assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode()); + location = response.getFirstHeader(HttpHeaders.LOCATION).getValue(); + } + + // 2b. authenticate + post = new HttpPost(location); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.addHeader(HttpHeaders.REFERER, get.getURI().toASCIIString()); + try (CloseableHttpResponse response = httpclient.execute(post, context)) { + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + responseBody = EntityUtils.toString(response.getEntity()); + } + try (CloseableHttpResponse response = + authenticateToWA(username, password, responseBody, httpclient, context)) { + assertEquals(HttpStatus.SC_UNAUTHORIZED, response.getStatusLine().getStatusCode()); + + // 3. redirected to WA reset password screen + responseBody = EntityUtils.toString(response.getEntity()); + + // check WA reset password screen + assertTrue(responseBody.contains("password")); + assertTrue(responseBody.contains("confirmedPassword")); + assertTrue(responseBody.contains("execution")); + } + + String execution = extractWAExecution(responseBody); + + // 3a. change password request + List<NameValuePair> form = new ArrayList<>(); + form.add(new BasicNameValuePair("_eventId", "submit")); + form.add(new BasicNameValuePair("execution", execution)); + form.add(new BasicNameValuePair("password", "PasswordChanged123!")); + form.add(new BasicNameValuePair("confirmedPassword", "PasswordChanged123!")); + + post = new HttpPost(WA_ADDRESS + "/login"); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8)); + try (CloseableHttpResponse response = httpclient.execute(post, context)) { + UserTO userTO = USER_SERVICE.read("mustChangePassword"); + + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + assertNotNull(userTO); + assertEquals(userTO.isMustChangePassword(), Boolean.FALSE); + + responseBody = EntityUtils.toString(response.getEntity()); + + assertTrue(responseBody.contains("execution")); + assertTrue(responseBody.contains("_csrf")); + } + + // 4. go to WA login screen + execution = extractWAExecution(responseBody); + String csrf = extractWACSRF(responseBody); + form.clear(); + form.add(new BasicNameValuePair("_eventId", "proceed")); + form.add(new BasicNameValuePair("_csrf", csrf)); + form.add(new BasicNameValuePair("execution", execution)); + + post = new HttpPost(WA_ADDRESS + "/login"); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8)); + try (CloseableHttpResponse response = httpclient.execute(post, context)) { + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + + responseBody = EntityUtils.toString(response.getEntity()); + + assertTrue(responseBody.contains("username")); + assertTrue(responseBody.contains("password")); + } + + // 2b. authenticate + post = new HttpPost(location); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.addHeader(HttpHeaders.REFERER, get.getURI().toASCIIString()); + try (CloseableHttpResponse response = httpclient.execute(post, context)) { + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + responseBody = EntityUtils.toString(response.getEntity()); + } + boolean isOk = false; + try (CloseableHttpResponse response = + authenticateToWA(username, "PasswordChanged123!", responseBody, httpclient, context)) { + + switch (response.getStatusLine().getStatusCode()) { + case HttpStatus.SC_OK: + isOk = true; + responseBody = EntityUtils.toString(response.getEntity()); + break; + + case HttpStatus.SC_MOVED_TEMPORARILY: + location = response.getFirstHeader(HttpHeaders.LOCATION).getValue(); + break; + + default: + fail(); + } + } + + // 2c. WA attribute consent screen + if (isOk) { + execution = extractWAExecution(responseBody); + + form.clear(); + form = new ArrayList<>(); + form.add(new BasicNameValuePair("_eventId", "confirm")); + form.add(new BasicNameValuePair("execution", execution)); + form.add(new BasicNameValuePair("option", "1")); + form.add(new BasicNameValuePair("reminder", "30")); + form.add(new BasicNameValuePair("reminderTimeUnit", "days")); + + post = new HttpPost(WA_ADDRESS + "/login"); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8)); + try (CloseableHttpResponse response = httpclient.execute(post, context)) { + assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode()); + location = response.getFirstHeader(HttpHeaders.LOCATION).getValue(); + } + } + + if (location.startsWith("http://localhost:8080/syncope-wa")) { + location = WA_ADDRESS + StringUtils.substringAfter(location, "http://localhost:8080/syncope-wa"); + } + + get = new HttpGet(location); + get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + try (CloseableHttpResponse response = httpclient.execute(get, context)) { + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + responseBody = EntityUtils.toString(response.getEntity()); + } + + // 2d. post SAML response + parsed = parseSAMLResponseForm(responseBody); + + post = new HttpPost(parsed.getLeft()); + post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML); + post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE); + post.setEntity(new UrlEncodedFormEntity( + List.of(new BasicNameValuePair("RelayState", parsed.getMiddle()), + new BasicNameValuePair("SAMLResponse", parsed.getRight())), Consts.UTF_8)); + try (CloseableHttpResponse response = httpclient.execute(post, context)) { + assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode()); + location = response.getFirstHeader(HttpHeaders.LOCATION).getValue(); + } + + // 3. verify that user is now authenticated + get = new HttpGet(baseURL + Strings.CS.removeStart(location, "../")); + try (CloseableHttpResponse response = httpclient.execute(get, context)) { + assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + assertTrue(EntityUtils.toString(response.getEntity()).contains(username)); + } + + // 4. logout + get = new HttpGet(CONSOLE_ADDRESS.equals(baseURL) + ? baseURL + "wicket/bookmarkable/org.apache.syncope.client.console.pages.Logout" + : baseURL + "wicket/bookmarkable/org.apache.syncope.client.enduser.pages.Logout"); + httpclient.execute(get, context); + } + @Override protected void doSelfReg(final Runnable runnable) { List<SAML2SP4UIIdPTO> idps = SAML2SP4UI_IDP_SERVICE.list(); diff --git a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java index 3d5098012a..120652772a 100644 --- a/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java +++ b/wa/bootstrap/src/main/java/org/apache/syncope/wa/bootstrap/WAPropertySourceLocator.java @@ -124,6 +124,9 @@ public class WAPropertySourceLocator implements PropertySourceLocator { properties.putAll(index(map, prefixes)); }); + properties.put("cas.authn.pm.syncope.url", + StringUtils.substringBefore(syncopeClient.getAddress(), "/rest")); + Set<String> customClaims = syncopeClient.getService(WAClientAppService.class).list().stream(). filter(app -> app.getClientAppTO() instanceof OIDCRPClientAppTO && app.getAttrReleasePolicy() != null). flatMap(app -> {