/** * Fills the request with required AuthNContext according to selected options. * * @param request request to fill * @param options options driving generation of the element */ protected void buildAuthnContext(AuthnRequest request, WebSSOProfileOptions options) { Collection<String> contexts = options.getAuthnContexts(); if (contexts != null && contexts.size() > 0) { SAMLObjectBuilder<RequestedAuthnContext> builder = (SAMLObjectBuilder<RequestedAuthnContext>) builderFactory.getBuilder(RequestedAuthnContext.DEFAULT_ELEMENT_NAME); RequestedAuthnContext authnContext = builder.buildObject(); authnContext.setComparison(options.getAuthnContextComparison()); for (String context : contexts) { SAMLObjectBuilder<AuthnContextClassRef> contextRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) builderFactory.getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME); AuthnContextClassRef authnContextClassRef = contextRefBuilder.buildObject(); authnContextClassRef.setAuthnContextClassRef(context); authnContext.getAuthnContextClassRefs().add(authnContextClassRef); } request.setRequestedAuthnContext(authnContext); log.debug("ajoute RequestedAuthnContext=" + request.toString()); } }
/** * 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); } }
/** * Fills the request with required AuthNContext according to selected options. * * @param request request to fill * @param options options driving generation of the element */ protected void builNameIDPolicy(AuthnRequest request, WebSSOProfileOptions options) { if (options.getNameID() != null) { SAMLObjectBuilder<NameIDPolicy> builder = (SAMLObjectBuilder<NameIDPolicy>) builderFactory.getBuilder(NameIDPolicy.DEFAULT_ELEMENT_NAME); NameIDPolicy nameIDPolicy = builder.buildObject(); nameIDPolicy.setFormat(options.getNameID()); nameIDPolicy.setAllowCreate(options.isAllowCreate()); nameIDPolicy.setSPNameQualifier(getSPNameQualifier()); request.setNameIDPolicy(nameIDPolicy); } }
/** * 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); }
/** * Fills the request with information about scoping, including IDP in the scope IDP List. * * @param request request to fill * @param serviceURI destination to send the request to * @param options options driving generation of the element, contains list of allowed IDPs */ protected void buildScoping( AuthnRequest request, SingleSignOnService serviceURI, WebSSOProfileOptions options) { if (options.isIncludeScoping() != null && options.isIncludeScoping()) { Set<String> idpEntityNames = options.getAllowedIDPs(); IDPList idpList = buildIDPList(idpEntityNames, serviceURI); SAMLObjectBuilder<Scoping> scopingBuilder = (SAMLObjectBuilder<Scoping>) builderFactory.getBuilder(Scoping.DEFAULT_ELEMENT_NAME); Scoping scoping = scopingBuilder.buildObject(); scoping.setIDPList(idpList); scoping.setProxyCount(options.getProxyCount()); request.setScoping(scoping); } }
/** * Method determines SingleSignOn service (and thus binding) to be used to deliver AuthnRequest to * the IDP. When binding is specified in the WebSSOProfileOptions it is honored. Otherwise first * suitable binding is used. * * @param options user supplied preferences, binding attribute is used * @param idpssoDescriptor idp * @param spDescriptor sp * @return service to send message to * @throws MetadataProviderException in case binding from the options is invalid or not found or * when no default service can be found */ protected SingleSignOnService getSingleSignOnService( WebSSOProfileOptions options, IDPSSODescriptor idpssoDescriptor, SPSSODescriptor spDescriptor) throws MetadataProviderException { // User specified value String userBinding = options.getBinding(); // Find the endpoint List<SingleSignOnService> services = idpssoDescriptor.getSingleSignOnServices(); for (SingleSignOnService service : services) { if (isEndpointSupported(service)) { if (userBinding != null) { if (isEndpointMatching(service, userBinding)) { log.debug("Found user specified binding {}", userBinding); return service; } } else { // Use as a default return service; } } } // No value found if (userBinding != null) { throw new MetadataProviderException( "User specified binding " + userBinding + " is not supported by the IDP using profile " + getProfileIdentifier()); } else { throw new MetadataProviderException( "No supported binding " + userBinding + " was found for profile " + getProfileIdentifier()); } }
/** * Returns AuthnRequest SAML message to be used to demand authentication from an IDP described * using idpEntityDescriptor, with an expected response to the assertionConsumer address. * * @param context message context * @param options preferences of message creation * @param assertionConsumer assertion consumer where the IDP should respond * @param bindingService service used to deliver the request * @return authnRequest ready to be sent to IDP * @throws SAMLException error creating the message * @throws MetadataProviderException error retreiving metadata */ protected AuthnRequest getAuthnRequest( SAMLMessageContext context, WebSSOProfileOptions options, AssertionConsumerService assertionConsumer, SingleSignOnService bindingService) throws SAMLException, MetadataProviderException { SAMLObjectBuilder<AuthnRequest> builder = (SAMLObjectBuilder<AuthnRequest>) builderFactory.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME); AuthnRequest request = builder.buildObject(); request.setIsPassive(options.getPassive()); request.setForceAuthn(options.getForceAuthN()); request.setProviderName(options.getProviderName()); request.setVersion(SAMLVersion.VERSION_20); if (options.getIncludeEidas()) { // ne sert à rien // request.setAssertionConsumerServiceURL(options.getIssuer()); // IssuerBuilder issuerBuilder = new IssuerBuilder(); // Issuer issuer = issuerBuilder.buildObject(); // issuer.setFormat(NAME_ISSUER_FORMAT_EIDAS); // log.debug("issuer="+options.getIssuer()); // issuer.setValue(options.getIssuer()); // request.setIssuer(issuer); NameIDPolicy nameIDPolicy = new NameIDPolicyBuilder().buildObject(); nameIDPolicy.setFormat(NAME_POLICY_FORMAT_EIDAS); nameIDPolicy.setAllowCreate(true); request.setNameIDPolicy(nameIDPolicy); QName eidas = new QName("xmlns:eidas", "http://eidas.europa.eu/saml-extensions"); request.getNamespaceManager().registerAttributeName(eidas); Extensions extEidas = new ExtensionsBuilder() .buildObject("urn:oasis:names:tc:SAML:2.0:protocol", "Extensions", "saml2p"); // Extensions extEidas = new EidasExtensions(); Collection<String> colAttr = options.getEidasAttributes(); // XSAnyBuilder raBuild = new XSAnyBuilder(); // <eidas:SPType>public</eidas:SPType> SPType pub = new SPTypeBuilder() .buildObject("http://eidas.europa.eu/saml-extensions", "SPType", "eidas"); // pub.setTextContent(EIDAS_PUBLIC); pub.setSPType(EIDAS_PUBLIC); // XSAny attrs = new XSAnyBuilder().buildObject("http://eidas.europa.eu/saml-extensions", // "RequestedAttributes", "eidas"); extEidas.getUnknownXMLObjects().add(pub); // XSAnyBuilder anyBuilder = (XSAnyBuilder) // Configuration.getBuilderFactory().getBuilder(XSAny.TYPE_NAME); String resAttrs = "<eidas:RequestedAttributes xmlns:eidas=\"http://eidas.europa.eu/saml-extensions\">"; for (String attr : colAttr) { resAttrs += oneAttribute(attr); } resAttrs += "</eidas:RequestedAttributes>"; log.debug("resAttrs=" + resAttrs); EidasExtensionConfiguration eidasExt = new EidasExtensionConfiguration(); eidasExt.configureExtension(); SAMLSchemaBuilder.addExtensionSchema("/schema/saml_eidas_extension.xsd"); BasicParserPool ppMgr = new BasicParserPool(); ppMgr.setNamespaceAware(true); try { ppMgr.setSchema(SAMLSchemaBuilder.getSAML11Schema()); } catch (SAXException ex) { log.error("Erreur schema=" + ex); return null; } InputStream is = new ByteArrayInputStream(resAttrs.getBytes()); Document domAttrsRaq = null; try { domAttrsRaq = ppMgr.parse(is); } catch (XMLParserException e) { log.error("Erreur dom=" + e); return null; } if (domAttrsRaq == null) { log.error("Erreur dom vide"); return null; } RequestedAttributesUnmarshaller unMars = new RequestedAttributesUnmarshaller(); XMLObject attrs = null; try { attrs = unMars.unmarshall(domAttrsRaq.getDocumentElement()); } catch (UnmarshallingException e) { System.err.println("Erreur unMarsh error=" + e); } extEidas.getUnknownXMLObjects().add(attrs); request.setExtensions(extEidas); } buildCommonAttributes(context.getLocalEntityId(), request, bindingService); buildScoping(request, bindingService, options); builNameIDPolicy(request, options); buildAuthnContext(request, options); buildReturnAddress(request, assertionConsumer); return request; }