| // Copyright 2008 Google Inc. |
| // |
| // Licensed 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 com.google.enterprise.adaptor.secmgr.saml; |
| |
| import static org.opensaml.common.xml.SAMLConstants.SAML20P_NS; |
| |
| import com.google.common.collect.Lists; |
| |
| import org.joda.time.DateTime; |
| import org.opensaml.Configuration; |
| import org.opensaml.DefaultBootstrap; |
| import org.opensaml.common.IdentifierGenerator; |
| import org.opensaml.common.SAMLObject; |
| import org.opensaml.common.SAMLObjectBuilder; |
| import org.opensaml.common.SAMLVersion; |
| import org.opensaml.common.binding.BasicEndpointSelector; |
| import org.opensaml.common.binding.BasicSAMLMessageContext; |
| import org.opensaml.common.binding.SAMLMessageContext; |
| import org.opensaml.common.binding.artifact.BasicSAMLArtifactMap; |
| import org.opensaml.common.binding.artifact.SAMLArtifactMap; |
| import org.opensaml.common.binding.artifact.SAMLArtifactMap.SAMLArtifactMapEntry; |
| import org.opensaml.common.impl.SecureRandomIdentifierGenerator; |
| import org.opensaml.saml2.core.Action; |
| import org.opensaml.saml2.core.Artifact; |
| import org.opensaml.saml2.core.ArtifactResolve; |
| import org.opensaml.saml2.core.ArtifactResponse; |
| import org.opensaml.saml2.core.Assertion; |
| import org.opensaml.saml2.core.Attribute; |
| import org.opensaml.saml2.core.AttributeStatement; |
| import org.opensaml.saml2.core.Audience; |
| import org.opensaml.saml2.core.AudienceRestriction; |
| import org.opensaml.saml2.core.AuthnContext; |
| import org.opensaml.saml2.core.AuthnContextClassRef; |
| import org.opensaml.saml2.core.AuthnRequest; |
| import org.opensaml.saml2.core.AuthnStatement; |
| import org.opensaml.saml2.core.AuthzDecisionQuery; |
| import org.opensaml.saml2.core.AuthzDecisionStatement; |
| import org.opensaml.saml2.core.Conditions; |
| import org.opensaml.saml2.core.DecisionTypeEnumeration; |
| import org.opensaml.saml2.core.Issuer; |
| import org.opensaml.saml2.core.NameID; |
| import org.opensaml.saml2.core.RequestAbstractType; |
| import org.opensaml.saml2.core.Response; |
| import org.opensaml.saml2.core.Statement; |
| import org.opensaml.saml2.core.Status; |
| import org.opensaml.saml2.core.StatusCode; |
| import org.opensaml.saml2.core.StatusMessage; |
| import org.opensaml.saml2.core.StatusResponseType; |
| import org.opensaml.saml2.core.Subject; |
| import org.opensaml.saml2.core.SubjectConfirmation; |
| import org.opensaml.saml2.core.SubjectConfirmationData; |
| import org.opensaml.saml2.metadata.EntityDescriptor; |
| import org.opensaml.saml2.metadata.KeyDescriptor; |
| import org.opensaml.saml2.metadata.AssertionConsumerService; |
| import org.opensaml.saml2.metadata.RoleDescriptor; |
| import org.opensaml.saml2.metadata.SingleSignOnService; |
| import org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider; |
| import org.opensaml.saml2.metadata.provider.MetadataProviderException; |
| import org.opensaml.saml2.metadata.provider.ObservableMetadataProvider; |
| import org.opensaml.security.MetadataCredentialResolver; |
| import org.opensaml.util.storage.MapBasedStorageService; |
| import org.opensaml.ws.message.MessageContext; |
| import org.opensaml.ws.message.decoder.MessageDecoder; |
| import org.opensaml.ws.message.decoder.MessageDecodingException; |
| import org.opensaml.ws.message.encoder.MessageEncoder; |
| import org.opensaml.ws.message.encoder.MessageEncodingException; |
| import org.opensaml.ws.security.SecurityPolicy; |
| import org.opensaml.ws.security.SecurityPolicyResolver; |
| import org.opensaml.ws.security.SecurityPolicyRule; |
| import org.opensaml.ws.security.provider.BasicSecurityPolicy; |
| import org.opensaml.ws.security.provider.StaticSecurityPolicyResolver; |
| import org.opensaml.xml.ConfigurationException; |
| import org.opensaml.xml.XMLObject; |
| import org.opensaml.xml.XMLObjectBuilderFactory; |
| import org.opensaml.xml.io.MarshallingException; |
| import org.opensaml.xml.io.UnmarshallingException; |
| import org.opensaml.xml.parse.BasicParserPool; |
| import org.opensaml.xml.security.CriteriaSet; |
| import org.opensaml.xml.security.SecurityException; |
| import org.opensaml.xml.security.SecurityHelper; |
| import org.opensaml.xml.security.credential.Credential; |
| import org.opensaml.xml.security.credential.CredentialResolver; |
| import org.opensaml.xml.security.credential.UsageType; |
| import org.opensaml.xml.security.keyinfo.BasicProviderKeyInfoCredentialResolver; |
| import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver; |
| import org.opensaml.xml.security.keyinfo.KeyInfoCriteria; |
| import org.opensaml.xml.security.keyinfo.KeyInfoProvider; |
| import org.opensaml.xml.security.keyinfo.provider.DSAKeyValueProvider; |
| import org.opensaml.xml.security.keyinfo.provider.InlineX509DataProvider; |
| import org.opensaml.xml.security.keyinfo.provider.RSAKeyValueProvider; |
| import org.opensaml.xml.security.trust.TrustEngine; |
| import org.opensaml.xml.signature.KeyInfo; |
| import org.w3c.dom.Element; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.security.KeyException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PrivateKey; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.logging.Logger; |
| |
| import javax.annotation.concurrent.Immutable; |
| import javax.xml.namespace.QName; |
| |
| /** |
| * A collection of utilities to support OpenSAML programming. The majority of the |
| * definitions here are static factory methods for SAML objects. |
| * <p> |
| * <strong>Notes on security policies and credential resolution:</strong> |
| * <p> |
| * OpenSAML has a very complicated mechanism for dealing with credentials and trust, of |
| * which we use only a small part. Here are the basic components: |
| * |
| * <dl> |
| * <dt>{@link Credential} |
| * <dd>Some information that can be used for signing or encryption. |
| * |
| * <dt>{@link CredentialResolver} |
| * <dd>Selects one or more credentials based on a set of criteria. We currently use only |
| * {@link MetadataCredentialResolver}, which gets credentials from metadata using criteria |
| * like entity ID and role. |
| * |
| * <dt>{@link KeyInfoCredentialResolver} |
| * <dd>Extracts one or more credentials from a {@link KeyInfo} element; it's allowed to |
| * choose between different credentials based on internal criteria. We currently use a |
| * KeyInfoCredentialResolver that selects only X.509 certificate credentials. |
| * |
| * <dt>{@link TrustEngine} |
| * <dd>Evaluates the trustworthiness and validity of a given object against some given |
| * criteria. It is used as an element of some policy rules. |
| * |
| * <dt>{@link SecurityPolicy} |
| * <dd>A collection of policy rules, evaluated against a message context, that determines |
| * if a message is well-formed, valid, and otherwise okay to process. |
| * |
| * <dt>{@link SecurityPolicyRule} |
| * <dd>A component of a security policy, also evaluated against a message context. |
| * |
| * <dt>{@link SecurityPolicyResolver} |
| * <dd>Uses a given set of criteria to select a security policy. We currently use only a |
| * static policy resolver, which always returns the same policy. |
| * </dl> |
| * |
| * <p> |
| * The programmer simply attaches a security-policy resolver to the SAML message context |
| * and OpenSAML will automatically enforce the security policy as appropriate. |
| */ |
| @Immutable |
| public final class OpenSamlUtil { |
| private static final Logger LOGGER = Logger.getLogger(OpenSamlUtil.class.getName()); |
| |
| /** |
| * The human-readable name of the (GSA) service provider. |
| */ |
| public static final String GOOGLE_PROVIDER_NAME = "Google Search Appliance"; |
| |
| /** |
| * The human-readable name of the (Security Manager) service provider. |
| */ |
| public static final String SM_PROVIDER_NAME = "Google Security Manager"; |
| |
| /** |
| * The SAML "bearer" method, normally used in SubjectConfirmation. |
| */ |
| public static final String BEARER_METHOD = "urn:oasis:names:tc:SAML:2.0:cm:bearer"; |
| |
| public static final String SAML_EXTENSIONS_TAG = "Extensions"; |
| |
| public static final String GSA_SESSION_ID_TAG = "GsaSessionId"; |
| |
| public static final String GSA_FLAG_FAST_AUTHZ_TAG = "EnableFastAuthz"; |
| public static final String GSA_FAST_AUTHZ_FAST = "FAST"; |
| public static final String GSA_FAST_AUTHZ_ALL = "ALL"; |
| |
| public static final String GOOGLE_NS_URI = "http://www.google.com/"; |
| public static final String GOOGLE_NS_PREFIX = "google:"; |
| public static final String GOOGLE_NS_PREFIX_NOCOLON = "google"; |
| /*public static final Namespace GOOGLE_NS = |
| Namespace.getNamespace(GOOGLE_NS_PREFIX_NOCOLON, GOOGLE_NS_URI);*/ |
| |
| static { |
| try { |
| DefaultBootstrap.bootstrap(); |
| } catch (ConfigurationException e) { |
| throw new IllegalStateException(e); |
| } |
| |
| // This is required in order to patch around missing code in OpenSAML. |
| Configuration.registerObjectProvider( |
| AttributeValue.DEFAULT_ELEMENT_NAME, |
| new AttributeValueBuilder(), |
| new AttributeValueMarshaller(), |
| new AttributeValueUnmarshaller()); |
| } |
| |
| private static final XMLObjectBuilderFactory objectBuilderFactory = |
| Configuration.getBuilderFactory(); |
| |
| // TODO(cph): @SuppressWarnings is needed because objectBuilderFactory.getBuilder() returns a |
| // supertype of the actual type. |
| @SuppressWarnings("unchecked") |
| private static <T extends SAMLObject> SAMLObjectBuilder<T> makeSamlObjectBuilder(QName name) { |
| return (SAMLObjectBuilder<T>) objectBuilderFactory.getBuilder(name); |
| } |
| |
| private static final SAMLObjectBuilder<Action> actionBuilder = |
| makeSamlObjectBuilder(Action.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Artifact> artifactBuilder = |
| makeSamlObjectBuilder(Artifact.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<ArtifactResolve> artifactResolveBuilder = |
| makeSamlObjectBuilder(ArtifactResolve.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<ArtifactResponse> artifactResponseBuilder = |
| makeSamlObjectBuilder(ArtifactResponse.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Assertion> assertionBuilder = |
| makeSamlObjectBuilder(Assertion.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AssertionConsumerService> assertionConsumerServiceBuilder = |
| makeSamlObjectBuilder(AssertionConsumerService.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Attribute> attributeBuilder = |
| makeSamlObjectBuilder(Attribute.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AttributeStatement> attributeStatementBuilder = |
| makeSamlObjectBuilder(AttributeStatement.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AttributeValue> attributeValueBuilder = |
| makeSamlObjectBuilder(AttributeValue.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Audience> audienceBuilder = |
| makeSamlObjectBuilder(Audience.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AudienceRestriction> audienceRestrictionBuilder = |
| makeSamlObjectBuilder(AudienceRestriction.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AuthnContext> authnContextBuilder = |
| makeSamlObjectBuilder(AuthnContext.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder = |
| makeSamlObjectBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AuthnRequest> authnRequestBuilder = |
| makeSamlObjectBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AuthnStatement> authnStatementBuilder = |
| makeSamlObjectBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AuthzDecisionQuery> authzDecisionQueryBuilder = |
| makeSamlObjectBuilder(AuthzDecisionQuery.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<AuthzDecisionStatement> authzDecisionStatementBuilder = |
| makeSamlObjectBuilder(AuthzDecisionStatement.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Conditions> conditionsBuilder = |
| makeSamlObjectBuilder(Conditions.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Issuer> issuerBuilder = |
| makeSamlObjectBuilder(Issuer.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<NameID> nameIDBuilder = |
| makeSamlObjectBuilder(NameID.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Response> responseBuilder = |
| makeSamlObjectBuilder(Response.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Status> statusBuilder = |
| makeSamlObjectBuilder(Status.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<StatusCode> statusCodeBuilder = |
| makeSamlObjectBuilder(StatusCode.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<StatusMessage> statusMessageBuilder = |
| makeSamlObjectBuilder(StatusMessage.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<Subject> subjectBuilder = |
| makeSamlObjectBuilder(Subject.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<SubjectConfirmation> subjectConfirmationBuilder = |
| makeSamlObjectBuilder(SubjectConfirmation.DEFAULT_ELEMENT_NAME); |
| private static final SAMLObjectBuilder<SubjectConfirmationData> subjectConfirmationDataBuilder = |
| makeSamlObjectBuilder(SubjectConfirmationData.DEFAULT_ELEMENT_NAME); |
| |
| // Metadata builders |
| |
| private static final SAMLObjectBuilder<SingleSignOnService> singleSignOnServiceBuilder = |
| makeSamlObjectBuilder(SingleSignOnService.DEFAULT_ELEMENT_NAME); |
| |
| // Identifier generator |
| |
| private static final IdentifierGenerator idGenerator; |
| static { |
| try { |
| idGenerator = new SecureRandomIdentifierGenerator(); |
| } catch (NoSuchAlgorithmException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| // Non-instantiable class. |
| private OpenSamlUtil() { |
| } |
| |
| private static void initializeRequest(RequestAbstractType request, String issuer, |
| DateTime issueInstant) { |
| request.setID(generateIdentifier()); |
| request.setVersion(SAMLVersion.VERSION_20); |
| request.setIssuer(makeIssuer(issuer)); |
| request.setIssueInstant(issueInstant); |
| } |
| |
| private static void initializeResponse(StatusResponseType response, String issuer, |
| DateTime issueInstant, Status status, String inResponseTo) { |
| response.setID(generateIdentifier()); |
| response.setVersion(SAMLVersion.VERSION_20); |
| response.setIssuer(makeIssuer(issuer)); |
| response.setIssueInstant(issueInstant); |
| response.setStatus(status); |
| response.setInResponseTo(inResponseTo); |
| } |
| |
| /** |
| * Static factory for SAML {@link Action} objects. |
| * |
| * @param name A URI identifying the represented action. |
| * @param namespace A URI identifying the class of names being specified. |
| * @return A new <code>Action</code> object. |
| */ |
| public static Action makeAction(String name, String namespace) { |
| Action action = actionBuilder.buildObject(); |
| action.setAction(name); |
| action.setNamespace(namespace); |
| return action; |
| } |
| |
| /** |
| * Static factory for SAML {@link Artifact} objects. |
| * |
| * @param value The artifact string. |
| * @return A new <code>Artifact</code> object. |
| */ |
| private static Artifact makeArtifact(String value) { |
| Artifact element = artifactBuilder.buildObject(); |
| element.setArtifact(value); |
| return element; |
| } |
| |
| /** |
| * Static factory for SAML {@link ArtifactResolve} objects. |
| * |
| * @param issuer The entity issuing this request. |
| * @param issueInstant The time of issue for this statement. |
| * @param value The artifact string to be resolved. |
| * @return A new <code>ArtifactResolve</code> object. |
| */ |
| public static ArtifactResolve makeArtifactResolve(String issuer, DateTime issueInstant, |
| String value) { |
| ArtifactResolve request = artifactResolveBuilder.buildObject(); |
| initializeRequest(request, issuer, issueInstant); |
| request.setArtifact(makeArtifact(value)); |
| return request; |
| } |
| |
| /** |
| * Static factory for SAML {@link ArtifactResponse} objects. |
| * |
| * @param issuer The entity issuing this response. |
| * @param issueInstant The time of issue for this statement. |
| * @param status The <code>Status</code> object indicating the success of the resolution. |
| * @param inResponseTo The message ID of the request this is a response to. |
| * @param message The embedded message. |
| * @return A new <code>ArtifactResponse</code> object. |
| */ |
| public static ArtifactResponse makeArtifactResponse(String issuer, DateTime issueInstant, |
| Status status, String inResponseTo, SAMLObject message) { |
| ArtifactResponse response = artifactResponseBuilder.buildObject(); |
| initializeResponse(response, issuer, issueInstant, status, inResponseTo); |
| if (message != null) { |
| response.setMessage(message); |
| } |
| return response; |
| } |
| |
| /** |
| * Static factory for SAML {@link Assertion} objects. |
| * |
| * @param issuer The entity issuing this assertion. |
| * @param issueInstant The time of issue for this statement. |
| * @param subject The subject of the assertion. |
| * @param conditions The conditions under which this assertion is valid. |
| * @param statements Statements being made by this assertion. |
| * @return A new <code>Assertion</code> object. |
| */ |
| public static Assertion makeAssertion(String issuer, DateTime issueInstant, Subject subject, |
| Conditions conditions, Statement... statements) { |
| Assertion assertion = assertionBuilder.buildObject(); |
| assertion.setID(generateIdentifier()); |
| assertion.setVersion(SAMLVersion.VERSION_20); |
| assertion.setIssuer(makeIssuer(issuer)); |
| assertion.setIssueInstant(issueInstant); |
| assertion.setSubject(subject); |
| if (conditions != null) { |
| assertion.setConditions(conditions); |
| } |
| for (Statement statement : statements) { |
| if (statement instanceof AuthnStatement) { |
| assertion.getAuthnStatements().add((AuthnStatement) statement); |
| } else if (statement instanceof AuthzDecisionStatement) { |
| assertion.getAuthzDecisionStatements().add((AuthzDecisionStatement) statement); |
| } else if (statement instanceof AttributeStatement) { |
| assertion.getAttributeStatements().add((AttributeStatement) statement); |
| } else { |
| throw new IllegalArgumentException("Unknown statement type: " + statement); |
| } |
| } |
| return assertion; |
| } |
| |
| /** |
| * Static factory for SAML {@link AssertionConsumerService} objects. |
| * |
| * @param location A URL for this service. |
| * @param binding A SAML binding used to communicate with the service. |
| * @return A new {@code AssertionConsumerService} object. |
| */ |
| public static AssertionConsumerService makeAssertionConsumerService(String location, |
| String binding) { |
| AssertionConsumerService endpoint = assertionConsumerServiceBuilder.buildObject(); |
| endpoint.setLocation(location); |
| endpoint.setBinding(binding); |
| endpoint.setIndex(0); |
| endpoint.setIsDefault(true); |
| return endpoint; |
| } |
| |
| /** |
| * Static factory for SAML {@link Attribute} objects. |
| * |
| * @param name The attribute name. |
| * @return A new <code>Attribute</code> object. |
| */ |
| public static Attribute makeAttribute(String name) { |
| Attribute attribute = attributeBuilder.buildObject(); |
| attribute.setName(name); |
| return attribute; |
| } |
| |
| /** |
| * Static factory for SAML {@link AttributeStatement} objects. |
| * |
| * @param attributes The attributes to include in the statement. |
| * @return A new <code>AttributeStatement</code> object. |
| */ |
| public static AttributeStatement makeAttributeStatement(Attribute... attributes) { |
| AttributeStatement statement = attributeStatementBuilder.buildObject(); |
| for (Attribute attribute : attributes) { |
| if (attribute != null) { |
| statement.getAttributes().add(attribute); |
| } |
| } |
| return statement; |
| } |
| |
| /** |
| * Static factory for SAML {@link AttributeValue} objects. |
| * |
| * @return A new <code>AttributeValue</code> object. |
| */ |
| public static AttributeValue makeAttributeValue(String value) { |
| AttributeValue attrValue = attributeValueBuilder.buildObject(); |
| attrValue.setValue(value); |
| return attrValue; |
| } |
| |
| /** |
| * Static factory for SAML {@link Audience} objects. |
| * |
| * @param uri The audience URI. |
| * @return A new <code>Audience</code> object. |
| */ |
| private static Audience makeAudience(String uri) { |
| Audience audience = audienceBuilder.buildObject(); |
| audience.setAudienceURI(uri); |
| return audience; |
| } |
| |
| /** |
| * Static factory for SAML {@link AudienceRestriction} objects. |
| * |
| * @param uris The audience URIs. |
| * @return A new <code>AudienceRestriction</code> object. |
| */ |
| public static AudienceRestriction makeAudienceRestriction(String... uris) { |
| AudienceRestriction restriction = audienceRestrictionBuilder.buildObject(); |
| for (String uri : uris) { |
| restriction.getAudiences().add(makeAudience(uri)); |
| } |
| return restriction; |
| } |
| |
| /** |
| * Static factory for SAML {@link AuthnContext} objects. |
| * |
| * @param classRef An <code>AuthnContextClassRef</code> identifying an authentication context |
| * class. |
| * @return A new <code>AuthnContext</code> object. |
| */ |
| private static AuthnContext makeAuthnContext(AuthnContextClassRef classRef) { |
| AuthnContext context = authnContextBuilder.buildObject(); |
| context.setAuthnContextClassRef(classRef); |
| return context; |
| } |
| |
| /** |
| * Static factory for SAML {@link AuthnContext} objects. |
| * |
| * A convenience method that wraps the given URI in an {@link AuthnContextClassRef} object. |
| * |
| * @param uri A URI identifying an authentication context class. |
| * @return A new <code>AuthnContext</code> object. |
| */ |
| private static AuthnContext makeAuthnContext(String uri) { |
| return makeAuthnContext(makeAuthnContextClassRef(uri)); |
| } |
| |
| /** |
| * Static factory for SAML {@link AuthnContextClassRef} objects. |
| * |
| * @param uri A URI identifying an authentication context class. |
| * @return A new <code>AuthnContextClassRef</code> object. |
| */ |
| private static AuthnContextClassRef makeAuthnContextClassRef(String uri) { |
| AuthnContextClassRef classRef = authnContextClassRefBuilder.buildObject(); |
| classRef.setAuthnContextClassRef(uri); |
| return classRef; |
| } |
| |
| /** |
| * Static factory for SAML {@link AuthnRequest} objects. |
| * |
| * @param issuer The entity issuing this request. |
| * @param issueInstant The time of issue for this statement. |
| * @return A new <code>AuthnRequest</code> object. |
| */ |
| public static AuthnRequest makeAuthnRequest(String issuer, DateTime issueInstant) { |
| AuthnRequest request = authnRequestBuilder.buildObject(); |
| initializeRequest(request, issuer, issueInstant); |
| return request; |
| } |
| |
| /** |
| * Static factory for SAML {@link AuthnStatement} objects. |
| * |
| * @param issueInstant The time of issue for this statement. |
| * @param uri A URI identifying an authentication context class. |
| * @return A new <code>AuthnStatement</code> object. |
| */ |
| public static AuthnStatement makeAuthnStatement(DateTime issueInstant, String uri) { |
| AuthnStatement statement = authnStatementBuilder.buildObject(); |
| statement.setAuthnInstant(issueInstant); |
| statement.setAuthnContext(makeAuthnContext(uri)); |
| return statement; |
| } |
| |
| /** |
| * Static factory for SAML {@link AuthzDecisionQuery} objects. |
| * |
| * @param issuer The entity issuing this query. |
| * @param issueInstant The time of issue for this query. |
| * @param subject The subject requesting access to a resource. |
| * @param resource The resource for which access is being requested. |
| * @param action The action on the resource for which access is being requested. |
| * @return A new <code>AuthzDecisionQuery</code> object. |
| */ |
| public static AuthzDecisionQuery makeAuthzDecisionQuery(String issuer, DateTime issueInstant, |
| Subject subject, String resource, Action action) { |
| AuthzDecisionQuery query = authzDecisionQueryBuilder.buildObject(); |
| initializeRequest(query, issuer, issueInstant); |
| query.setSubject(subject); |
| query.setResource(resource); |
| query.getActions().add(action); |
| return query; |
| } |
| |
| /** |
| * Static factory for SAML {@link AuthzDecisionStatement} objects. |
| * |
| * @param resource The resource referred to by this access decision. |
| * @param decision The access decision made by the authorization service. |
| * @param actions The actions authorized to perform on the stated resource. |
| * @return A new <code>AuthzDecisionStatement</code> object. |
| */ |
| public static AuthzDecisionStatement makeAuthzDecisionStatement(String resource, |
| DecisionTypeEnumeration decision, Action... actions) { |
| AuthzDecisionStatement statement = authzDecisionStatementBuilder.buildObject(); |
| statement.setResource(resource); |
| statement.setDecision(decision); |
| statement.getActions().addAll(Arrays.asList(actions)); |
| return statement; |
| } |
| |
| /** |
| * Static factory for SAML {@link Conditions} objects. |
| * |
| * @param notBefore Earliest time at which assertion is valid. |
| * @param notOnOrAfter Latest time at which assertion is valid. |
| * @param restriction The audience restriction that must be satisfied. |
| * @return A new <code>Conditions</code> object. |
| */ |
| public static Conditions makeConditions(DateTime notBefore, DateTime notOnOrAfter, |
| AudienceRestriction restriction) { |
| Conditions conditions = conditionsBuilder.buildObject(); |
| conditions.setNotBefore(notBefore); |
| conditions.setNotOnOrAfter(notOnOrAfter); |
| conditions.getAudienceRestrictions().add(restriction); |
| return conditions; |
| } |
| |
| /** |
| * Static factory for SAML {@link Issuer} objects. |
| * |
| * @param name The issuer of a response object. In the absence of a specific format, this is a |
| * URI identifying the issuer. |
| * @return A new <code>Issuer</code> object. |
| */ |
| private static Issuer makeIssuer(String name) { |
| Issuer issuer = issuerBuilder.buildObject(); |
| issuer.setValue(name); |
| return issuer; |
| } |
| |
| /** |
| * Static factory for SAML {@link NameID} objects. |
| * |
| * @param name The name represented by this object. |
| * @return A new <code>NameID</code> object. |
| */ |
| private static NameID makeNameId(String name) { |
| NameID id = nameIDBuilder.buildObject(); |
| id.setValue(name); |
| return id; |
| } |
| |
| /** |
| * Static factory for SAML {@link Response} objects. |
| * |
| * @param issuer The entity issuing this response. |
| * @param issueInstant The time of issue for this statement. |
| * @param status The <code>Status</code> object indicating the success of requested action. |
| * @param request The request that this is a response to. |
| * @param assertions The assertions carried by this response. |
| * @return A new <code>Response</code> object. |
| */ |
| public static Response makeResponse(String issuer, DateTime issueInstant, |
| Status status, RequestAbstractType request, Assertion... assertions) { |
| return makeResponse(issuer, issueInstant, status, request.getID(), assertions); |
| } |
| |
| /** |
| * Static factory for SAML {@link Response} objects. |
| * |
| * @param issuer The entity issuing this response. |
| * @param issueInstant The time of issue for this statement. |
| * @param status The <code>Status</code> object indicating the success of requested action. |
| * @param inResponseTo The message ID of the request this is a response to. |
| * @param assertions The assertions carried by this response. |
| * @return A new <code>Response</code> object. |
| */ |
| public static Response makeResponse(String issuer, DateTime issueInstant, Status status, |
| String inResponseTo, Assertion... assertions) { |
| Response response = responseBuilder.buildObject(); |
| initializeResponse(response, issuer, issueInstant, status, inResponseTo); |
| response.getAssertions().addAll(Arrays.asList(assertions)); |
| return response; |
| } |
| |
| /** |
| * Static factory for SAML {@link Status} objects. |
| * |
| * @param value A URI specifying one of the standard SAML status codes. |
| * @return A new <code>Status</code> object. |
| */ |
| public static Status makeStatus(String value) { |
| Status status = statusBuilder.buildObject(); |
| status.setStatusCode(makeStatusCode(value)); |
| return status; |
| } |
| |
| /** |
| * Static factory for SAML {@link Status} objects. |
| * |
| * A convenience method that generates a status object with a message. |
| * |
| * @param value A URI specifying one of the standard SAML status codes. |
| * @param message A string describing the status. |
| * @return A new <code>Status</code> object. |
| */ |
| public static Status makeStatus(String value, String message) { |
| Status status = makeStatus(value); |
| status.setStatusMessage(makeStatusMessage(message)); |
| return status; |
| } |
| |
| /** |
| * Static factory for SAML {@link Status} objects. |
| * |
| * @param statusCode A status code indicating result status of a request. |
| * @param statusMessage An optional message providing human-readable detail of the status. |
| * @return A new {@link Status} object. |
| */ |
| public static Status makeStatus(StatusCode statusCode, StatusMessage statusMessage) { |
| Status status = statusBuilder.buildObject(); |
| status.setStatusCode(statusCode); |
| if (statusMessage != null) { |
| status.setStatusMessage(statusMessage); |
| } |
| return status; |
| } |
| |
| /** |
| * Static factory for a successful SAML status element. |
| * |
| * @return A successful {@link Status} element. |
| */ |
| public static Status makeSuccessfulStatus() { |
| return makeStatus(makeStatusCode(StatusCode.SUCCESS_URI), null); |
| } |
| |
| /** |
| * Static factory for SAML {@link StatusCode} objects. |
| * |
| * @param value A URI specifying one of the standard SAML status codes. |
| * @return A new <code>StatusCode</code> object. |
| */ |
| public static StatusCode makeStatusCode(String value) { |
| StatusCode code = statusCodeBuilder.buildObject(); |
| code.setValue(value); |
| return code; |
| } |
| |
| /** |
| * Static factory for SAML {@link StatusMessage} objects. |
| * |
| * @param value A status message string. |
| * @return A new <code>StatusMessage</code> object. |
| */ |
| public static StatusMessage makeStatusMessage(String value) { |
| StatusMessage message = statusMessageBuilder.buildObject(); |
| message.setMessage(value); |
| return message; |
| } |
| |
| /** |
| * Static factory for SAML {@link Subject} objects. |
| * |
| * @param name The name identifying the subject. |
| * @param confirmations The confirmations for this subject. |
| * @return A new <code>Subject</code> object. |
| */ |
| public static Subject makeSubject(String name, SubjectConfirmation... confirmations) { |
| Subject samlSubject = subjectBuilder.buildObject(); |
| samlSubject.setNameID(makeNameId(name)); |
| if (confirmations != null) { |
| samlSubject.getSubjectConfirmations().addAll(Arrays.asList(confirmations)); |
| } |
| return samlSubject; |
| } |
| |
| /** |
| * Static factory for SAML {@link SubjectConfirmation} objects. |
| * |
| * @param method The method used to confirm the subject. |
| * @param data The data about the confirmation. |
| * @return A new <code>SubjectConfirmation</code> object. |
| */ |
| public static SubjectConfirmation makeSubjectConfirmation(String method, |
| SubjectConfirmationData data) { |
| SubjectConfirmation confirmation = subjectConfirmationBuilder.buildObject(); |
| confirmation.setMethod(method); |
| confirmation.setSubjectConfirmationData(data); |
| return confirmation; |
| } |
| |
| /** |
| * Static factory for SAML {@link SubjectConfirmationData} objects. |
| * |
| * @param recipient The entity ID of the intended recipient. |
| * @param expirationTime The expiration time for this subject. |
| * @param inResponseTo The message ID of the AuthnRequest this is a response to. |
| * @return A new <code>SubjectConfirmationData</code> object. |
| */ |
| public static SubjectConfirmationData makeSubjectConfirmationData(String recipient, |
| DateTime expirationTime, String inResponseTo) { |
| SubjectConfirmationData data = subjectConfirmationDataBuilder.buildObject(); |
| data.setRecipient(recipient); |
| data.setNotOnOrAfter(expirationTime); |
| data.setInResponseTo(inResponseTo); |
| return data; |
| } |
| |
| /* |
| * Metadata descriptions. |
| */ |
| |
| /** |
| * Static factory for SAML {@link SingleSignOnService} objects. |
| * |
| * @param binding The SAML binding implemented by this service. |
| * @param location The URL that the service listens to. |
| * @return A new <code>SingleSignOnService</code> object. |
| */ |
| public static SingleSignOnService makeSingleSignOnService(String binding, String location) { |
| SingleSignOnService service = singleSignOnServiceBuilder.buildObject(); |
| service.setBinding(binding); |
| service.setLocation(location); |
| return service; |
| } |
| |
| /* |
| * Endpoint selection |
| */ |
| |
| public static void initializeLocalEntity( |
| SAMLMessageContext<? extends SAMLObject, ? extends SAMLObject, ? extends SAMLObject> context, |
| EntityDescriptor entity, RoleDescriptor role) { |
| initializeLocalEntity(context, entity); |
| context.setLocalEntityRole(role.getElementQName()); |
| context.setLocalEntityRoleMetadata(role); |
| } |
| |
| public static void initializeLocalEntity( |
| SAMLMessageContext<? extends SAMLObject, ? extends SAMLObject, ? extends SAMLObject> context, |
| EntityDescriptor entity) { |
| context.setLocalEntityId(entity.getEntityID()); |
| context.setLocalEntityMetadata(entity); |
| context.setOutboundMessageIssuer(entity.getEntityID()); |
| } |
| |
| public static void initializePeerEntity( |
| SAMLMessageContext<? extends SAMLObject, ? extends SAMLObject, ? extends SAMLObject> context, |
| EntityDescriptor entity, RoleDescriptor role, QName endpointType, String binding) { |
| context.setPeerEntityId(entity.getEntityID()); |
| context.setPeerEntityMetadata(entity); |
| context.setPeerEntityRole(role.getElementQName()); |
| context.setPeerEntityRoleMetadata(role); |
| { |
| BasicEndpointSelector selector = new BasicEndpointSelector(); |
| selector.setEntityMetadata(entity); |
| selector.setEndpointType(endpointType); |
| selector.setEntityRoleMetadata(role); |
| selector.getSupportedIssuerBindings().add(binding); |
| context.setPeerEntityEndpoint(selector.selectEndpoint()); |
| } |
| } |
| |
| /* |
| * Identifiers |
| */ |
| |
| /** |
| * Generate a random identifier. |
| * |
| * @return A new identifier string. |
| */ |
| public static String generateIdentifier() { |
| return idGenerator.generateIdentifier(); |
| } |
| |
| /* |
| * Context and codecs |
| */ |
| |
| /** |
| * Static factory for OpenSAML message-context objects. |
| * |
| * @param <TI> The type of the request object. |
| * @param <TO> The type of the response object. |
| * @param <TN> The type of the name identifier used for subjects. |
| * @return A new message-context object. |
| */ |
| public static <TI extends SAMLObject, TO extends SAMLObject, TN extends SAMLObject> |
| SAMLMessageContext<TI, TO, TN> makeSamlMessageContext() { |
| SAMLMessageContext<TI, TO, TN> context = new BasicSAMLMessageContext<TI, TO, TN>(); |
| context.setInboundSAMLProtocol(SAML20P_NS); // we only use SAML 2.0 |
| context.setOutboundSAMLProtocol(SAML20P_NS); |
| return context; |
| } |
| |
| /** |
| * Run a message encoder. |
| * |
| * @param encoder The message encoder to run. |
| * @param context The message context to pass to the encoder. |
| * @throws IOException if unable to encode message. |
| */ |
| public static void runEncoder(MessageEncoder encoder, MessageContext context) |
| throws IOException { |
| try { |
| encoder.encode(context); |
| } catch (MessageEncodingException e) { |
| throw new IOException(e); |
| } |
| } |
| |
| /** |
| * Run a message decoder. |
| * |
| * @param decoder The message decoder to run. |
| * @param context The message context to pass to the decoder. |
| * @throws IOException if unable to decode message. |
| */ |
| public static void runDecoder(MessageDecoder decoder, MessageContext context) |
| throws IOException { |
| try { |
| decoder.decode(context); |
| } catch (MessageDecodingException e) { |
| throw new IOException(e); |
| } catch (SecurityException e) { |
| throw new IOException(e); |
| } |
| } |
| |
| /** |
| * Get a string for the current date/time, in the correct format for SAML. |
| * |
| * @return The corresponding string. |
| */ |
| public static String samlDateString() { |
| return samlDateString(new DateTime()); |
| } |
| |
| /** |
| * Get a string for a given date/time, in the correct format for SAML. |
| * |
| * @param date The date/time to convert. |
| * @return The corresponding string. |
| */ |
| public static String samlDateString(DateTime date) { |
| synchronized (samlDateStringLock) { |
| return Configuration.getSAMLDateFormatter().print(date); |
| } |
| } |
| |
| private static final Object samlDateStringLock = new Object(); |
| |
| /** |
| * Get a SAML metadata provider that reads from a specified file. The provider adjusts |
| * its output as the file is changed. |
| * |
| * @param file The file containing the metadata. |
| * @return A metadata provider for the given file. |
| * @throws MetadataProviderException if there are problems reading the file. |
| */ |
| public static ObservableMetadataProvider getMetadataFromFile(File file) |
| throws MetadataProviderException { |
| FilesystemMetadataProvider provider = new FilesystemMetadataProvider(file); |
| provider.setParserPool(new BasicParserPool()); |
| // Causes null-pointer errors in OpenSAML code: |
| //provider.setRequireValidMetadata(true); |
| return provider; |
| } |
| |
| /** |
| * Make a static security-policy resolver that resolves to a given policy. |
| * |
| * @param policy The security policy to resolve to. |
| * @return A security-policy resolver that resolves to the given policy. |
| */ |
| public static SecurityPolicyResolver makeStaticSecurityPolicyResolver(SecurityPolicy policy) { |
| return new StaticSecurityPolicyResolver(policy); |
| } |
| |
| /** |
| * Make a static security-policy resolver that resolves to a given policy. |
| * |
| * @param rules The policy rules that comprise the returned policy. |
| * @return A security-policy resolver that resolves to the given policy. |
| */ |
| public static SecurityPolicyResolver makeStaticSecurityPolicyResolver( |
| SecurityPolicyRule... rules) { |
| return makeStaticSecurityPolicyResolver(makeBasicSecurityPolicy(rules)); |
| } |
| |
| /** |
| * Make a basic security policy, consisting of a set of rules that must all be satisfied. |
| * |
| * @param rules The rules comprising the policy. |
| * @return A new security policy. |
| */ |
| public static SecurityPolicy makeBasicSecurityPolicy(SecurityPolicyRule... rules) { |
| BasicSecurityPolicy securityPolicy = new BasicSecurityPolicy(); |
| securityPolicy.getPolicyRules().addAll(Arrays.asList(rules)); |
| return securityPolicy; |
| } |
| |
| private static KeyInfoCredentialResolver standardKeyInfoCredentialResolver = null; |
| |
| /** |
| * Make a KeyInfoCredentialResolver that knows about some basic credential types. |
| * |
| * @return A new KeyInfoCredentialResolver. |
| */ |
| public static synchronized KeyInfoCredentialResolver getStandardKeyInfoCredentialResolver() { |
| if (standardKeyInfoCredentialResolver == null) { |
| List<KeyInfoProvider> providers = Lists.newArrayList(); |
| providers.add(new DSAKeyValueProvider()); |
| providers.add(new RSAKeyValueProvider()); |
| providers.add(new InlineX509DataProvider()); |
| standardKeyInfoCredentialResolver = new BasicProviderKeyInfoCredentialResolver(providers); |
| } |
| return standardKeyInfoCredentialResolver; |
| } |
| |
| /** |
| * Return the standard credentials from a given KeyInfo object. |
| * |
| * @param keyInfo The KeyInfo object to examine. |
| * @return The standard credentials found in the object. |
| * @throws SecurityException |
| */ |
| public static Iterable<Credential> resolveStandardKeyInfoCredentials(KeyInfo keyInfo) |
| throws SecurityException { |
| return getStandardKeyInfoCredentialResolver() |
| .resolve(new CriteriaSet(new KeyInfoCriteria(keyInfo))); |
| } |
| |
| /** |
| * Does a given peer's metadata support verification of signed messages? |
| * |
| * @param context The SAML message context containing the peer's metadata. |
| * @return True if the role supports verification of signed messages. |
| */ |
| public static boolean peerSupportsSignatureVerification( |
| SAMLMessageContext<? extends SAMLObject, ? extends SAMLObject, ? extends SAMLObject> |
| context) { |
| for (KeyDescriptor keyDescriptor : context.getPeerEntityRoleMetadata().getKeyDescriptors()) { |
| if (keyDescriptor.getUse() == UsageType.SIGNING) { |
| try { |
| if (resolveStandardKeyInfoCredentials(keyDescriptor.getKeyInfo()).iterator().hasNext()) { |
| return true; |
| } |
| } catch (SecurityException e) { |
| throw new IllegalStateException("Got SecurityException while extracting credentials:", e); |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Read a PEM-encoded private-key file and return it as a {@link PrivateKey} object. |
| * |
| * @param file The file to read. |
| * @return The private-key object, never null. |
| * @throws IOException if there's some kind of error reading or converting the file. |
| */ |
| public static PrivateKey readPrivateKeyFile(File file) |
| throws IOException { |
| try { |
| return SecurityHelper.decodePrivateKey(file, new char[0]); |
| } catch (KeyException e) { |
| throw new IOException(e); |
| } |
| } |
| |
| /** |
| * Convert an OpenSAML object to a DOM object. |
| * |
| * @param xmlObject The OpenSAML object to convert. |
| * @return The corresponding DOM object. |
| * @throws MarshallingException if unable to convert object. |
| */ |
| public static Element marshallXmlObject(XMLObject xmlObject) throws MarshallingException { |
| return Configuration.getMarshallerFactory().getMarshaller(xmlObject).marshall(xmlObject); |
| } |
| |
| /** |
| * Convert a DOM object to an OpenSAML object. |
| * |
| * @param element A DOM object representing a SAML element. |
| * @return The corresponding OpenSAML object. |
| * @throws UnmarshallingException if unable to convert object. |
| */ |
| public static XMLObject unmarshallXmlObject(Element element) throws UnmarshallingException { |
| return Configuration.getUnmarshallerFactory().getUnmarshaller(element).unmarshall(element); |
| } |
| |
| /** |
| * Make a new artifact map. |
| * |
| * @param artifactLifetime The lifetime of the map's artifacts, in milliseconds. |
| * @return A new artifact map. |
| */ |
| public static SAMLArtifactMap makeArtifactMap(int artifactLifetime) { |
| return new BasicSAMLArtifactMap( |
| new BasicParserPool(), |
| new MapBasedStorageService<String, SAMLArtifactMapEntry>(), |
| artifactLifetime); |
| } |
| } |