Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(oxshib): slo redirect #182

Merged
merged 2 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,5 @@ public String getExtraHttpParameters() {
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
* @author Yuriy Movchan
* @version 0.1, 09/13/2018
*/
@WebServlet(name = "ShibOxAuthAuthServlet", urlPatterns = { "/Authn/oxAuth/*" })
@WebServlet(name = "ShibOxAuthAuthServlet", urlPatterns = { "/ceva/auth/*" })
public class ShibOxAuthAuthServlet extends HttpServlet {

private static final long serialVersionUID = -4864851392327422662L;
Expand Down Expand Up @@ -229,6 +229,7 @@ private void processAuthorizationResponse(final HttpServletRequest request, fina

final UserProfile userProfile = authClient.getUserProfile(openIdCredentials, context);
LOG.debug("User profile : {}", userProfile);


if (userProfile == null) {
LOG.error("Token validation failed, returning InvalidToken");
Expand Down Expand Up @@ -320,7 +321,7 @@ protected void startLoginRequest(final HttpServletRequest request, final HttpSer
}
}
}catch(ExternalAuthenticationException e) {
LOG.debug("Could not set extra parameters for the request. Extra request parameters will not be available to oxAuth",e);
LOG.info("Could not set extra parameters for the request. Extra request parameters will not be available to oxAuth",e);
}


Expand All @@ -338,7 +339,7 @@ protected void startLoginRequest(final HttpServletRequest request, final HttpSer
Function<ProfileRequestContext, RelyingPartyContext> authenticationContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
final RelyingPartyContext relyingPartyContext = authenticationContextLookupStrategy.apply(profileRequestContext);
if (relyingPartyContext != null) {
ProfileConfiguration profileConfiguration = relyingPartyContext.getProfileConfig();
ProfileConfiguration profileConfiguration = relyingPartyContext.getProfileConfig();
if (profileConfiguration instanceof BrowserSSOProfileConfiguration) {
List<Principal> principals = ((BrowserSSOProfileConfiguration) profileConfiguration).getDefaultAuthenticationMethods(profileRequestContext);
acrs = principals.stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.gluu.idp.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;

import org.gluu.persist.annotation.AttributeName;
import org.gluu.persist.annotation.DataEntry;
import org.gluu.persist.annotation.DN;
import org.gluu.persist.annotation.ObjectClass;
import org.gluu.persist.model.base.InumEntry;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

import com.fasterxml.jackson.annotation.JsonInclude.Include;

/**
* Vanilla Trust relationship model
* Just enough data in it to extract some useful information
* (e.g. logout redirect uri)
*/

@DataEntry()
@ObjectClass(value="gluuSAMLconfig")
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown=true)
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class GluuVanillaTrustRelationship {

private static final long serialVersionUID = -1L;

@DN
private String dn;

@AttributeName(ignoreDuringUpdate=true)
private String inum;

@AttributeName(name = "oxAuthPostLogoutRedirectURI")
private String spLogoutURL;

@AttributeName(name="spLogoutRedirectUrl")
private String spLogoutRedirectUrl;

public String getDn() {

return this.dn;
}

public void setDn(final String dn) {

this.dn = dn;
}

public String getInum() {

return this.inum;
}

public void setInum(final String inum) {

this.inum = inum;
}

public String getSpLogoutURL() {

return this.spLogoutURL;
}

public void setSpLogoutURL(final String spLogoutURL) {

this.spLogoutURL = spLogoutURL;
}

public String getSpLogoutRedirectUrl() {

return this.spLogoutRedirectUrl;
}

public void setSpLogoutRedirectUrl(final String spLogoutRedirectUrl) {

this.spLogoutRedirectUrl = spLogoutRedirectUrl;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.gluu.idp.service;

import java.util.function.Function;
import java.util.Iterator;

import net.shibboleth.idp.profile.context.RelyingPartyContext;
import org.gluu.idp.model.GluuVanillaTrustRelationship;
import org.gluu.idp.service.GluuVanillaTrustRelationshipService;

import org.opensaml.messaging.context.navigate.ChildContextLookup;
import org.opensaml.profile.context.ProfileRequestContext;
import net.shibboleth.idp.profile.context.MultiRelyingPartyContext;
import net.shibboleth.idp.session.context.LogoutContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



public class GluuCustomViewService {

private final Logger log = LoggerFactory.getLogger(GluuCustomViewService.class);
private GluuVanillaTrustRelationshipService trService;

public GluuCustomViewService(final GluuVanillaTrustRelationshipService trService) {

log.info("GluuCustomViewService() constructor");
this.trService = trService;
}

public String getRelyingPartyLogoutRedirectUrl(final ProfileRequestContext prContext, final MultiRelyingPartyContext mrpContext, LogoutContext logoutContext) {


String ret = getRelyingPartyLogoutRedirectUrlFromPrContext(prContext);
if(ret == null) {
ret = getRelyingPartyLogoutRedirectUrlFromMultiRpContext(mrpContext,logoutContext);
}
return ret;
}

private String getRelyingPartyLogoutRedirectUrlFromPrContext(final ProfileRequestContext prContext) {

try {
log.debug("Getting logout url for the currently active relying party");
final Function<ProfileRequestContext,RelyingPartyContext> rpCtxLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
final RelyingPartyContext rpCtx = rpCtxLookupStrategy.apply(prContext);
if(rpCtx == null) {
log.debug("Could not obtain the relying party context from the profile request context");
return null;
}
GluuVanillaTrustRelationship tr = trService.findTrustRelationshipByRelyingParty(rpCtx.getRelyingPartyId());
if(tr == null) {
log.debug("No trust relationship found associated to the relying party {}",rpCtx.getRelyingPartyId());
return null;
}
return tr.getSpLogoutRedirectUrl();
}catch(Exception e) {
log.debug("Error while fetching logout url for currently active relying party",e);
return null;
}
}

private String getRelyingPartyLogoutRedirectUrlFromMultiRpContext(final MultiRelyingPartyContext mrpContext, final LogoutContext logoutContext) {

try {
log.debug("Getting logout url from MultiRelyingPartyContext and/or LogoutContext");
if(mrpContext == null || logoutContext == null) {
log.debug("MultiRelyingPartyContext object is null");
return null;
}

for(String relyingPartyId : logoutContext.getSessionMap().keySet()) {
log.debug("Processing relying party with id " + relyingPartyId);
GluuVanillaTrustRelationship tr = trService.findTrustRelationshipByRelyingParty(relyingPartyId);
if(tr != null && tr.getSpLogoutRedirectUrl() != null && !tr.getSpLogoutRedirectUrl().isEmpty()) {
return tr.getSpLogoutRedirectUrl();
}
}
log.debug("No RelyingPartyContext iterated upon has a logout url");
return null;
}catch(Exception e) {
log.debug("Error while fetching logout url from MultiRelyingPartyContext",e);
return null;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.gluu.idp.service;

import java.util.List;

import org.gluu.idp.externalauth.openid.conf.IdpConfigurationFactory;
import org.gluu.search.filter.Filter;

import org.gluu.idp.model.GluuVanillaTrustRelationship;
import org.gluu.persist.PersistenceEntryManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class GluuVanillaTrustRelationshipService {

private static final String ORG_DN = "o=gluu";
private static final Logger log = LoggerFactory.getLogger(GluuVanillaTrustRelationshipService.class);

private PersistenceEntryManager persistenceEntryManager;

public GluuVanillaTrustRelationshipService(final IdpConfigurationFactory configurationFactory) {

persistenceEntryManager = configurationFactory.getPersistenceEntryManager();
}

public GluuVanillaTrustRelationship findTrustRelationshipByInum(String inum) {

try {
log.debug("findTrustRelationshipByInum() -- inum {}",inum);
String [] attributes= new String[] {"dn","inum","oxAuthPostLogoutRedirectURI","spLogoutRedirectUrl"};
return persistenceEntryManager.find(getDnForTrustRelationship(inum),GluuVanillaTrustRelationship.class,attributes);
}catch(Exception e) {
log.error(String.format("Error fetching TrustRelationship with inum -- %s",inum),e);
return null;
}
}

public GluuVanillaTrustRelationship findTrustRelationshipByRelyingParty(String relyingPartyId) {

try {
log.debug("findTrustRelationshipByRelyingParty() -- relyingPartyId {}",relyingPartyId);
final String [] attributes = new String [] {"dn","inum","oxAuthPostLogoutRedirectURI","spLogoutRedirectUrl"};
String dn = getDnForTrustRelationship(null);
Filter filter = Filter.createEqualityFilter("gluuEntityId",relyingPartyId).multiValued();
List<GluuVanillaTrustRelationship> trlist = persistenceEntryManager.findEntries(dn,GluuVanillaTrustRelationship.class,filter,attributes,1);
if(trlist.isEmpty()) {
log.debug("findTrustRelationshipByRelyingParty() -- no entries found for relyingParty {}",relyingPartyId);
return null;
}
return trlist.get(0);
}catch(Exception e) {
log.error(String.format("Error fetching TrustRelationship with relyingPartyId -- %s",relyingPartyId),e);
return null;
}
}

public String getDnForTrustRelationship(final String inum) {

if(inum == null || inum.isEmpty()) {

return String.format("ou=trustRelationships,%s",ORG_DN);
}else {
return String.format("inum=%s,ou=trustRelationships,%s",inum,ORG_DN);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff -aurN shibboleth-idp.orig/conf/authn/oxauth-authn-global.xml shibboleth-idp/conf/authn/oxauth-authn-global.xml
--- shibboleth-idp.orig/conf/authn/oxauth-authn-global.xml 2024-04-08 14:17:50.468803551 +0100
+++ shibboleth-idp/conf/authn/oxauth-authn-global.xml 2024-04-08 15:39:24.208219767 +0100
@@ -55,4 +55,8 @@

<bean id="shibboleth.gluu.AuthResultReuseCondition" class="org.gluu.idp.externalauth.OxAuthReuseResultByAcr">
</bean>
+
+ <bean id="gluuVanillaTrustRelationshipService" class="org.gluu.idp.service.GluuVanillaTrustRelationshipService">
+ <constructor-arg name="configurationFactory" value="#{idpConfigurationFactory.instance()}"/>
+ </bean>
</beans>
32 changes: 32 additions & 0 deletions static/src/main/patches/018.add-gluu-custom-view-bean.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
diff -aurN shibboleth-idp.orig/conf/global.xml shibboleth-idp/conf/global.xml
--- shibboleth-idp.orig/conf/global.xml 2024-04-08 15:47:28.754193000 +0100
+++ shibboleth-idp/conf/global.xml 2024-04-10 10:28:12.856588766 +0100
@@ -64,5 +64,6 @@
<import resource="conditional:${idp.home}/conf/gluu-storage.xml"/>
<import resource="conditional:${idp.home}/conf/gluu-release-attributes.xml"/>
<import resource="conditional:${idp.home}/conf/gluu-populate-context.xml"/>
+ <import resource="conditional:${idp.home}/conf/gluu-custom-view-bean.xml"/>

</beans>
diff -aurN shibboleth-idp.orig/conf/gluu-custom-view-bean.xml shibboleth-idp/conf/gluu-custom-view-bean.xml
--- shibboleth-idp.orig/conf/gluu-custom-view-bean.xml 1970-01-01 01:00:00.000000000 +0100
+++ shibboleth-idp/conf/gluu-custom-view-bean.xml 2024-04-10 11:16:32.107680798 +0100
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
+
+ default-init-method="initialize"
+ default-destroy-method="destroy">
+
+ <bean id="shibboleth.CustomViewContext" class="org.gluu.idp.service.GluuCustomViewService">
+ <constructor-arg name="vanillaTrustRelationshipService" ref="gluuVanillaTrustRelationshipService"/>
+ </bean>
+</beans>
67 changes: 67 additions & 0 deletions static/src/main/patches/019.add-custom-view-context.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
diff -aurN shibboleth-idp.orig/views/logout-complete.vm shibboleth-idp/views/logout-complete.vm
--- shibboleth-idp.orig/views/logout-complete.vm 2024-04-14 12:46:38.725262468 +0100
+++ shibboleth-idp/views/logout-complete.vm 2024-04-12 17:50:00.374229000 +0100
@@ -16,6 +16,8 @@
##
#set ($activeIdPSessions = $logoutContext and !$logoutContext.getIdPSessions().isEmpty())
#set ($activeSPSessions = $logoutContext and !$logoutContext.getSessionMap().isEmpty())
+#set ($gluuLogoutRedirectUrl = $custom.getRelyingPartyLogoutRedirectUrl($profileRequestContext))
+#set ($gluuLogoutRedirectMethod="get")
<!DOCTYPE html>
<html>
<head>
diff -aurN shibboleth-idp.orig/views/logout.vm shibboleth-idp/views/logout.vm
--- shibboleth-idp.orig/views/logout.vm 2024-04-14 12:46:38.729262496 +0100
+++ shibboleth-idp/views/logout.vm 2024-04-12 18:00:19.684950000 +0100
@@ -20,6 +20,8 @@
#end
#set ($promptForIdP = $logoutContext and !$logoutContext.getIdPSessions().isEmpty())
#set ($promptForSP = $logoutContext and !$logoutContext.getSessionMap().isEmpty())
+#set ($gluuLogoutRedirectUrl = $custom.getRelyingPartyLogoutRedirectUrl($profileRequestContext))
+#set ($gluuLogoutRedirectMethod="get")
<!DOCTYPE html>
<html>
<head>
@@ -100,6 +102,22 @@
<p><strong>#springMessageText("idp.logout.complete", "The logout operation is complete, and no other services appear to have been accessed during this session.")</strong></p>
<!-- Complete the flow by adding a hidden iframe. -->
<iframe style="display:none" src="$flowExecutionUrl&_eventId=proceed"></iframe>
+
+ <!-- gluu logout redirect url -->
+ #if($gluuLogoutRedirectUrl)
+ <p>
+ <strong>
+ #springMessageText("idp.gluulogout.redirect-msg","You will be redirected shortly")
+ </strong>
+ </p>
+ <form id="f_logout_redirect" method="$gluuLogoutRedirectMethod" action="$gluuLogoutRedirectUrl">
+ <p>
+ <button class="button-secondary" type="submit" name="_gluu_action" value="redirect">
+ #springMessageText("idp.gluu.logout.button","Click here if it's taking too long")
+ </button>
+ </p>
+ </form>
+ #end
#end

<ul>
@@ -107,7 +125,18 @@
<li><a href="#springMessageText("idp.url.helpdesk", '#')">#springMessageText("idp.login.needHelp", "Need Help?")</a></li>
</ul>
</section>
-
+ #if($gluuLogoutRedirectUrl)
+ <script type="text/javascript">
+ function performRedirect() {
+
+ var redir_form = document.getElementById("f_logout_redirect");
+ if(redir_form != null) {
+ redir_form.submit();
+ }
+ }
+ setTimeout(performRedirect,5000); // 5 seconds , making sure the hidden iframe to complete the flow has run
+ </script>
+ #end
</main>
<footer class="footer">
<div class="cc">
Loading
Loading