|
|
@@ -0,0 +1,88 @@
|
|
|
+/*
|
|
|
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
|
|
+ * or more contributor license agreements. Licensed under the Elastic License;
|
|
|
+ * you may not use this file except in compliance with the Elastic License.
|
|
|
+ */
|
|
|
+
|
|
|
+package org.elasticsearch.xpack.security.action.saml;
|
|
|
+
|
|
|
+import org.apache.logging.log4j.message.ParameterizedMessage;
|
|
|
+import org.elasticsearch.action.ActionListener;
|
|
|
+import org.elasticsearch.action.support.ActionFilters;
|
|
|
+import org.elasticsearch.action.support.HandledTransportAction;
|
|
|
+import org.elasticsearch.common.inject.Inject;
|
|
|
+import org.elasticsearch.tasks.Task;
|
|
|
+import org.elasticsearch.transport.TransportService;
|
|
|
+import org.elasticsearch.xpack.core.security.action.saml.SamlSpMetadataAction;
|
|
|
+import org.elasticsearch.xpack.core.security.action.saml.SamlSpMetadataRequest;
|
|
|
+import org.elasticsearch.xpack.core.security.action.saml.SamlSpMetadataResponse;
|
|
|
+import org.elasticsearch.xpack.security.authc.Realms;
|
|
|
+import org.elasticsearch.xpack.security.authc.saml.SamlRealm;
|
|
|
+import org.elasticsearch.xpack.security.authc.saml.SamlSpMetadataBuilder;
|
|
|
+import org.elasticsearch.xpack.security.authc.saml.SamlUtils;
|
|
|
+import org.elasticsearch.xpack.security.authc.saml.SpConfiguration;
|
|
|
+import org.opensaml.saml.saml2.core.AuthnRequest;
|
|
|
+import org.opensaml.saml.saml2.metadata.EntityDescriptor;
|
|
|
+import org.opensaml.saml.saml2.metadata.impl.EntityDescriptorMarshaller;
|
|
|
+import org.w3c.dom.Element;
|
|
|
+
|
|
|
+import javax.xml.transform.Transformer;
|
|
|
+import javax.xml.transform.dom.DOMSource;
|
|
|
+import javax.xml.transform.stream.StreamResult;
|
|
|
+import java.io.StringWriter;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Locale;
|
|
|
+
|
|
|
+import static org.elasticsearch.xpack.security.authc.saml.SamlRealm.findSamlRealms;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Transport action responsible for generating a SAML SP Metadata.
|
|
|
+ */
|
|
|
+public class TransportSamlSpMetadataAction
|
|
|
+ extends HandledTransportAction<SamlSpMetadataRequest, SamlSpMetadataResponse> {
|
|
|
+
|
|
|
+ private final Realms realms;
|
|
|
+
|
|
|
+ @Inject
|
|
|
+ public TransportSamlSpMetadataAction(TransportService transportService, ActionFilters actionFilters, Realms realms) {
|
|
|
+ super(SamlSpMetadataAction.NAME, transportService, actionFilters, SamlSpMetadataRequest::new
|
|
|
+ );
|
|
|
+ this.realms = realms;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void doExecute(Task task, SamlSpMetadataRequest request,
|
|
|
+ ActionListener<SamlSpMetadataResponse> listener) {
|
|
|
+ List<SamlRealm> realms = findSamlRealms(this.realms, request.getRealmName(), null);
|
|
|
+ if (realms.isEmpty()) {
|
|
|
+ listener.onFailure(SamlUtils.samlException("Cannot find any matching realm for [{}]", request.getRealmName()));
|
|
|
+ } else if (realms.size() > 1) {
|
|
|
+ listener.onFailure(SamlUtils.samlException("Found multiple matching realms [{}] for [{}]", realms, request.getRealmName()));
|
|
|
+ } else {
|
|
|
+ prepareMetadata(realms.get(0), listener);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void prepareMetadata(SamlRealm realm, ActionListener<SamlSpMetadataResponse> listener) {
|
|
|
+ try {
|
|
|
+ final EntityDescriptorMarshaller marshaller = new EntityDescriptorMarshaller();
|
|
|
+ final SpConfiguration spConfig = realm.getServiceProvider();
|
|
|
+ final SamlSpMetadataBuilder builder = new SamlSpMetadataBuilder(Locale.getDefault(), spConfig.getEntityId())
|
|
|
+ .assertionConsumerServiceUrl(spConfig.getAscUrl())
|
|
|
+ .singleLogoutServiceUrl(spConfig.getLogoutUrl())
|
|
|
+ .encryptionCredentials(spConfig.getEncryptionCredentials())
|
|
|
+ .signingCredential(spConfig.getSigningConfiguration().getCredential())
|
|
|
+ .authnRequestsSigned(spConfig.getSigningConfiguration().shouldSign(AuthnRequest.DEFAULT_ELEMENT_LOCAL_NAME));
|
|
|
+ final EntityDescriptor descriptor = builder.build();
|
|
|
+ final Element element = marshaller.marshall(descriptor);
|
|
|
+ final StringWriter writer = new StringWriter();
|
|
|
+ final Transformer serializer = SamlUtils.getHardenedXMLTransformer();
|
|
|
+ serializer.transform(new DOMSource(element), new StreamResult(writer));
|
|
|
+ listener.onResponse(new SamlSpMetadataResponse(writer.toString()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error(new ParameterizedMessage(
|
|
|
+ "Error during SAML SP metadata generation for realm [{}]", realm.name()), e);
|
|
|
+ listener.onFailure(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|