View Javadoc

    1   /*
    2    * Copyright [2007] [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.profile;
    18
    19  import java.util.ArrayList;
    20  import java.util.List;
    21  import java.util.Map;
    22
    23  import javax.servlet.http.HttpServletRequest;
    24
    25  import org.opensaml.common.IdentifierGenerator;
    26  import org.opensaml.common.binding.SAMLMessageContext;
    27  import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
    28  import org.opensaml.common.binding.encoding.SAMLMessageEncoder;
    29  import org.opensaml.saml1.core.NameIdentifier;
    30  import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
    31  import org.opensaml.saml2.metadata.AuthnAuthorityDescriptor;
    32  import org.opensaml.saml2.metadata.Endpoint;
    33  import org.opensaml.saml2.metadata.EntityDescriptor;
    34  import org.opensaml.saml2.metadata.NameIDFormat;
    35  import org.opensaml.saml2.metadata.PDPDescriptor;
    36  import org.opensaml.saml2.metadata.RoleDescriptor;
    37  import org.opensaml.saml2.metadata.SSODescriptor;
    38  import org.opensaml.saml2.metadata.provider.MetadataProvider;
    39  import org.opensaml.saml2.metadata.provider.MetadataProviderException;
    40  import org.opensaml.ws.message.encoder.MessageEncodingException;
    41  import org.opensaml.ws.security.SecurityPolicyResolver;
    42  import org.opensaml.ws.transport.InTransport;
    43  import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
    44  import org.opensaml.xml.security.credential.Credential;
    45  import org.slf4j.Logger;
    46  import org.slf4j.LoggerFactory;
    47
    48  import edu.internet2.middleware.shibboleth.common.log.AuditLogEntry;
    49  import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
    50  import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractShibbolethProfileHandler;
    51  import edu.internet2.middleware.shibboleth.common.profile.provider.BaseSAMLProfileRequestContext;
    52  import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
    53  import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartySecurityPolicyResolver;
    54  import edu.internet2.middleware.shibboleth.common.relyingparty.provider.AbstractSAMLProfileConfiguration;
    55  import edu.internet2.middleware.shibboleth.common.relyingparty.provider.CryptoOperationRequirementLevel;
    56  import edu.internet2.middleware.shibboleth.common.relyingparty.provider.SAMLMDRelyingPartyConfigurationManager;
    57  import edu.internet2.middleware.shibboleth.idp.session.Session;
    58
    59  /**
    60   * Base class for SAML profile handlers.
    61   */
    62  public abstract class AbstractSAMLProfileHandler extends
    63          AbstractShibbolethProfileHandler<SAMLMDRelyingPartyConfigurationManager, Session> {
    64
    65      /** SAML message audit log. */
    66      private final Logger auditLog = LoggerFactory.getLogger(AuditLogEntry.AUDIT_LOGGER_NAME);
    67
    68      /** Class logger. */
    69      private final Logger log = LoggerFactory.getLogger(AbstractSAMLProfileHandler.class);
    70
    71      /** Generator of IDs which may be used for SAML assertions, requests, etc. */
    72      private IdentifierGenerator idGenerator;
    73
    74      /** All the SAML message decoders configured for the IdP. */
    75      private Map<String, SAMLMessageDecoder> messageDecoders;
    76
    77      /** All the SAML message encoders configured for the IdP. */
    78      private Map<String, SAMLMessageEncoder> messageEncoders;
    79
    80      /** SAML message binding used by inbound messages. */
    81      private String inboundBinding;
    82
    83      /** SAML message bindings that may be used by outbound messages. */
    84      private List<String> supportedOutboundBindings;
    85
    86      /** Resolver used to determine active security policy for an incoming request. */
    87      private SecurityPolicyResolver securityPolicyResolver;
    88
    89      /** Constructor. */
    90      protected AbstractSAMLProfileHandler() {
    91          super();
    92      }
    93
    94      /**
    95       * Gets the resolver used to determine active security policy for an incoming request.
    96       *
    97       * @return resolver used to determine active security policy for an incoming request
    98       */
    99      public SecurityPolicyResolver getSecurityPolicyResolver() {
    100         if (securityPolicyResolver == null) {
    101             setSecurityPolicyResolver(new RelyingPartySecurityPolicyResolver(getRelyingPartyConfigurationManager()));
    102         }
    103
    104         return securityPolicyResolver;
    105     }
    106
    107     /**
    108      * Sets the resolver used to determine active security policy for an incoming request.
    109      *
    110      * @param resolver resolver used to determine active security policy for an incoming request
    111      */
    112     public void setSecurityPolicyResolver(SecurityPolicyResolver resolver) {
    113         securityPolicyResolver = resolver;
    114     }
    115
    116     /**
    117      * Gets the audit log for this handler.
    118      *
    119      * @return audit log for this handler
    120      */
    121     protected Logger getAduitLog() {
    122         return auditLog;
    123     }
    124
    125     /**
    126      * Gets an ID generator which may be used for SAML assertions, requests, etc.
    127      *
    128      * @return ID generator
    129      */
    130     public IdentifierGenerator getIdGenerator() {
    131         return idGenerator;
    132     }
    133
    134     /**
    135      * Gets the SAML message binding used by inbound messages.
    136      *
    137      * @return SAML message binding used by inbound messages
    138      */
    139     public String getInboundBinding() {
    140         return inboundBinding;
    141     }
    142
    143     /**
    144      * Gets all the SAML message decoders configured for the IdP indexed by SAML binding URI.
    145      *
    146      * @return SAML message decoders configured for the IdP indexed by SAML binding URI
    147      */
    148     public Map<String, SAMLMessageDecoder> getMessageDecoders() {
    149         return messageDecoders;
    150     }
    151
    152     /**
    153      * Gets all the SAML message encoders configured for the IdP indexed by SAML binding URI.
    154      *
    155      * @return SAML message encoders configured for the IdP indexed by SAML binding URI
    156      */
    157     public Map<String, SAMLMessageEncoder> getMessageEncoders() {
    158         return messageEncoders;
    159     }
    160
    161     /**
    162      * A convenience method for retrieving the SAML metadata provider from the relying party manager.
    163      *
    164      * @return the metadata provider or null
    165      */
    166     public MetadataProvider getMetadataProvider() {
    167         SAMLMDRelyingPartyConfigurationManager rpcManager = getRelyingPartyConfigurationManager();
    168         if (rpcManager != null) {
    169             return rpcManager.getMetadataProvider();
    170         }
    171
    172         return null;
    173     }
    174
    175     /**
    176      * Gets the SAML message bindings that may be used by outbound messages.
    177      *
    178      * @return SAML message bindings that may be used by outbound messages
    179      */
    180     public List<String> getSupportedOutboundBindings() {
    181         return supportedOutboundBindings;
    182     }
    183
    184     /**
    185      * Gets the user's session, if there is one.
    186      *
    187      * @param inTransport current inbound transport
    188      *
    189      * @return user's session
    190      */
    191     protected Session getUserSession(InTransport inTransport) {
    192         HttpServletRequest rawRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
    193         return (Session) rawRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
    194     }
    195
    196     /**
    197      * Gets the user's session based on their principal name.
    198      *
    199      * @param principalName user's principal name
    200      *
    201      * @return the user's session
    202      */
    203     protected Session getUserSession(String principalName) {
    204         return getSessionManager().getSession(principalName);
    205     }
    206
    207     /**
    208      * Gets an ID generator which may be used for SAML assertions, requests, etc.
    209      *
    210      * @param generator an ID generator which may be used for SAML assertions, requests, etc
    211      */
    212     public void setIdGenerator(IdentifierGenerator generator) {
    213         idGenerator = generator;
    214     }
    215
    216     /**
    217      * Sets the SAML message binding used by inbound messages.
    218      *
    219      * @param binding SAML message binding used by inbound messages
    220      */
    221     public void setInboundBinding(String binding) {
    222         inboundBinding = binding;
    223     }
    224
    225     /**
    226      * Sets all the SAML message decoders configured for the IdP indexed by SAML binding URI.
    227      *
    228      * @param decoders SAML message decoders configured for the IdP indexed by SAML binding URI
    229      */
    230     public void setMessageDecoders(Map<String, SAMLMessageDecoder> decoders) {
    231         messageDecoders = decoders;
    232     }
    233
    234     /**
    235      * Sets all the SAML message encoders configured for the IdP indexed by SAML binding URI.
    236      *
    237      * @param encoders SAML message encoders configured for the IdP indexed by SAML binding URI
    238      */
    239     public void setMessageEncoders(Map<String, SAMLMessageEncoder> encoders) {
    240         messageEncoders = encoders;
    241     }
    242
    243     /**
    244      * Sets the SAML message bindings that may be used by outbound messages.
    245      *
    246      * @param bindings SAML message bindings that may be used by outbound messages
    247      */
    248     public void setSupportedOutboundBindings(List<String> bindings) {
    249         supportedOutboundBindings = bindings;
    250     }
    251
    252     /** {@inheritDoc} */
    253     public RelyingPartyConfiguration getRelyingPartyConfiguration(String relyingPartyId) {
    254         try {
    255             if (getMetadataProvider().getEntityDescriptor(relyingPartyId) == null) {
    256                 log.warn("No metadata for relying party {}, treating party as anonymous", relyingPartyId);
    257                 return getRelyingPartyConfigurationManager().getAnonymousRelyingConfiguration();
    258             }
    259         } catch (MetadataProviderException e) {
    260             log.error("Unable to look up relying party metadata", e);
    261             return null;
    262         }
    263
    264         return super.getRelyingPartyConfiguration(relyingPartyId);
    265     }
    266
    267     /**
    268      * Populates the request context with information.
    269      *
    270      * This method requires the the following request context properties to be populated: inbound message transport,
    271      * peer entity ID, metadata provider
    272      *
    273      * This methods populates the following request context properties: user's session, user's principal name, service
    274      * authentication method, peer entity metadata, relying party configuration, local entity ID, outbound message
    275      * issuer, local entity metadata
    276      *
    277      * @param requestContext current request context
    278      * @throws ProfileException thrown if there is a problem looking up the relying party's metadata
    279      */
    280     protected void populateRequestContext(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
    281         populateRelyingPartyInformation(requestContext);
    282         populateAssertingPartyInformation(requestContext);
    283         populateSAMLMessageInformation(requestContext);
    284         populateProfileInformation(requestContext);
    285         populateUserInformation(requestContext);
    286     }
    287
    288     /**
    289      * Populates the request context with information about the relying party.
    290      *
    291      * This method requires the the following request context properties to be populated: peer entity ID
    292      *
    293      * This methods populates the following request context properties: peer entity metadata, relying party
    294      * configuration
    295      *
    296      * @param requestContext current request context
    297      * @throws ProfileException thrown if there is a problem looking up the relying party's metadata
    298      */
    299     protected void populateRelyingPartyInformation(BaseSAMLProfileRequestContext requestContext)
    300             throws ProfileException {
    301         MetadataProvider metadataProvider = requestContext.getMetadataProvider();
    302         String relyingPartyId = requestContext.getInboundMessageIssuer();
    303
    304         EntityDescriptor relyingPartyMetadata;
    305         try {
    306             relyingPartyMetadata = metadataProvider.getEntityDescriptor(relyingPartyId);
    307             requestContext.setPeerEntityMetadata(relyingPartyMetadata);
    308         } catch (MetadataProviderException e) {
    309             log.error("Error looking up metadata for relying party " + relyingPartyId, e);
    310             throw new ProfileException("Error looking up metadata for relying party " + relyingPartyId);
    311         }
    312
    313         RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
    314         if (rpConfig == null) {
    315             log.error("Unable to retrieve relying party configuration data for entity with ID {}", relyingPartyId);
    316             throw new ProfileException("Unable to retrieve relying party configuration data for entity with ID "
    317                     + relyingPartyId);
    318         }
    319         requestContext.setRelyingPartyConfiguration(rpConfig);
    320     }
    321
    322     /**
    323      * Populates the request context with information about the asserting party. Unless overridden,
    324      * {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
    325      * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)} has already been invoked and the
    326      * properties it provides are available in the request context.
    327      *
    328      * This method requires the the following request context properties to be populated: metadata provider, relying
    329      * party configuration
    330      *
    331      * This methods populates the following request context properties: local entity ID, outbound message issuer, local
    332      * entity metadata
    333      *
    334      * @param requestContext current request context
    335      * @throws ProfileException thrown if there is a problem looking up the asserting party's metadata
    336      */
    337     protected void populateAssertingPartyInformation(BaseSAMLProfileRequestContext requestContext)
    338             throws ProfileException {
    339         String assertingPartyId = requestContext.getRelyingPartyConfiguration().getProviderId();
    340         requestContext.setLocalEntityId(assertingPartyId);
    341         requestContext.setOutboundMessageIssuer(assertingPartyId);
    342
    343         try {
    344             EntityDescriptor localEntityDescriptor = requestContext.getMetadataProvider().getEntityDescriptor(
    345                     assertingPartyId);
    346             if (localEntityDescriptor != null) {
    347                 requestContext.setLocalEntityMetadata(localEntityDescriptor);
    348             }
    349         } catch (MetadataProviderException e) {
    350             log.error("Error looking up metadata for asserting party " + assertingPartyId, e);
    351             throw new ProfileException("Error looking up metadata for asserting party " + assertingPartyId);
    352         }
    353     }
    354
    355     /**
    356      * Populates the request context with information from the inbound SAML message. Unless overridden,
    357      * {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
    358      * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)},and
    359      * {@link #populateAssertingPartyInformation(BaseSAMLProfileRequestContext)} have already been invoked and the
    360      * properties they provide are available in the request context.
    361      *
    362      *
    363      * @param requestContext current request context
    364      *
    365      * @throws ProfileException thrown if there is a problem populating the request context with information
    366      */
    367     protected abstract void populateSAMLMessageInformation(BaseSAMLProfileRequestContext requestContext)
    368             throws ProfileException;
    369
    370     /**
    371      * Populates the request context with the information about the profile. Unless overridden,
    372      * {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
    373      * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)},
    374      * {@link #populateAssertingPartyInformation(BaseSAMLProfileRequestContext)}, and
    375      * {@link #populateSAMLMessageInformation(BaseSAMLProfileRequestContext)} have already been invoked and the
    376      * properties they provide are available in the request context.
    377      *
    378      * This method requires the the following request context properties to be populated: relying party configuration
    379      *
    380      * This methods populates the following request context properties: communication profile ID, profile configuration,
    381      * outbound message artifact type, peer entity endpoint
    382      *
    383      * @param requestContext current request context
    384      *
    385      * @throws ProfileException thrown if there is a problem populating the profile information
    386      */
    387     protected void populateProfileInformation(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
    388         AbstractSAMLProfileConfiguration profileConfig = (AbstractSAMLProfileConfiguration) requestContext
    389                 .getRelyingPartyConfiguration().getProfileConfiguration(getProfileId());
    390         if (profileConfig != null) {
    391             requestContext.setProfileConfiguration(profileConfig);
    392             requestContext.setOutboundMessageArtifactType(profileConfig.getOutboundArtifactType());
    393         }
    394
    395         Endpoint endpoint = selectEndpoint(requestContext);
    396         if (endpoint == null) {
    397             log.error("No return endpoint available for relying party {}", requestContext.getInboundMessageIssuer());
    398             throw new ProfileException("No peer endpoint available to which to send SAML response");
    399         }
    400         requestContext.setPeerEntityEndpoint(endpoint);
    401     }
    402
    403     /**
    404      * Gets the name identifier formats to use when creating identifiers for the relying party.
    405      *
    406      * @param requestContext current request context
    407      *
    408      * @return list of formats that may be used with the relying party, or an empty list for no preference
    409      *
    410      * @throws ProfileException thrown if there is a problem determining the name identifier format to use
    411      */
    412     protected List<String> getNameFormats(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
    413         ArrayList<String> nameFormats = new ArrayList<String>();
    414
    415         RoleDescriptor relyingPartyRole = requestContext.getPeerEntityRoleMetadata();
    416         if (relyingPartyRole != null) {
    417             List<String> relyingPartySupportedFormats = getEntitySupportedFormats(relyingPartyRole);
    418             if (relyingPartySupportedFormats != null && !relyingPartySupportedFormats.isEmpty()) {
    419                 nameFormats.addAll(relyingPartySupportedFormats);
    420             }
    421         }
    422
    423         // If metadata contains the unspecified name format this means that any are supported
    424         if (nameFormats.contains(NameIdentifier.UNSPECIFIED)) {
    425             nameFormats.clear();
    426         }
    427
    428         return nameFormats;
    429     }
    430
    431     /**
    432      * Gets the list of name identifier formats supported for a given role.
    433      *
    434      * @param role the role to get the list of supported name identifier formats
    435      *
    436      * @return list of supported name identifier formats
    437      */
    438     protected List<String> getEntitySupportedFormats(RoleDescriptor role) {
    439         List<NameIDFormat> nameIDFormats = null;
    440
    441         if (role instanceof SSODescriptor) {
    442             nameIDFormats = ((SSODescriptor) role).getNameIDFormats();
    443         } else if (role instanceof AuthnAuthorityDescriptor) {
    444             nameIDFormats = ((AuthnAuthorityDescriptor) role).getNameIDFormats();
    445         } else if (role instanceof PDPDescriptor) {
    446             nameIDFormats = ((PDPDescriptor) role).getNameIDFormats();
    447         } else if (role instanceof AttributeAuthorityDescriptor) {
    448             nameIDFormats = ((AttributeAuthorityDescriptor) role).getNameIDFormats();
    449         }
    450
    451         ArrayList<String> supportedFormats = new ArrayList<String>();
    452         if (nameIDFormats != null) {
    453             for (NameIDFormat format : nameIDFormats) {
    454                 supportedFormats.add(format.getFormat());
    455             }
    456         }
    457
    458         return supportedFormats;
    459     }
    460
    461     /**
    462      * Populates the request context with the information about the user if they have an existing session. Unless
    463      * overridden, {@link #populateRequestContext(BaseSAMLProfileRequestContext)} has already invoked
    464      * {@link #populateRelyingPartyInformation(BaseSAMLProfileRequestContext)},
    465      * {@link #populateAssertingPartyInformation(BaseSAMLProfileRequestContext)},
    466      * {@link #populateProfileInformation(BaseSAMLProfileRequestContext)}, and
    467      * {@link #populateSAMLMessageInformation(BaseSAMLProfileRequestContext)} have already been invoked and the
    468      * properties they provide are available in the request context.
    469      *
    470      * This method should populate: user's session, user's principal name, and service authentication method
    471      *
    472      * @param requestContext current request context
    473      *
    474      * @throws ProfileException thrown if there is a problem populating the user's information
    475      */
    476     protected abstract void populateUserInformation(BaseSAMLProfileRequestContext requestContext)
    477             throws ProfileException;
    478
    479     /**
    480      * Selects the appropriate endpoint for the relying party and stores it in the request context.
    481      *
    482      * @param requestContext current request context
    483      *
    484      * @return Endpoint selected from the information provided in the request context
    485      *
    486      * @throws ProfileException thrown if there is a problem selecting a response endpoint
    487      */
    488     protected abstract Endpoint selectEndpoint(BaseSAMLProfileRequestContext requestContext) throws ProfileException;
    489
    490     /**
    491      * Encodes the request's SAML response and writes it to the servlet response.
    492      *
    493      * @param requestContext current request context
    494      *
    495      * @throws ProfileException thrown if no message encoder is registered for this profiles binding
    496      */
    497     protected void encodeResponse(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
    498         try {
    499             SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
    500
    501             AbstractSAMLProfileConfiguration profileConfig = (AbstractSAMLProfileConfiguration) requestContext
    502                     .getProfileConfiguration();
    503             if (profileConfig != null) {
    504                 if (isSignResponse(requestContext)) {
    505                     Credential signingCredential = profileConfig.getSigningCredential();
    506                     if (signingCredential == null) {
    507                         signingCredential = requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential();
    508                     }
    509
    510                     if (signingCredential == null) {
    511                         throw new ProfileException(
    512                                 "Signing of responses is required but no signing credential is available");
    513                     }
    514
    515                     if (signingCredential.getPrivateKey() == null) {
    516                         throw new ProfileException(
    517                                 "Signing of response is required but signing credential does not have a private key");
    518                     }
    519
    520                     requestContext.setOutboundSAMLMessageSigningCredential(signingCredential);
    521                 }
    522             }
    523
    524             log.debug("Encoding response to SAML request {} from relying party {}", requestContext
    525                     .getInboundSAMLMessageId(), requestContext.getInboundMessageIssuer());
    526
    527             requestContext.setMessageEncoder(encoder);
    528             encoder.encode(requestContext);
    529         } catch (MessageEncodingException e) {
    530             throw new ProfileException("Unable to encode response to relying party: "
    531                     + requestContext.getInboundMessageIssuer(), e);
    532         }
    533     }
    534
    535     /**
    536      * Determine whether responses should be signed.
    537      *
    538      * @param requestContext the current request context
    539      * @return true if responses should be signed, false otherwise
    540      * @throws ProfileException if there is a problem determining whether responses should be signed
    541      */
    542     protected boolean isSignResponse(BaseSAMLProfileRequestContext requestContext) throws ProfileException {
    543
    544         SAMLMessageEncoder encoder = getOutboundMessageEncoder(requestContext);
    545
    546         AbstractSAMLProfileConfiguration profileConfig =
    547             (AbstractSAMLProfileConfiguration) requestContext.getProfileConfiguration();
    548
    549         if (profileConfig != null) {
    550             try {
    551                 return profileConfig.getSignResponses() == CryptoOperationRequirementLevel.always
    552                     || (profileConfig.getSignResponses() == CryptoOperationRequirementLevel.conditional
    553                         && !encoder.providesMessageIntegrity(requestContext));
    554             } catch (MessageEncodingException e) {
    555                 log.error("Unable to determine if outbound encoding '{}' provides message integrity protection",
    556                         encoder.getBindingURI());
    557                 throw new ProfileException("Unable to determine if outbound response should be signed");
    558             }
    559         } else {
    560             return false;
    561         }
    562
    563     }
    564
    565     /**
    566      * Get the outbound message encoder to use.
    567      *
    568      * <p>The default implementation uses the binding URI from the
    569      * {@link SAMLMessageContext#getPeerEntityEndpoint()} to lookup
    570      * the encoder from the supported message encoders defined in {@link #getMessageEncoders()}.
    571      * </p>
    572      *
    573      * <p>
    574      * Subclasses may override to implement a different mechanism to determine the
    575      * encoder to use, such as for example cases where an active intermediary actor
    576      * sits between this provider and the peer entity endpoint (e.g. the SAML 2 ECP case).
    577      * </p>
    578      *
    579      * @param requestContext current request context
    580      * @return the message encoder to use
    581      * @throws ProfileException if the encoder to use can not be resolved based on the request context
    582      */
    583     protected SAMLMessageEncoder getOutboundMessageEncoder(BaseSAMLProfileRequestContext requestContext)
    584             throws ProfileException {
    585         SAMLMessageEncoder encoder = null;
    586
    587         Endpoint endpoint = requestContext.getPeerEntityEndpoint();
    588         if (endpoint == null) {
    589             log.warn("No peer endpoint available for peer. Unable to send response.");
    590             throw new ProfileException("No peer endpoint available for peer. Unable to send response.");
    591         }
    592
    593         if (endpoint != null) {
    594             encoder = getMessageEncoders().get(endpoint.getBinding());
    595             if (encoder == null) {
    596                 log.error("No outbound message encoder configured for binding: {}", requestContext
    597                         .getPeerEntityEndpoint().getBinding());
    598                 throw new ProfileException("No outbound message encoder configured for binding: "
    599                         + requestContext.getPeerEntityEndpoint().getBinding());
    600             }
    601         }
    602         return encoder;
    603     }
    604
    605     /**
    606      * Get the inbound message decoder to use.
    607      *
    608      * <p>The default implementation uses the binding URI from
    609      * {@link #getInboundBinding()} to lookup the decoder from the supported message decoders
    610      * defined in {@link #getMessageDecoders()}.
    611      * </p>
    612      *
    613      * <p>
    614      * Subclasses may override to implement a different mechanism to determine the
    615      * decoder to use.
    616      * </p>
    617      *
    618      * @param requestContext current request context
    619      * @return the message decoder to use
    620      * @throws ProfileException if the decoder to use can not be resolved based on the request context
    621      */
    622     protected SAMLMessageDecoder getInboundMessageDecoder(BaseSAMLProfileRequestContext requestContext)
    623             throws ProfileException {
    624         SAMLMessageDecoder decoder = null;
    625
    626         decoder = getMessageDecoders().get(getInboundBinding());
    627         if (decoder == null) {
    628             log.error("No inbound message decoder configured for binding: {}", getInboundBinding());
    629             throw new ProfileException("No inbound message decoder configured for binding: " + getInboundBinding());
    630         }
    631         return decoder;
    632     }
    633
    634     /**
    635      * Writes an audit log entry indicating the successful response to the attribute request.
    636      *
    637      * @param context current request context
    638      */
    639     protected void writeAuditLogEntry(BaseSAMLProfileRequestContext context) {
    640         AuditLogEntry auditLogEntry = new AuditLogEntry();
    641         auditLogEntry.setMessageProfile(getProfileId());
    642         auditLogEntry.setPrincipalAuthenticationMethod(context.getPrincipalAuthenticationMethod());
    643         auditLogEntry.setPrincipalName(context.getPrincipalName());
    644         auditLogEntry.setAssertingPartyId(context.getLocalEntityId());
    645         auditLogEntry.setRelyingPartyId(context.getInboundMessageIssuer());
    646         auditLogEntry.setRequestBinding(context.getMessageDecoder().getBindingURI());
    647         auditLogEntry.setRequestId(context.getInboundSAMLMessageId());
    648         auditLogEntry.setResponseBinding(context.getMessageEncoder().getBindingURI());
    649         auditLogEntry.setResponseId(context.getOutboundSAMLMessageId());
    650         if (context.getReleasedAttributes() != null) {
    651             auditLogEntry.getReleasedAttributes().addAll(context.getReleasedAttributes());
    652         }
    653
    654         getAduitLog().info(auditLogEntry.toString());
    655     }
    656 }