/** * Initializes SSO by creating AuthnRequest assertion and sending it to the IDP using the default * binding. Default IDP is used to send the request. * * @param options values specified by caller to customize format of sent request * @throws SAMLException error initializing SSO * @throws SAMLRuntimeException in case context doesn't contain required entities or contains * invalid data * @throws MetadataProviderException error retrieving needed metadata * @throws MessageEncodingException error forming SAML message */ public void sendAuthenticationRequest(SAMLMessageContext context, WebSSOProfileOptions options) throws SAMLException, MetadataProviderException, MessageEncodingException { // Verify we deal with a local SP if (!SPSSODescriptor.DEFAULT_ELEMENT_NAME.equals(context.getLocalEntityRole())) { throw new SAMLException( "WebSSO can only be initialized for local SP, but localEntityRole is: " + context.getLocalEntityRole()); } // Load the entities from the context SPSSODescriptor spDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata(); IDPSSODescriptor idpssoDescriptor = (IDPSSODescriptor) context.getPeerEntityRoleMetadata(); ExtendedMetadata idpExtendedMetadata = context.getPeerExtendedMetadata(); if (spDescriptor == null || idpssoDescriptor == null || idpExtendedMetadata == null) { throw new SAMLException( "SPSSODescriptor, IDPSSODescriptor or IDPExtendedMetadata are not present in the SAMLContext"); } log.debug( "idpExtendedMetadata.getSigningAlgorithm=" + idpExtendedMetadata.getSigningAlgorithm()); idpExtendedMetadata.setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); SingleSignOnService ssoService = getSingleSignOnService(options, idpssoDescriptor, spDescriptor); AssertionConsumerService consumerService = getAssertionConsumerService(options, idpssoDescriptor, spDescriptor); AuthnRequest authRequest = getAuthnRequest(context, options, consumerService, ssoService); if (authRequest == null) { throw new SAMLException("Erreur dans getAuthnRequest null"); } // authRequest.setForceAuthn(Boolean.TRUE); log.debug("getAuthnRequest.providerName=" + authRequest.getProviderName()); // TODO optionally implement support for conditions, subject context.setCommunicationProfileId(getProfileIdentifier()); context.setOutboundMessage(authRequest); context.setOutboundSAMLMessage(authRequest); context.setPeerEntityEndpoint(ssoService); context.setPeerEntityRoleMetadata(idpssoDescriptor); context.setPeerExtendedMetadata(idpExtendedMetadata); if (options.getRelayState() != null) { context.setRelayState(options.getRelayState()); } boolean sign = spDescriptor.isAuthnRequestsSigned() || idpssoDescriptor.getWantAuthnRequestsSigned(); log.debug("signature?" + sign + " avec http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); context .getLocalExtendedMetadata() .setSigningAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"); sendMessage(context, sign); SAMLMessageStorage messageStorage = context.getMessageStorage(); if (messageStorage != null) { messageStorage.storeMessage(authRequest.getID(), authRequest); } }
/** * Determines endpoint where should the identity provider return the SAML message. Endpoint also * implies the used binding. In case assertionConsumerIndex in the WebSSOProfileOptions is * specified the endpoint with the given ID is used. Otherwise assertionConsumerService marked as * default is used when present, otherwise first found supported assertionConsumerService is used. * * <p>In case endpoint determined by the webSSOProfileOptions index is not supported by the * profile an exception is raised. * * @param options user supplied preferences * @param idpSSODescriptor idp, can be null when no IDP is known in advance * @param spDescriptor sp * @return consumer service or null * @throws MetadataProviderException in case index supplied in options is invalid or unsupported * or no supported consumer service can be found */ protected AssertionConsumerService getAssertionConsumerService( WebSSOProfileOptions options, IDPSSODescriptor idpSSODescriptor, SPSSODescriptor spDescriptor) throws MetadataProviderException { List<AssertionConsumerService> services = spDescriptor.getAssertionConsumerServices(); // Use user preference if (options.getAssertionConsumerIndex() != null) { for (AssertionConsumerService service : services) { if (options.getAssertionConsumerIndex().equals(service.getIndex())) { if (!isEndpointSupported(service)) { throw new MetadataProviderException( "Endpoint designated by the value in the WebSSOProfileOptions is not supported by this profile"); } else { log.debug( "Using consumer service determined by user preference with binding {}", service.getBinding()); return service; } } } throw new MetadataProviderException( "AssertionConsumerIndex " + options.getAssertionConsumerIndex() + " not found for spDescriptor " + spDescriptor); } // Use default if (spDescriptor.getDefaultAssertionConsumerService() != null && isEndpointSupported(spDescriptor.getDefaultAssertionConsumerService())) { AssertionConsumerService service = spDescriptor.getDefaultAssertionConsumerService(); log.debug("Using default consumer service with binding {}", service.getBinding()); log.debug("Default ACS url=" + service.getLocation()); return service; } // Iterate and find first match if (services.size() > 0) { for (AssertionConsumerService service : services) { if (isEndpointSupported(service)) { log.debug("Using first available consumer service with binding {}", service.getBinding()); return service; } } } throw new MetadataProviderException( "Service provider has no assertion consumer service available for the selected profile " + spDescriptor); }
private void init(InputStream inputStream) throws SAMLException { BasicParserPool parsers = new BasicParserPool(); parsers.setNamespaceAware(true); EntityDescriptor edesc; try { Document doc = parsers.parse(inputStream); Element root = doc.getDocumentElement(); UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory(); edesc = (EntityDescriptor) unmarshallerFactory.getUnmarshaller(root).unmarshall(root); } catch (org.opensaml.xml.parse.XMLParserException e) { throw new SAMLException(e); } catch (org.opensaml.xml.io.UnmarshallingException e) { throw new SAMLException(e); } // fetch sp information SPSSODescriptor spDesc = edesc.getSPSSODescriptor("urn:oasis:names:tc:SAML:2.0:protocol"); if (spDesc == null) throw new SAMLException("No SP SSO descriptor found"); // get first redirect or post binding String acsUrl = null; for (AssertionConsumerService svc : spDesc.getAssertionConsumerServices()) { if (svc.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI) || svc.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) { acsUrl = svc.getLocation(); break; } } if (acsUrl == null) throw new SAMLException("No acceptable Assertion Consumer Service found"); this.setEntityId(edesc.getEntityID()); this.setAcs(acsUrl); }
public void processLogoutResponse(SAMLMessageContext context) throws SAMLException, org.opensaml.xml.security.SecurityException, ValidationException { SAMLObject message = context.getInboundSAMLMessage(); // Verify type if (!(message instanceof LogoutResponse)) { log.debug("Received response is not of a Response object type"); throw new SAMLException("Error validating SAML response"); } LogoutResponse response = (LogoutResponse) message; // Make sure request was authenticated if required, authentication is done as part of the // binding processing if (!context.isInboundSAMLMessageAuthenticated() && context.getLocalExtendedMetadata().isRequireLogoutResponseSigned()) { log.debug( "Logout Response object is required to be signed by the entity policy: " + context.getInboundSAMLMessageId()); throw new SAMLException("Logout Response object is required to be signed"); } // Verify issue time DateTime time = response.getIssueInstant(); if (!isDateTimeSkewValid(getResponseSkew(), time)) { log.debug("Response issue time is either too old or with date in the future"); throw new SAMLException("Error validating SAML response"); } // Verify response to field if present, set request if correct // The inResponseTo field is optional, SAML 2.0 Core, 1542 SAMLMessageStorage messageStorage = context.getMessageStorage(); if (messageStorage != null && response.getInResponseTo() != null) { XMLObject xmlObject = messageStorage.retrieveMessage(response.getInResponseTo()); if (xmlObject == null) { log.debug( "InResponseToField doesn't correspond to sent message", response.getInResponseTo()); throw new SAMLException("Error validating SAML response"); } else if (xmlObject instanceof LogoutRequest) { // Expected } else { log.debug( "Sent request was of different type then received response", response.getInResponseTo()); throw new SAMLException("Error validating SAML response"); } } // Verify destination if (response.getDestination() != null) { SPSSODescriptor localDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata(); // Check if destination is correct on this SP List<SingleLogoutService> services = localDescriptor.getSingleLogoutServices(); boolean found = false; for (SingleLogoutService service : services) { if (response.getDestination().equals(service.getLocation()) && context.getInboundSAMLBinding().equals(service.getBinding())) { found = true; break; } } if (!found) { log.debug( "Destination of the response was not the expected value", response.getDestination()); throw new SAMLException("Error validating SAML response"); } } // Verify issuer if (response.getIssuer() != null) { Issuer issuer = response.getIssuer(); verifyIssuer(issuer, context); } // Verify status String statusCode = response.getStatus().getStatusCode().getValue(); if (StatusCode.SUCCESS_URI.equals(statusCode)) { log.trace("Single Logout was successful"); } else if (StatusCode.PARTIAL_LOGOUT_URI.equals(statusCode)) { log.trace("Single Logout was partially successful"); } else { String[] logMessage = new String[2]; logMessage[0] = response.getStatus().getStatusCode().getValue(); StatusMessage message1 = response.getStatus().getStatusMessage(); if (message1 != null) { logMessage[1] = message1.getMessage(); } log.warn("Received LogoutResponse has invalid status code", logMessage); } }