KEYCLOAK-4504 New configuration option for SAML Broker:

* postBindingLogout: Indicates if POST or redirect should be used for the logout requests.

This applies to both IdP-initiated logout, and Keycloak-initiated logout. If unset (for example when upgrading Keycloak), the setting is initially set to the same as postBindingResponse.

The flag is also set when importing IdP metadata.
This commit is contained in:
Anders Båtstrand 2017-02-24 14:02:54 +01:00
parent 75909a0add
commit 8d82390843
7 changed files with 39 additions and 9 deletions

View file

@ -303,7 +303,7 @@ public class SAMLEndpoint {
builder.issuer(issuerURL); builder.issuer(issuerURL);
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder() JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
.relayState(relayState); .relayState(relayState);
boolean postBinding = config.isPostBindingResponse(); boolean postBinding = config.isPostBindingLogout();
if (config.isWantAuthnRequestsSigned()) { if (config.isWantAuthnRequestsSigned()) {
KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm); KeyManager.ActiveRsaKey keys = session.keys().getActiveRsaKey(realm);
String keyName = config.getXmlSigKeyInfoKeyNameTransformer().getKeyName(keys.getKid(), keys.getCertificate()); String keyName = config.getXmlSigKeyInfoKeyNameTransformer().getKeyName(keys.getKid(), keys.getCertificate());

View file

@ -184,12 +184,15 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
try { try {
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl); SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
JaxrsSAML2BindingBuilder binding = buildLogoutBinding(session, userSession, realm); JaxrsSAML2BindingBuilder binding = buildLogoutBinding(session, userSession, realm);
return binding.postBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl); if (getConfig().isPostBindingLogout()) {
return binding.postBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl);
} else {
return binding.redirectBinding(logoutBuilder.buildDocument()).request(singleLogoutServiceUrl);
}
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
} }
protected SAML2LogoutRequestBuilder buildLogoutRequest(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm, String singleLogoutServiceUrl) { protected SAML2LogoutRequestBuilder buildLogoutRequest(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm, String singleLogoutServiceUrl) {

View file

@ -161,6 +161,20 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
getConfig().put("postBindingResponse", String.valueOf(postBindingResponse)); getConfig().put("postBindingResponse", String.valueOf(postBindingResponse));
} }
public boolean isPostBindingLogout() {
String postBindingLogout = getConfig().get("postBindingLogout");
if (postBindingLogout == null) {
// To maintain unchanged behavior when adding this field, we set the inital value to equal that
// of the binding for the response:
return isPostBindingResponse();
}
return Boolean.valueOf(postBindingLogout);
}
public void setPostBindingLogout(boolean postBindingLogout) {
getConfig().put("postBindingLogout", String.valueOf(postBindingLogout));
}
public boolean isBackchannelSupported() { public boolean isBackchannelSupported() {
return Boolean.valueOf(getConfig().get("backchannelSupported")); return Boolean.valueOf(getConfig().get("backchannelSupported"));
} }

View file

@ -84,11 +84,12 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
if (idpDescriptor != null) { if (idpDescriptor != null) {
SAMLIdentityProviderConfig samlIdentityProviderConfig = new SAMLIdentityProviderConfig(); SAMLIdentityProviderConfig samlIdentityProviderConfig = new SAMLIdentityProviderConfig();
String singleSignOnServiceUrl = null; String singleSignOnServiceUrl = null;
boolean postBinding = false; boolean postBindingResponse = false;
boolean postBindingLogout = false;
for (EndpointType endpoint : idpDescriptor.getSingleSignOnService()) { for (EndpointType endpoint : idpDescriptor.getSingleSignOnService()) {
if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) { if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
singleSignOnServiceUrl = endpoint.getLocation().toString(); singleSignOnServiceUrl = endpoint.getLocation().toString();
postBinding = true; postBindingResponse = true;
break; break;
} else if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){ } else if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
singleSignOnServiceUrl = endpoint.getLocation().toString(); singleSignOnServiceUrl = endpoint.getLocation().toString();
@ -96,10 +97,11 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
} }
String singleLogoutServiceUrl = null; String singleLogoutServiceUrl = null;
for (EndpointType endpoint : idpDescriptor.getSingleLogoutService()) { for (EndpointType endpoint : idpDescriptor.getSingleLogoutService()) {
if (postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) { if (postBindingResponse && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
singleLogoutServiceUrl = endpoint.getLocation().toString(); singleLogoutServiceUrl = endpoint.getLocation().toString();
postBindingLogout = true;
break; break;
} else if (!postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){ } else if (!postBindingResponse && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
singleLogoutServiceUrl = endpoint.getLocation().toString(); singleLogoutServiceUrl = endpoint.getLocation().toString();
break; break;
} }
@ -110,8 +112,9 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned()); samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
samlIdentityProviderConfig.setAddExtensionsElementWithKeyInfo(false); samlIdentityProviderConfig.setAddExtensionsElementWithKeyInfo(false);
samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned()); samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
samlIdentityProviderConfig.setPostBindingResponse(postBinding); samlIdentityProviderConfig.setPostBindingResponse(postBindingResponse);
samlIdentityProviderConfig.setPostBindingAuthnRequest(postBinding); samlIdentityProviderConfig.setPostBindingAuthnRequest(postBindingResponse);
samlIdentityProviderConfig.setPostBindingLogout(postBindingLogout);
List<KeyDescriptorType> keyDescriptor = idpDescriptor.getKeyDescriptor(); List<KeyDescriptorType> keyDescriptor = idpDescriptor.getKeyDescriptor();
String defaultCertificate = null; String defaultCertificate = null;

View file

@ -529,6 +529,7 @@ public class IdentityProviderTest extends AbstractAdminTest {
assertThat(config.keySet(), containsInAnyOrder( assertThat(config.keySet(), containsInAnyOrder(
"validateSignature", "validateSignature",
"singleLogoutServiceUrl", "singleLogoutServiceUrl",
"postBindingLogout",
"postBindingResponse", "postBindingResponse",
"postBindingAuthnRequest", "postBindingAuthnRequest",
"singleSignOnServiceUrl", "singleSignOnServiceUrl",

View file

@ -523,6 +523,8 @@ http-post-binding-response=HTTP-POST Binding Response
http-post-binding-response.tooltip=Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used. http-post-binding-response.tooltip=Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
http-post-binding-for-authn-request=HTTP-POST Binding for AuthnRequest http-post-binding-for-authn-request=HTTP-POST Binding for AuthnRequest
http-post-binding-for-authn-request.tooltip=Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used. http-post-binding-for-authn-request.tooltip=Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
http-post-binding-logout=HTTP-POST Binding Logout
http-post-binding-logout.tooltip=Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
want-authn-requests-signed=Want AuthnRequests Signed want-authn-requests-signed=Want AuthnRequests Signed
want-authn-requests-signed.tooltip=Indicates whether the identity provider expects signed a AuthnRequest. want-authn-requests-signed.tooltip=Indicates whether the identity provider expects signed a AuthnRequest.
force-authentication=Force Authentication force-authentication=Force Authentication

View file

@ -142,6 +142,13 @@
</div> </div>
<kc-tooltip>{{:: 'http-post-binding-for-authn-request.tooltip' | translate}}</kc-tooltip> <kc-tooltip>{{:: 'http-post-binding-for-authn-request.tooltip' | translate}}</kc-tooltip>
</div> </div>
<div class="form-group">
<label class="col-md-2 control-label" for="postBindingLogout">{{:: 'http-post-binding-logout' | translate}}</label>
<div class="col-md-6">
<input ng-model="identityProvider.config.postBindingLogout" id="postBindingLogout" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
<kc-tooltip>{{:: 'http-post-binding-logout.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group"> <div class="form-group">
<label class="col-md-2 control-label" for="wantAuthnRequestsSigned">{{:: 'want-authn-requests-signed' | translate}}</label> <label class="col-md-2 control-label" for="wantAuthnRequestsSigned">{{:: 'want-authn-requests-signed' | translate}}</label>
<div class="col-md-6"> <div class="col-md-6">