View Javadoc

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