protected NameID getNameID(SAMLMessageContext context, LogoutRequest request) throws DecryptionException { NameID id; if (request.getEncryptedID() != null) { Assert.notNull( context.getLocalDecrypter(), "Can't decrypt NameID, no decrypter is set in the context"); id = (NameID) context.getLocalDecrypter().decrypt(request.getEncryptedID()); } else { id = request.getNameID(); } return id; }
public void sendLogoutRequest(SAMLMessageContext context, SAMLCredential credential) throws SAMLException, MetadataProviderException, MessageEncodingException { // If no user is logged in we do not initialize the protocol. if (credential == null) { return; } IDPSSODescriptor idpDescriptor = (IDPSSODescriptor) context.getPeerEntityRoleMetadata(); SPSSODescriptor spDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata(); String binding = SAMLUtil.getLogoutBinding(idpDescriptor, spDescriptor); SingleLogoutService logoutServiceIDP = SAMLUtil.getLogoutServiceForBinding(idpDescriptor, binding); LogoutRequest logoutRequest = getLogoutRequest(context, credential, logoutServiceIDP); context.setCommunicationProfileId(getProfileIdentifier()); context.setOutboundMessage(logoutRequest); context.setOutboundSAMLMessage(logoutRequest); context.setPeerEntityEndpoint(logoutServiceIDP); boolean signMessage = context.getPeerExtendedMetadata().isRequireLogoutRequestSigned(); sendMessage(context, signMessage); SAMLMessageStorage messageStorage = context.getMessageStorage(); if (messageStorage != null) { messageStorage.storeMessage(logoutRequest.getID(), logoutRequest); } }
/** * Returns logout request message ready to be sent to the IDP. * * @param context message context * @param credential information about assertions used to log current user in * @param bindingService service used to deliver the request * @return logoutRequest to be sent to IDP * @throws SAMLException error creating the message * @throws MetadataProviderException error retrieving metadata */ protected LogoutRequest getLogoutRequest( SAMLMessageContext context, SAMLCredential credential, Endpoint bindingService) throws SAMLException, MetadataProviderException { SAMLObjectBuilder<LogoutRequest> builder = (SAMLObjectBuilder<LogoutRequest>) builderFactory.getBuilder(LogoutRequest.DEFAULT_ELEMENT_NAME); LogoutRequest request = builder.buildObject(); buildCommonAttributes(context.getLocalEntityId(), request, bindingService); // Add session indexes SAMLObjectBuilder<SessionIndex> sessionIndexBuilder = (SAMLObjectBuilder<SessionIndex>) builderFactory.getBuilder(SessionIndex.DEFAULT_ELEMENT_NAME); for (AuthnStatement statement : credential.getAuthenticationAssertion().getAuthnStatements()) { SessionIndex index = sessionIndexBuilder.buildObject(); index.setSessionIndex(statement.getSessionIndex()); request.getSessionIndexes().add(index); } if (request.getSessionIndexes().size() == 0) { throw new SAMLException("No session indexes to logout user for were found"); } SAMLObjectBuilder<NameID> nameIDBuilder = (SAMLObjectBuilder<NameID>) builderFactory.getBuilder(NameID.DEFAULT_ELEMENT_NAME); NameID nameID = nameIDBuilder.buildObject(); nameID.setFormat(credential.getNameID().getFormat()); nameID.setNameQualifier(credential.getNameID().getNameQualifier()); nameID.setSPNameQualifier(credential.getNameID().getSPNameQualifier()); nameID.setSPProvidedID(credential.getNameID().getSPProvidedID()); nameID.setValue(credential.getNameID().getValue()); request.setNameID(nameID); return request; }
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); } }
protected void sendLogoutResponse(Status status, SAMLMessageContext context) throws MetadataProviderException, SAMLException, MessageEncodingException { SAMLObjectBuilder<LogoutResponse> responseBuilder = (SAMLObjectBuilder<LogoutResponse>) builderFactory.getBuilder(LogoutResponse.DEFAULT_ELEMENT_NAME); LogoutResponse logoutResponse = responseBuilder.buildObject(); IDPSSODescriptor idpDescriptor = SAMLUtil.getIDPDescriptor(metadata, context.getPeerEntityId()); SPSSODescriptor spDescriptor = (SPSSODescriptor) context.getLocalEntityRoleMetadata(); String binding = SAMLUtil.getLogoutBinding(idpDescriptor, spDescriptor); SingleLogoutService logoutService = SAMLUtil.getLogoutServiceForBinding(idpDescriptor, binding); logoutResponse.setID(generateID()); logoutResponse.setIssuer(getIssuer(context.getLocalEntityId())); logoutResponse.setVersion(SAMLVersion.VERSION_20); logoutResponse.setIssueInstant(new DateTime()); logoutResponse.setInResponseTo(context.getInboundSAMLMessageId()); logoutResponse.setDestination(logoutService.getLocation()); logoutResponse.setStatus(status); context.setCommunicationProfileId(getProfileIdentifier()); context.setOutboundMessage(logoutResponse); context.setOutboundSAMLMessage(logoutResponse); context.setPeerEntityEndpoint(logoutService); context.setPeerEntityId(idpDescriptor.getID()); context.setPeerEntityRoleMetadata(idpDescriptor); boolean signMessage = context.getPeerExtendedMetadata().isRequireLogoutResponseSigned(); sendMessage(context, signMessage); }
public boolean processLogoutRequest(SAMLMessageContext context, SAMLCredential credential) throws SAMLException, MetadataProviderException, MessageEncodingException { SAMLObject message = context.getInboundSAMLMessage(); // Verify type if (message == null || !(message instanceof LogoutRequest)) { log.warn("Received request is not of a LogoutRequest object type"); throw new SAMLException("Error validating SAML request"); } LogoutRequest logoutRequest = (LogoutRequest) message; // Make sure request was authenticated if required, authentication is done as part of the // binding processing if (!context.isInboundSAMLMessageAuthenticated() && context.getLocalExtendedMetadata().isRequireLogoutRequestSigned()) { log.warn( "Logout Request object is required to be signed by the entity policy: " + context.getInboundSAMLMessageId()); Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Message signature is required"); sendLogoutResponse(status, context); return false; } try { // Verify destination verifyEndpoint(context.getLocalEntityEndpoint(), logoutRequest.getDestination()); } catch (SAMLException e) { log.warn( "Destination of the request {} does not match any singleLogout endpoint", logoutRequest.getDestination()); Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Destination URL of the request is invalid"); sendLogoutResponse(status, context); return false; } // Verify issuer if (logoutRequest.getIssuer() != null) { try { Issuer issuer = logoutRequest.getIssuer(); verifyIssuer(issuer, context); } catch (SAMLException e) { log.warn( "Response issue time is either too old or with date in the future, id {}", context.getInboundSAMLMessageId()); Status status = getStatus(StatusCode.REQUEST_DENIED_URI, "Issuer of the message is unknown"); sendLogoutResponse(status, context); return false; } } // Verify issue time DateTime time = logoutRequest.getIssueInstant(); if (!isDateTimeSkewValid(getResponseSkew(), time)) { log.warn( "Response issue time is either too old or with date in the future, id {}.", context.getInboundSAMLMessageId()); Status status = getStatus(StatusCode.REQUESTER_URI, "Message has been issued too long time ago"); sendLogoutResponse(status, context); return false; } // Check whether any user is logged in if (credential == null) { Status status = getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "No user is logged in"); sendLogoutResponse(status, context); return false; } // Find index for which the logout is requested boolean indexFound = false; if (logoutRequest.getSessionIndexes() != null && logoutRequest.getSessionIndexes().size() > 0) { for (AuthnStatement statement : credential.getAuthenticationAssertion().getAuthnStatements()) { String statementIndex = statement.getSessionIndex(); if (statementIndex != null) { for (SessionIndex index : logoutRequest.getSessionIndexes()) { if (statementIndex.equals(index.getSessionIndex())) { indexFound = true; } } } } } else { indexFound = true; } // Fail if sessionIndex is not found in any assertion if (!indexFound) { // Check logout request still valid and store request if (logoutRequest.getNotOnOrAfter() != null) { // TODO store request for assertions possibly arriving later } Status status = getStatus(StatusCode.REQUESTER_URI, "The requested SessionIndex was not found"); sendLogoutResponse(status, context); return false; } try { // Fail if NameId doesn't correspond to the currently logged user NameID nameID = getNameID(context, logoutRequest); if (nameID == null || !equalsNameID(credential.getNameID(), nameID)) { Status status = getStatus(StatusCode.UNKNOWN_PRINCIPAL_URI, "The requested NameID is invalid"); sendLogoutResponse(status, context); return false; } } catch (DecryptionException e) { Status status = getStatus(StatusCode.RESPONDER_URI, "The NameID can't be decrypted"); sendLogoutResponse(status, context); return false; } // Message is valid, let's logout Status status = getStatus(StatusCode.SUCCESS_URI, null); sendLogoutResponse(status, context); return true; }
/** * 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); } }
/** * 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; }