View Javadoc

    1   /*
    2    * Copyright 2006 University Corporation for Advanced Internet Development, Inc.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    * http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16
    17  package edu.internet2.middleware.shibboleth.idp.authn;
    18
    19  import java.io.Serializable;
    20  import java.io.StringWriter;
    21  import java.util.List;
    22
    23  import org.opensaml.Configuration;
    24  import org.opensaml.saml2.core.AuthnContext;
    25  import org.opensaml.saml2.core.AuthnContextClassRef;
    26  import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
    27  import org.opensaml.saml2.core.AuthnContextDeclRef;
    28  import org.opensaml.saml2.core.AuthnRequest;
    29  import org.opensaml.saml2.core.RequestedAuthnContext;
    30  import org.opensaml.xml.io.Marshaller;
    31  import org.opensaml.xml.io.MarshallingException;
    32  import org.opensaml.xml.io.UnmarshallingException;
    33  import org.opensaml.xml.util.DatatypeHelper;
    34  import org.opensaml.xml.util.LazyList;
    35  import org.opensaml.xml.util.XMLHelper;
    36  import org.slf4j.Logger;
    37  import org.slf4j.LoggerFactory;
    38  import org.w3c.dom.Element;
    39
    40  /**
    41   * A SAML 2.0 {@link LoginContext}.
    42   *
    43   * This class can interpret {@link RequestedAuthnContext} and act accordingly.
    44   */
    45  public class Saml2LoginContext extends LoginContext implements Serializable {
    46
    47      /** Serial version UID. */
    48      private static final long serialVersionUID = -7117092606828289070L;
    49
    50      /** Relay state from authentication request. */
    51      private String relayState;
    52
    53      /** Serialized authentication request. */
    54      private String serialAuthnRequest;
    55
    56      /** Unsolicited SSO indicator.  */
    57      private boolean unsolicited;
    58
    59      /**
    60       * Creates a new instance of Saml2LoginContext.
    61       *
    62       * @param relyingParty entity ID of the relying party
    63       * @param state relay state from incoming authentication request
    64       * @param request SAML 2.0 Authentication Request
    65       *
    66       * @throws MarshallingException thrown if the given request can not be marshalled and serialized into a string
    67       */
    68      public Saml2LoginContext(String relyingParty, String state, AuthnRequest request) throws MarshallingException {
    69          super();
    70
    71          if (relyingParty == null || request == null) {
    72              throw new IllegalArgumentException("SAML 2 authentication request and relying party ID may not be null");
    73          }
    74          setRelyingParty(relyingParty);
    75          relayState = state;
    76          serialAuthnRequest = serializeRequest(request);
    77
    78          setForceAuthRequired(request.isForceAuthn());
    79          setPassiveAuthRequired(request.isPassive());
    80          getRequestedAuthenticationMethods().addAll(extractRequestedAuthenticationMethods(request));
    81      }
    82
    83      /**
    84       * Gets the serialized authentication request that started the login process.
    85       *
    86       * @return authentication request that started the login process
    87       *
    88       * @throws UnmarshallingException thrown if the serialized form on the authentication request can be unmarshalled
    89       */
    90      public synchronized String getAuthenticationRequest() throws UnmarshallingException {
    91          return serialAuthnRequest;
    92      }
    93
    94      /**
    95       * Gets the relay state from the originating authentication request.
    96       *
    97       * @return relay state from the originating authentication request
    98       */
    99      public synchronized String getRelayState(){
    100         return relayState;
    101     }
    102
    103     /**
    104      * Returns the unsolicited SSO indicator.
    105      *
    106      * @return the unsolicited SSO indicator
    107      */
    108     public boolean isUnsolicited() {
    109         return unsolicited;
    110     }
    111
    112     /**
    113      * Sets the unsolicited SSO indicator.
    114      *
    115      * @param unsolicited unsolicited SSO indicator to set
    116      */
    117     public void setUnsolicited(boolean unsolicited) {
    118         this.unsolicited = unsolicited;
    119     }
    120
    121     /**
    122      * Serializes an authentication request into a string.
    123      *
    124      * @param request the request to serialize
    125      *
    126      * @return the serialized form of the string
    127      *
    128      * @throws MarshallingException thrown if the request can not be marshalled and serialized
    129      */
    130     protected String serializeRequest(AuthnRequest request) throws MarshallingException {
    131         Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(request);
    132         Element requestElem = marshaller.marshall(request);
    133         StringWriter writer = new StringWriter();
    134         XMLHelper.writeNode(requestElem, writer);
    135         return writer.toString();
    136     }
    137
    138
    139     /**
    140      * Extracts the authentication methods requested within the request.
    141      *
    142      * @param request the authentication request
    143      *
    144      * @return requested authentication methods, or an empty list if no preference
    145      */
    146     protected List<String> extractRequestedAuthenticationMethods(AuthnRequest request){
    147         LazyList<String> requestedMethods = new LazyList<String>();
    148
    149         RequestedAuthnContext authnContext = request.getRequestedAuthnContext();
    150         if (authnContext == null) {
    151             return requestedMethods;
    152         }
    153
    154         // For the immediate future, we only support the "exact" comparator.
    155         AuthnContextComparisonTypeEnumeration comparator = authnContext.getComparison();
    156         if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
    157             Logger log = LoggerFactory.getLogger(Saml2LoginContext.class);
    158             log.warn("Unsupported comparision operator ( " + comparator
    159                     + ") in RequestedAuthnContext. Only exact comparisions are supported.");
    160             return requestedMethods;
    161         }
    162
    163         // build a list of all requested authn classes and declrefs
    164         List<AuthnContextClassRef> authnClasses = authnContext.getAuthnContextClassRefs();
    165         if (authnClasses != null) {
    166             for (AuthnContextClassRef classRef : authnClasses) {
    167                 if (classRef != null && !DatatypeHelper.isEmpty(classRef.getAuthnContextClassRef())) {
    168                     requestedMethods.add(classRef.getAuthnContextClassRef());
    169                 }
    170             }
    171         }
    172
    173         List<AuthnContextDeclRef> authnDeclRefs = authnContext.getAuthnContextDeclRefs();
    174         if (authnDeclRefs != null) {
    175             for (AuthnContextDeclRef declRef : authnDeclRefs) {
    176                 if (declRef != null&& !DatatypeHelper.isEmpty(declRef.getAuthnContextDeclRef())) {
    177                     requestedMethods.add(declRef.getAuthnContextDeclRef());
    178                 }
    179             }
    180         }
    181
    182         if(requestedMethods.contains(AuthnContext.UNSPECIFIED_AUTHN_CTX)){
    183             requestedMethods.clear();
    184         }
    185
    186         return requestedMethods;
    187     }
    188 }