org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext Maven / Gradle / Ivy
The newest version!
/*
* Licensed to the University Corporation for Advanced Internet Development,
* Inc. (UCAID) under one or more contributor license agreements. See the
* NOTICE file distributed with this work for additional information regarding
* copyright ownership. The UCAID licenses this file to You under the Apache
* License, Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.opensaml.saml.common.messaging.context;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
import org.opensaml.messaging.context.MessageContext;
import org.opensaml.saml.common.SAMLObject;
import org.opensaml.saml.saml1.core.AttributeQuery;
import org.opensaml.saml.saml1.core.AuthorizationDecisionQuery;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.NameIDType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Subcontext that carries information about a SAML peer entity.
*
*
* This context will often contain subcontexts, whose data is construed to be scoped to that peer entity.
*
*
*
* The method {@link #getEntityId()} will attempt to dynamically resolve the appropriate data
* from the SAML message held in the message context if the data has not been set statically
* by the corresponding setter method. This evaluation will be attempted only if the this
* context instance is an immediate child of the message context, as returned by {@link #getParent()}.
*
*/
public final class SAMLPeerEntityContext extends AbstractAuthenticatableSAMLEntityContext {
/** Logger. */
@Nonnull private Logger log = LoggerFactory.getLogger(SAMLPeerEntityContext.class);
/** Whether to use the resource of SAML 1 queries to resolve the entity ID. */
private boolean useSAML1QueryResourceAsEntityId;
/** Constructor. */
public SAMLPeerEntityContext() {
useSAML1QueryResourceAsEntityId = true;
}
/** {@inheritDoc} */
@Override
@Nullable @NotEmpty public String getEntityId() {
if (super.getEntityId() == null) {
setEntityId(resolveEntityId());
}
return super.getEntityId();
}
/**
* Gets whether to use the Resource attribute of some SAML 1 queries to resolve the entity
* ID.
*
* @return whether to use the Resource attribute of some SAML 1 queries to resolve the entity ID
*/
public boolean getUseSAML1QueryResourceAsEntityId() {
return useSAML1QueryResourceAsEntityId;
}
/**
* Sets whether to use the Resource attribute of some SAML 1 queries to resolve the entity ID.
*
* @param useResource whether to use the Resource attribute of some SAML 1 queries to resolve the entity ID
*/
public void setUseSAML1QueryResourceAsEntityId(final boolean useResource) {
useSAML1QueryResourceAsEntityId = useResource;
}
/**
* Dynamically resolve the SAML peer entity ID from the SAML protocol message held in
* {@link MessageContext#getMessage()}.
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String resolveEntityId() {
final SAMLObject samlMessage = resolveSAMLMessage();
//SAML 2 Request
if (samlMessage instanceof org.opensaml.saml.saml2.core.RequestAbstractType) {
final org.opensaml.saml.saml2.core.RequestAbstractType request =
(org.opensaml.saml.saml2.core.RequestAbstractType) samlMessage;
return processSaml2Request(request);
//SAML 2 Response
} else if (samlMessage instanceof org.opensaml.saml.saml2.core.StatusResponseType) {
final org.opensaml.saml.saml2.core.StatusResponseType response =
(org.opensaml.saml.saml2.core.StatusResponseType) samlMessage;
return processSaml2Response(response);
//SAML 1 Response
} else if (samlMessage instanceof org.opensaml.saml.saml1.core.Response) {
final org.opensaml.saml.saml1.core.Response response =
(org.opensaml.saml.saml1.core.Response) samlMessage;
return processSaml1Response(response);
//SAML 1 Request
} else if (samlMessage instanceof org.opensaml.saml.saml1.core.Request) {
final org.opensaml.saml.saml1.core.Request request =
(org.opensaml.saml.saml1.core.Request) samlMessage;
return processSaml1Request(request);
}
return null;
}
/**
* Resolve the SAML entity ID from a SAML 2 request.
*
* @param request the request
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml2Request(
@Nonnull final org.opensaml.saml.saml2.core.RequestAbstractType request) {
return processSaml2Issuer(request.getIssuer());
}
/**
* Resolve the SAML entity ID from a SAML 2 response.
*
* @param statusResponse the response
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml2Response(
@Nonnull final org.opensaml.saml.saml2.core.StatusResponseType statusResponse) {
if (statusResponse.getIssuer() != null) {
return processSaml2Issuer(statusResponse.getIssuer());
}
if (statusResponse instanceof org.opensaml.saml.saml2.core.Response) {
processSaml2ResponseAssertions((org.opensaml.saml.saml2.core.Response)statusResponse);
}
return null;
}
/**
* Resolve the SAML entity ID from the Assertions of a SAML 2 response.
*
* @param response the response
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml2ResponseAssertions(
@Nonnull final org.opensaml.saml.saml2.core.Response response) {
String issuer = null;
final List assertions = response.getAssertions();
if (assertions != null && assertions.size() > 0) {
log.info("Attempting to extract issuer from enclosed SAML 2.x Assertion(s)");
if (response.getEncryptedAssertions() != null && response.getEncryptedAssertions().size() > 0) {
log.warn("SAML 2.x Response '{}' contained both Assertions and EncryptedAssertions, "
+ "can not currently dynamically resolve SAML peer entity ID on that basis",
response.getID());
return null;
}
for (final org.opensaml.saml.saml2.core.Assertion assertion : assertions) {
if (assertion != null) {
final String current = processSaml2Issuer(assertion.getIssuer());
if (issuer != null && !issuer.equals(current)) {
log.warn("SAML 2.x assertions within response '{}' contain different issuer IDs, "
+ "can not dynamically resolve SAML peer entity ID", response.getID());
return null;
}
issuer = current;
}
}
}
if (issuer == null) {
log.warn("Issuer could not be extracted from standard SAML 2.x Response message via Assertions");
}
return issuer;
}
/**
* Resolve the SAML entity ID from a SAML 2 Issuer.
*
* @param issuer the issuer
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml2Issuer(@Nullable final Issuer issuer) {
if (issuer == null) {
return null;
}
if (issuer.getFormat() == null || issuer.getFormat().equals(NameIDType.ENTITY)) {
return issuer.getValue();
}
log.warn("Couldn't dynamically resolve SAML 2 peer entity ID due to unsupported NameID format: {}",
issuer.getFormat());
return null;
}
/**
* Resolve the SAML entity ID from a SAML 1 response.
*
* @param response the response
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml1Response(@Nonnull final org.opensaml.saml.saml1.core.Response response) {
String issuer = null;
final List assertions = response.getAssertions();
if (assertions != null && assertions.size() > 0) {
log.info("Attempting to extract issuer from enclosed SAML 1.x Assertion(s)");
for (final org.opensaml.saml.saml1.core.Assertion assertion : assertions) {
if (assertion != null && assertion.getIssuer() != null) {
if (issuer != null && !issuer.equals(assertion.getIssuer())) {
log.warn("SAML 1.x assertions within response '{}' contain different issuer IDs, "
+ "can not dynamically resolve SAML peer entity ID", response.getID());
return null;
}
issuer = assertion.getIssuer();
}
}
}
if (issuer == null) {
log.warn("Issuer could not be extracted from standard SAML 1.x response message");
}
return issuer;
}
/**
* Resolve the SAML entity ID from a SAML 1 request.
*
* @param request the request
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml1Request(@Nonnull final org.opensaml.saml.saml1.core.Request request) {
String entityId = null;
if (request.getAttributeQuery() != null) {
entityId = processSaml1AttributeQuery(request.getAttributeQuery());
if (entityId != null) {
return entityId;
}
}
if (request.getAuthorizationDecisionQuery() != null) {
entityId = processSaml1AuthorizationDecisionQuery(request.getAuthorizationDecisionQuery());
if (entityId != null) {
return entityId;
}
}
return null;
}
/**
* Resolve the SAML entity ID from a SAML 1 AttributeQuery.
*
* @param query the query
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml1AttributeQuery(@Nonnull final AttributeQuery query) {
if (getUseSAML1QueryResourceAsEntityId()) {
log.debug("Attempting to extract entity ID from SAML 1 AttributeQuery Resource attribute");
final String resource = StringSupport.trimOrNull(query.getResource());
if (resource != null) {
log.debug("Extracted entity ID from SAML 1.x AttributeQuery: {}", resource);
return resource;
}
}
return null;
}
/**
* Resolve the SAML entityID from a SAML 1 AuthorizationDecisionQuery.
*
* @param query the query
*
* @return the entity ID, or null if it could not be resolved
*/
@Nullable protected String processSaml1AuthorizationDecisionQuery(@Nonnull final AuthorizationDecisionQuery query) {
if (getUseSAML1QueryResourceAsEntityId()) {
log.debug("Attempting to extract entity ID from SAML 1 AuthorizationDecisionQuery Resource attribute");
final String resource = StringSupport.trimOrNull(query.getResource());
if (resource != null) {
log.debug("Extracted entity ID from SAML 1.x AuthorizationDecisionQuery: {}", resource);
return resource;
}
}
return null;
}
/**
* Resolve the SAML message from the message context.
*
* @return the SAML message, or null if it can not be resolved
*/
@Nullable protected SAMLObject resolveSAMLMessage() {
if (getParent() instanceof MessageContext) {
final MessageContext parent = (MessageContext) getParent();
if (parent.getMessage() instanceof SAMLObject) {
return (SAMLObject) parent.getMessage();
}
}
return null;
}
}