From 06181b500a08e6326cd9a5b55ecc131a59e3def8 Mon Sep 17 00:00:00 2001 From: Okke Harsta Date: Tue, 18 Jun 2024 12:52:36 +0200 Subject: [PATCH] Remember idp switch choice after stepup --- .../dashboard/control/LoginController.java | 28 +++++++++++++++++-- .../dashboard/domain/IdentityProvider.java | 2 +- .../dashboard/domain/ServiceProvider.java | 2 +- .../dashboard/manage/UrlResourceManage.java | 2 +- ...olethPreAuthenticatedProcessingFilter.java | 13 ++++++++- .../shibboleth/mock/MockShibbolethFilter.java | 7 +++-- .../src/main/resources/application.properties | 2 +- 7 files changed, 46 insertions(+), 10 deletions(-) diff --git a/dashboard-server/src/main/java/dashboard/control/LoginController.java b/dashboard-server/src/main/java/dashboard/control/LoginController.java index 1ed2bc8b7..88412f522 100644 --- a/dashboard-server/src/main/java/dashboard/control/LoginController.java +++ b/dashboard-server/src/main/java/dashboard/control/LoginController.java @@ -1,5 +1,8 @@ package dashboard.control; +import dashboard.domain.CoinUser; +import dashboard.domain.IdentityProvider; +import dashboard.util.SpringSecurity; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.StringUtils; @@ -8,6 +11,7 @@ import org.springframework.web.bind.annotation.RestController; import javax.servlet.ServletException; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @@ -17,21 +21,26 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; @RestController public class LoginController { + public final static String IDP_ID_COOKIE_NAME = "IDP_ID_COOKIE_NAME"; private final String mailBaseUrl; private final Map loaLevels; + private final boolean secureCookie; public LoginController(@Value("${mailBaseUrl}") String mailBaseUrl, - @Value("${loa_values_supported}") String loaLevels) { + @Value("${loa_values_supported}") String loaLevels, + @Value("${server.servlet.session.cookie.secure}") boolean secureCookie) { this.mailBaseUrl = mailBaseUrl; this.loaLevels = Arrays.stream(loaLevels.replaceAll("\"", "").split(",")) .map(String::trim) .collect(Collectors.toMap(level -> Integer.valueOf(level.substring(level.length() - 1)), level -> level)); + this.secureCookie = secureCookie; } @RequestMapping(value = "/startSSO") @@ -49,8 +58,23 @@ public void start(HttpServletRequest request, @RequestParam("redirect_url") String redirectUrl, @RequestParam(value = "loa", required = false) Integer loa) throws IOException, ServletException { + //Need to remember the switched IdP, we will check if the IdP is allowed on login again + CoinUser currentUser = SpringSecurity.getCurrentUser(); + Optional switchedToIdp = currentUser.getSwitchedToIdp(); + switchedToIdp.ifPresentOrElse(idp -> { + Cookie cookie = new Cookie(IDP_ID_COOKIE_NAME, idp.getId()); + cookie.setSecure(secureCookie); + cookie.setHttpOnly(true); + response.addCookie(cookie); + }, () -> { + Cookie cookie = new Cookie(IDP_ID_COOKIE_NAME, ""); + cookie.setSecure(secureCookie); + cookie.setMaxAge(0); + cookie.setHttpOnly(true); + response.addCookie(cookie); + }); SecurityContextHolder.clearContext(); - request.logout(); + //We could do this client side, but one extra redirect is not a problem String target = "/startSSO?redirect_url=" + redirectUrl; String shibbolethLogin = String.format("/Shibboleth.sso/Login?target=%s%s", diff --git a/dashboard-server/src/main/java/dashboard/domain/IdentityProvider.java b/dashboard-server/src/main/java/dashboard/domain/IdentityProvider.java index bd6a50d98..92abe1113 100644 --- a/dashboard-server/src/main/java/dashboard/domain/IdentityProvider.java +++ b/dashboard-server/src/main/java/dashboard/domain/IdentityProvider.java @@ -56,7 +56,7 @@ public IdentityProvider(String id, String institutionId, String name, Long eid) public IdentityProvider(Map metaData) { super(metaData); - this.institutionId = (String) metaData.get("coin:institution_id"); + this.institutionId = (String) metaData.get("coin:institution_guid"); this.disableConsent = (List) metaData.getOrDefault("disableConsent", new ArrayList<>()); addKeywords("en", (String) metaData.get("keywords:en")); addKeywords("nl", (String) metaData.get("keywords:nl")); diff --git a/dashboard-server/src/main/java/dashboard/domain/ServiceProvider.java b/dashboard-server/src/main/java/dashboard/domain/ServiceProvider.java index dd5d936cf..1f2d9a235 100644 --- a/dashboard-server/src/main/java/dashboard/domain/ServiceProvider.java +++ b/dashboard-server/src/main/java/dashboard/domain/ServiceProvider.java @@ -81,7 +81,7 @@ public ServiceProvider(Map metaData) { super(metaData); this.dashboardConnectOption = DashboardConnectOption.fromOption((String) metaData.getOrDefault("coin:dashboard_connect_option", "connect_with_interaction")); this.applicationUrl = (String) metaData.get("coin:application_url"); - this.institutionId = (String) metaData.get("coin:institution_id"); + this.institutionId = (String) metaData.get("coin:institution_guid"); this.eulaURL = (String) metaData.get("coin:eula"); this.interfedSource = (String) metaData.getOrDefault("coin:interfed_source", "SURFconext"); this.privacyStatementUrlEn = (String) metaData.get("mdui:PrivacyStatementURL:en"); diff --git a/dashboard-server/src/main/java/dashboard/manage/UrlResourceManage.java b/dashboard-server/src/main/java/dashboard/manage/UrlResourceManage.java index 0f3018712..c0a8c7288 100644 --- a/dashboard-server/src/main/java/dashboard/manage/UrlResourceManage.java +++ b/dashboard-server/src/main/java/dashboard/manage/UrlResourceManage.java @@ -33,7 +33,7 @@ public class UrlResourceManage implements Manage { private final String body = "{" + requestedAttributes + "}"; private final String bodyForEntity = "{\"entityid\":\"@@entityid@@\", " + requestedAttributes + "}"; private final String bodyForInstitutionId = - "{\"metaDataFields.coin:institution_id\":\"@@institution_id@@\", \"ALL_ATTRIBUTES\":true}"; + "{\"metaDataFields.coin:institution_guid\":\"@@institution_id@@\", \"ALL_ATTRIBUTES\":true}"; private final String linkedQuery = "{$and: [{$or:[{\"data.allowedEntities.name\": {$in: [\"@@entityid@@\"]}}, {\"data" + ".allowedall\": true}]}]}"; diff --git a/dashboard-server/src/main/java/dashboard/shibboleth/ShibbolethPreAuthenticatedProcessingFilter.java b/dashboard-server/src/main/java/dashboard/shibboleth/ShibbolethPreAuthenticatedProcessingFilter.java index a63490ed2..a8d05c49d 100644 --- a/dashboard-server/src/main/java/dashboard/shibboleth/ShibbolethPreAuthenticatedProcessingFilter.java +++ b/dashboard-server/src/main/java/dashboard/shibboleth/ShibbolethPreAuthenticatedProcessingFilter.java @@ -14,12 +14,14 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.util.*; import java.util.stream.Stream; import static com.google.common.base.Preconditions.checkState; +import static dashboard.control.LoginController.IDP_ID_COOKIE_NAME; import static dashboard.domain.CoinAuthority.Authority.*; import static dashboard.shibboleth.ShibbolethHeader.*; import static java.util.Collections.singletonList; @@ -150,7 +152,6 @@ protected Object getPreAuthenticatedPrincipal(final HttpServletRequest request) String idpId = authorityOptional.orElseThrow(() -> new IllegalArgumentException(String.format("Missing %s Shibboleth header (%s)", Shib_Authenticating_Authority.getValue(), request.getRequestURL()))); - CoinUser coinUser = new CoinUser(); coinUser.setUid(uid); coinUser.setDisplayName(getFirstShibHeaderValue(Shib_DisplayName, request).orElse(null)); @@ -181,6 +182,16 @@ protected Object getPreAuthenticatedPrincipal(final HttpServletRequest request) coinUser.setIdp(getCurrentIdp(idpId, institutionIdentityProviders)); coinUser.getInstitutionIdps().addAll(institutionIdentityProviders); Collections.sort(coinUser.getInstitutionIdps(), Comparator.comparing(Provider::getName)); + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + Stream.of(cookies).filter(cookie -> IDP_ID_COOKIE_NAME.equals(cookie.getName())) + .findAny() + .ifPresent(cookie -> { + IdentityProvider switchedToIdp = institutionIdentityProviders.stream() + .filter(idp -> idp.getId().equals(cookie.getValue())).findAny().orElse(null); + coinUser.setSwitchedToIdp(switchedToIdp); + }); + } } Optional roles = sab.getRoles(uid); diff --git a/dashboard-server/src/main/java/dashboard/shibboleth/mock/MockShibbolethFilter.java b/dashboard-server/src/main/java/dashboard/shibboleth/mock/MockShibbolethFilter.java index 966f75b8f..0b38ea828 100644 --- a/dashboard-server/src/main/java/dashboard/shibboleth/mock/MockShibbolethFilter.java +++ b/dashboard-server/src/main/java/dashboard/shibboleth/mock/MockShibbolethFilter.java @@ -26,8 +26,8 @@ public class MockShibbolethFilter extends GenericFilterBean { public static final String idp = "http://mock-idp";//,"https://idp.surfnet.nl";//"https://localhost.surf.id"; //"https://idp.surf.nl" public final String authnContextClass = "urn:oasis:names:tc:SAML:2.0:ac:classes:Password"; // public final String authnContextClass = "http://test2.surfconext.nl/assurance/loa2"; -// public String role = "admin"; - public String role = "super"; + public String role = "admin"; +// public String role = "super"; @Override public void doFilter(ServletRequest servletRequest, ServletResponse response, FilterChain chain) throws IOException, @@ -57,7 +57,8 @@ public void doFilter(ServletRequest servletRequest, ServletResponse response, Fi wrapper.setHeader(Shib_EduPersonScopedAffiliation.getValue(), "urn:mace:terena.org:tcs:eduPersonScopedAffiliation"); wrapper.setHeader(Shib_SURFEckid.getValue(), "some surf eckid value"); - wrapper.setHeader(HTTP_X_IDP_ENTITY_ID, idp); + String entityIdHeader = request.getHeader(HTTP_X_IDP_ENTITY_ID); + wrapper.setHeader(HTTP_X_IDP_ENTITY_ID, StringUtils.hasText(entityIdHeader) ? entityIdHeader : idp); wrapper.setHeader(Shib_AuthnContext_Class.getValue(), authnContextClass); switch (role) { diff --git a/dashboard-server/src/main/resources/application.properties b/dashboard-server/src/main/resources/application.properties index 6e1e37b50..bf3f5ac2e 100644 --- a/dashboard-server/src/main/resources/application.properties +++ b/dashboard-server/src/main/resources/application.properties @@ -9,7 +9,7 @@ static.baseurl=http://localhost:8280/ # 8 hours server.servlet.session.timeout=28800 - +server.servlet.session.cookie.secure=false manage.username=dashboard manage.password=secret manage.manageBaseUrl=https://manage.test2.surfconext.nl