[7.x] Add end to end QA authentication test (#54215) (#54567)

Use the same ES cluster as both an SP and an IDP and perform
IDP initiated and SP initiated SSO. The REST client plays the role
of both the Cloud UI and Kibana in these flows

Backport of #54215

* fix compilation issues
This commit is contained in:
Ioannis Kakavas 2020-04-01 18:35:21 +03:00 committed by GitHub
parent 1d0a9a1b27
commit c9ffa379ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 333 additions and 77 deletions

View File

@ -1154,16 +1154,17 @@ public abstract class ESRestTestCase extends ESTestCase {
return true;
}
switch (name) {
case ".triggered_watches":
case ".watches":
case "logstash-index-template":
case ".logstash-management":
case "security_audit_log":
case ".slm-history":
case ".async-search":
return true;
default:
return false;
case ".triggered_watches":
case ".watches":
case "logstash-index-template":
case ".logstash-management":
case "security_audit_log":
case ".slm-history":
case ".async-search":
case "saml-service-provider":
return true;
default:
return false;
}
}

View File

@ -23,12 +23,26 @@ testClusters.integTest {
setting 'xpack.security.enabled', 'true'
setting 'xpack.security.authc.token.enabled', 'true'
setting 'xpack.security.authc.api_key.enabled', 'true'
setting 'xpack.security.authc.realms.file.file.order', '0'
setting 'xpack.security.authc.realms.native.native.order', '1'
setting 'xpack.security.authc.realms.saml.cloud-saml.order', '2'
setting 'xpack.security.authc.realms.saml.cloud-saml.idp.entity_id', 'https://idp.test.es.elasticsearch.org/'
setting 'xpack.security.authc.realms.saml.cloud-saml.idp.metadata.path', 'idp-metadata.xml'
setting 'xpack.security.authc.realms.saml.cloud-saml.sp.entity_id', 'ec:123456:abcdefg'
// This is a dummy one, we simulate the browser and a web app in our tests
setting 'xpack.security.authc.realms.saml.cloud-saml.sp.acs', 'https://sp1.test.es.elasticsearch.org/saml/acs'
setting 'xpack.security.authc.realms.saml.cloud-saml.attributes.principal', 'https://idp.test.es.elasticsearch.org/attribute/principal'
setting 'xpack.security.authc.realms.saml.cloud-saml.attributes.name', 'https://idp.test.es.elasticsearch.org/attribute/name'
setting 'logger.org.elasticsearch.xpack.security.authc.saml', 'TRACE'
setting 'logger.org.elasticsearch.xpack.idp', 'TRACE'
extraConfigFile 'roles.yml', file('src/test/resources/roles.yml')
extraConfigFile 'idp-sign.crt', file('src/test/resources/idp-sign.crt')
extraConfigFile 'idp-sign.key', file('src/test/resources/idp-sign.key')
extraConfigFile 'wildcard_services.json', file('src/test/resources/wildcard_services.json')
// The SAML SP is preconfigured with the metadata of the IDP
extraConfigFile 'idp-metadata.xml', file('src/test/resources/idp-metadata.xml')
user username: "admin_user", password: "admin-password"
user username: "idp_user", password: "idp-password", role: "idp_role"
user username: "idp_admin", password: "idp-password", role: "idp_admin"
user username: "idp_user", password: "idp-password", role: "idp_user"
}

View File

@ -0,0 +1,196 @@
/*
* 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.idp;
import org.apache.http.HttpHost;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.ObjectPath;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.xpack.core.security.action.saml.SamlPrepareAuthenticationResponse;
import org.elasticsearch.xpack.idp.saml.sp.SamlServiceProviderIndex;
import org.junit.Before;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.instanceOf;
public class IdentityProviderAuthenticationIT extends IdpRestTestCase {
// From build.gradle
private final String SP_ENTITY_ID = "ec:123456:abcdefg";
private final String SP_ACS = "https://sp1.test.es.elasticsearch.org/saml/acs";
private final String REALM_NAME = "cloud-saml";
@Before
public void createUsers() throws IOException {
setUserPassword("kibana", new SecureString("kibana".toCharArray()));
}
public void testRegistrationAndIdpInitiatedSso() throws Exception {
final Map<String, Object> request = new HashMap<>();
request.put("name", "Test SP");
request.put("acs", SP_ACS);
final Map<String, Object> privilegeMap = new HashMap<>();
privilegeMap.put("resource", SP_ENTITY_ID);
final Map<String, String> roleMap = new HashMap<>();
roleMap.put("superuser", "role:superuser");
roleMap.put("viewer", "role:viewer");
privilegeMap.put("roles", roleMap);
request.put("privileges", privilegeMap);
final Map<String, String> attributeMap = new HashMap<>();
attributeMap.put("principal", "https://idp.test.es.elasticsearch.org/attribute/principal");
attributeMap.put("name", "https://idp.test.es.elasticsearch.org/attribute/name");
attributeMap.put("email", "https://idp.test.es.elasticsearch.org/attribute/email");
attributeMap.put("roles", "https://idp.test.es.elasticsearch.org/attribute/roles");
request.put("attributes", attributeMap);
final SamlServiceProviderIndex.DocumentVersion docVersion = createServiceProvider(SP_ENTITY_ID, request);
checkIndexDoc(docVersion);
ensureGreen(SamlServiceProviderIndex.INDEX_NAME);
final String samlResponse = generateSamlResponse(SP_ENTITY_ID, SP_ACS, null);
authenticateWithSamlResponse(samlResponse, null);
}
public void testRegistrationAndSpInitiatedSso() throws Exception {
final Map<String, Object> request = new HashMap<>();
request.put("name", "Test SP");
request.put("acs", SP_ACS);
final Map<String, Object> privilegeMap = new HashMap<>();
privilegeMap.put("resource", SP_ENTITY_ID);
final Map<String, String> roleMap = new HashMap<>();
roleMap.put("superuser", "role:superuser");
roleMap.put("viewer", "role:viewer");
privilegeMap.put("roles", roleMap);
request.put("privileges", privilegeMap);
final Map<String, String> attributeMap = new HashMap<>();
attributeMap.put("principal", "https://idp.test.es.elasticsearch.org/attribute/principal");
attributeMap.put("name", "https://idp.test.es.elasticsearch.org/attribute/name");
attributeMap.put("email", "https://idp.test.es.elasticsearch.org/attribute/email");
attributeMap.put("roles", "https://idp.test.es.elasticsearch.org/attribute/roles");
request.put("attributes", attributeMap);
final SamlServiceProviderIndex.DocumentVersion docVersion = createServiceProvider(SP_ENTITY_ID, request);
checkIndexDoc(docVersion);
ensureGreen(SamlServiceProviderIndex.INDEX_NAME);
SamlPrepareAuthenticationResponse samlPrepareAuthenticationResponse = generateSamlAuthnRequest(REALM_NAME);
final String requestId = samlPrepareAuthenticationResponse.getRequestId();
final String query = samlPrepareAuthenticationResponse.getRedirectUrl().split("\\?")[1];
Map<String, Object> authnState = validateAuthnRequest(SP_ENTITY_ID, query);
final String samlResponse = generateSamlResponse(SP_ENTITY_ID, SP_ACS, authnState);
assertThat(samlResponse, containsString("InResponseTo=\"" + requestId + "\""));
authenticateWithSamlResponse(samlResponse, requestId);
}
private Map<String, Object> validateAuthnRequest(String entityId, String authnRequestQuery) throws Exception {
final Request request = new Request("POST", "/_idp/saml/validate");
request.setJsonEntity("{\"authn_request_query\":\"" + authnRequestQuery + "\"}");
final Response response = client().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("service_provider.entity_id", map), instanceOf(String.class));
assertThat(ObjectPath.eval("service_provider.entity_id", map), equalTo(entityId));
assertThat(ObjectPath.eval("authn_state", map), instanceOf(Map.class));
return ObjectPath.eval("authn_state", map);
}
private SamlPrepareAuthenticationResponse generateSamlAuthnRequest(String realmName) throws Exception {
final Request request = new Request("POST", "/_security/saml/prepare");
request.setJsonEntity("{\"realm\":\"" + realmName + "\"}");
try (RestClient kibanaClient = restClientAsKibana()) {
final Response response = kibanaClient.performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("realm", map), equalTo(realmName));
assertThat(ObjectPath.eval("id", map), instanceOf(String.class));
assertThat(ObjectPath.eval("redirect", map), instanceOf(String.class));
return new SamlPrepareAuthenticationResponse(realmName, ObjectPath.eval("id", map), ObjectPath.eval("redirect", map));
}
}
private String generateSamlResponse(String entityId, String acs, @Nullable Map<String, Object> authnState) throws Exception {
final Request request = new Request("POST", "/_idp/saml/init");
if (authnState != null && authnState.isEmpty() == false) {
request.setJsonEntity("{\"entity_id\":\"" + entityId + "\", \"acs\":\"" + acs + "\"," +
"\"authn_state\":" + Strings.toString(JsonXContent.contentBuilder().map(authnState)) + "}");
} else {
request.setJsonEntity("{\"entity_id\":\"" + entityId + "\", \"acs\":\"" + acs + "\"}");
}
request.setOptions(RequestOptions.DEFAULT.toBuilder()
.addHeader("es-secondary-authorization", basicAuthHeaderValue("idp_user",
new SecureString("idp-password".toCharArray())))
.build());
final Response response = client().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("service_provider.entity_id", map), equalTo(entityId));
assertThat(ObjectPath.eval("post_url", map), equalTo(acs));
assertThat(ObjectPath.eval("saml_response", map), instanceOf(String.class));
return (String) ObjectPath.eval("saml_response", map);
}
private void authenticateWithSamlResponse(String samlResponse, @Nullable String id) throws Exception {
final String encodedResponse = Base64.getEncoder().encodeToString(samlResponse.getBytes(StandardCharsets.UTF_8));
final Request request = new Request("POST", "/_security/saml/authenticate");
if (Strings.hasText(id)) {
request.setJsonEntity("{\"content\":\"" + encodedResponse + "\", \"realm\":\"" + REALM_NAME + "\", \"ids\":[\"" + id + "\"]}");
} else {
request.setJsonEntity("{\"content\":\"" + encodedResponse + "\", \"realm\":\"" + REALM_NAME + "\"}");
}
final String accessToken;
try (RestClient kibanaClient = restClientAsKibana()) {
final Response response = kibanaClient.performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("username", map), instanceOf(String.class));
assertThat(ObjectPath.eval("username", map), equalTo("idp_user"));
assertThat(ObjectPath.eval("realm", map), instanceOf(String.class));
assertThat(ObjectPath.eval("realm", map), equalTo(REALM_NAME));
assertThat(ObjectPath.eval("access_token", map), instanceOf(String.class));
accessToken = ObjectPath.eval("access_token", map);
assertThat(ObjectPath.eval("refresh_token", map), instanceOf(String.class));
}
try (RestClient accessTokenClient = restClientWithToken(accessToken)) {
final Request authenticateRequest = new Request("GET", "/_security/_authenticate");
final Response authenticateResponse = accessTokenClient.performRequest(authenticateRequest);
final Map<String, Object> authMap = entityAsMap(authenticateResponse);
assertThat(ObjectPath.eval("username", authMap), instanceOf(String.class));
assertThat(ObjectPath.eval("username", authMap), equalTo("idp_user"));
assertThat(ObjectPath.eval("metadata.saml_nameid_format", authMap), instanceOf(String.class));
assertThat(ObjectPath.eval("metadata.saml_nameid_format", authMap),
equalTo("urn:oasis:names:tc:SAML:2.0:nameid-format:transient"));
assertThat(ObjectPath.eval("metadata.saml_roles", authMap), instanceOf(List.class));
assertThat(ObjectPath.eval("metadata.saml_roles", authMap), hasSize(1));
assertThat(ObjectPath.eval("metadata.saml_roles", authMap), contains("viewer"));
}
}
private RestClient restClientWithToken(String accessToken) throws IOException {
return buildClient(
Settings.builder().put(ThreadContext.PREFIX + ".Authorization", "Bearer " + accessToken).build(),
getClusterHosts().toArray(new HttpHost[getClusterHosts().size()]));
}
private RestClient restClientAsKibana() throws IOException {
return buildClient(
Settings.builder().put(ThreadContext.PREFIX + ".Authorization", basicAuthHeaderValue("kibana",
new SecureString("kibana".toCharArray()))).build(),
getClusterHosts().toArray(new HttpHost[getClusterHosts().size()]));
}
}

View File

@ -5,8 +5,11 @@
*/
package org.elasticsearch.xpack.idp;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.security.ChangePasswordRequest;
import org.elasticsearch.client.security.DeleteRoleRequest;
import org.elasticsearch.client.security.DeleteUserRequest;
import org.elasticsearch.client.security.PutRoleRequest;
@ -16,18 +19,28 @@ import org.elasticsearch.client.security.user.User;
import org.elasticsearch.client.security.user.privileges.ApplicationResourcePrivileges;
import org.elasticsearch.client.security.user.privileges.IndicesPrivileges;
import org.elasticsearch.client.security.user.privileges.Role;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.ObjectPath;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.test.rest.ESRestTestCase;
import org.elasticsearch.xpack.idp.saml.sp.SamlServiceProviderIndex;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
public abstract class IdpRestTestCase extends ESRestTestCase {
@ -43,7 +56,7 @@ public abstract class IdpRestTestCase extends ESRestTestCase {
@Override
protected Settings restClientSettings() {
String token = basicAuthHeaderValue("idp_user", new SecureString("idp-password".toCharArray()));
String token = basicAuthHeaderValue("idp_admin", new SecureString("idp-password".toCharArray()));
return Settings.builder()
.put(ThreadContext.PREFIX + ".Authorization", token)
.build();
@ -92,4 +105,61 @@ public abstract class IdpRestTestCase extends ESRestTestCase {
final DeleteRoleRequest request = new DeleteRoleRequest(name, RefreshPolicy.WAIT_UNTIL);
client.security().deleteRole(request, RequestOptions.DEFAULT);
}
protected void setUserPassword(String username, SecureString password) throws IOException {
final RestHighLevelClient client = getHighLevelAdminClient();
final ChangePasswordRequest request = new ChangePasswordRequest(username, password.getChars(), RefreshPolicy.NONE);
client.security().changePassword(request, RequestOptions.DEFAULT);
}
protected SamlServiceProviderIndex.DocumentVersion createServiceProvider(String entityId, Map<String, Object> body) throws IOException {
// so that we don't hit [SERVICE_UNAVAILABLE/1/state not recovered / initialized]
ensureGreen("");
final Request request = new Request("PUT", "/_idp/saml/sp/" + encode(entityId) + "?refresh=" + RefreshPolicy.IMMEDIATE.getValue());
final String entity = Strings.toString(JsonXContent.contentBuilder().map(body));
request.setJsonEntity(entity);
final Response response = client().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("service_provider.entity_id", map), equalTo(entityId));
assertThat(ObjectPath.eval("service_provider.enabled", map), equalTo(true));
final Object docId = ObjectPath.eval("document._id", map);
final Object seqNo = ObjectPath.eval("document._seq_no", map);
final Object primaryTerm = ObjectPath.eval("document._primary_term", map);
assertThat(docId, instanceOf(String.class));
assertThat(seqNo, instanceOf(Number.class));
assertThat(primaryTerm, instanceOf(Number.class));
return new SamlServiceProviderIndex.DocumentVersion((String) docId, asLong(primaryTerm), asLong(seqNo));
}
protected void checkIndexDoc(SamlServiceProviderIndex.DocumentVersion docVersion) throws IOException {
final Request request = new Request("GET", SamlServiceProviderIndex.INDEX_NAME + "/_doc/" + docVersion.id);
final Response response = adminClient().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(map.get("_index"), equalTo(SamlServiceProviderIndex.INDEX_NAME));
assertThat(map.get("_id"), equalTo(docVersion.id));
assertThat(asLong(map.get("_seq_no")), equalTo(docVersion.seqNo));
assertThat(asLong(map.get("_primary_term")), equalTo(docVersion.primaryTerm));
}
protected Long asLong(Object val) {
if (val == null) {
return null;
}
if (val instanceof Long) {
return (Long) val;
}
if (val instanceof Number) {
return ((Number) val).longValue();
}
if (val instanceof String) {
return Long.parseLong((String) val);
}
throw new IllegalArgumentException("Value [" + val + "] of type [" + val.getClass() + "] is not a Long");
}
protected String encode(String param) throws UnsupportedEncodingException {
return URLEncoder.encode(param, StandardCharsets.UTF_8.name());
}
}

View File

@ -9,18 +9,13 @@ import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.xcontent.ObjectPath;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.xpack.idp.saml.sp.SamlServiceProviderIndex;
import org.elasticsearch.xpack.idp.saml.sp.SamlServiceProviderIndex.DocumentVersion;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.HashMap;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
@ -36,7 +31,6 @@ public class ManageServiceProviderRestIT extends IdpRestTestCase {
// From SAMLConstants
private final String REDIRECT_BINDING = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect";
@AwaitsFix(bugUrl = "https://github.com/elastic/elasticsearch/issues/54445")
public void testCreateAndDeleteServiceProvider() throws Exception {
final String entityId = "ec:" + randomAlphaOfLength(8) + ":" + randomAlphaOfLength(12);
final Map<String, Object> request = new HashMap<>();
@ -64,35 +58,6 @@ public class ManageServiceProviderRestIT extends IdpRestTestCase {
expectThrows(ResponseException.class, () -> deleteServiceProvider(entityId, docVersion));
}
private DocumentVersion createServiceProvider(String entityId, Map<String, Object> body) throws IOException {
final Request request = new Request("PUT", "/_idp/saml/sp/" + encode(entityId) + "?refresh=" + RefreshPolicy.IMMEDIATE.getValue());
final String entity = Strings.toString(JsonXContent.contentBuilder().map(body));
request.setJsonEntity(entity);
final Response response = client().performRequest(request);
final Map<String, Object> map = entityAsMap(response);
assertThat(ObjectPath.eval("service_provider.entity_id", map), equalTo(entityId));
assertThat(ObjectPath.eval("service_provider.enabled", map), equalTo(true));
final Object docId = ObjectPath.eval("document._id", map);
final Object seqNo = ObjectPath.eval("document._seq_no", map);
final Object primaryTerm = ObjectPath.eval("document._primary_term", map);
assertThat(docId, instanceOf(String.class));
assertThat(seqNo, instanceOf(Number.class));
assertThat(primaryTerm, instanceOf(Number.class));
return new DocumentVersion((String) docId, asLong(primaryTerm), asLong(seqNo));
}
private void checkIndexDoc(DocumentVersion docVersion) throws IOException {
final Request request = new Request("GET", SamlServiceProviderIndex.INDEX_NAME + "/_doc/" + docVersion.id);
final Response response = adminClient().performRequest(request);
final Map<String, Object>
map = entityAsMap(response);
assertThat(map.get("_index"), equalTo(SamlServiceProviderIndex.INDEX_NAME));
assertThat(map.get("_id"), equalTo(docVersion.id));
assertThat(asLong(map.get("_seq_no")), equalTo(docVersion.seqNo));
assertThat(asLong(map.get("_primary_term")), equalTo(docVersion.primaryTerm));
}
private void deleteServiceProvider(String entityId, DocumentVersion version) throws IOException {
final Response response = client().performRequest(new Request("DELETE",
"/_idp/saml/sp/" + encode(entityId) + "?refresh=" + RefreshPolicy.IMMEDIATE.getValue()));
@ -121,24 +86,4 @@ public class ManageServiceProviderRestIT extends IdpRestTestCase {
assertThat((String) metadata, containsString(IDP_ENTITY_ID));
assertThat((String) metadata, containsString(REDIRECT_BINDING));
}
private String encode(String param) throws UnsupportedEncodingException {
return URLEncoder.encode(param, StandardCharsets.UTF_8.name());
}
private Long asLong(Object val) {
if (val == null) {
return null;
}
if (val instanceof Long) {
return (Long) val;
}
if (val instanceof Number) {
return ((Number) val).longValue();
}
if (val instanceof String) {
return Long.parseLong((String) val);
}
throw new IllegalArgumentException("Value [" + val + "] of type [" + val.getClass() + "] is not a Long");
}
}

View File

@ -17,8 +17,6 @@ import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
@ -114,9 +112,4 @@ public class WildcardServiceProviderRestIT extends IdpRestTestCase {
return BytesReference.bytes(builder).utf8ToString();
}
}
private String encode(String param) throws UnsupportedEncodingException {
return URLEncoder.encode(param, "UTF-8");
}
}

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" entityID="https://idp.test.es.elasticsearch.org/">
<md:IDPSSODescriptor WantAuthnRequestsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<md:KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>
MIIDoDCCAoigAwIBAgIVAPVpXjny5MCPPsCCGvInDfZQu+4JMA0GCSqGSIb3DQEBCwUAMF8xEzAR
BgoJkiaJk/IsZAEZFgNvcmcxHTAbBgoJkiaJk/IsZAEZFg1lbGFzdGljc2VhcmNoMQswCQYDVQQL
EwJlczENMAsGA1UECxMEdGVzdDENMAsGA1UEAxMEc2lnbjAeFw0yMDAzMTMwNDAzNDFaFw00NzA3
MjkwNDAzNDFaMF8xEzARBgoJkiaJk/IsZAEZFgNvcmcxHTAbBgoJkiaJk/IsZAEZFg1lbGFzdGlj
c2VhcmNoMQswCQYDVQQLEwJlczENMAsGA1UECxMEdGVzdDENMAsGA1UEAxMEc2lnbjCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAI/JK5IlwBixo2mqX8wDnH2F+tXDJKU8uNezoulYFVMp
17iK41sOIwCInd6DGDJPkgIIkobnmp3gDgnLckzApC8Ck9xmnjmEQdg+gZO1gtgBVU6Lj8haCzPU
MVszGodlraeaRMTIBrNG6GPMo/PN5hj2vyC383jiDlE9K3I+Y0rxhnPj6Ic3aTO/bXiMG4FiIvag
/ViITnvkL1mREwF9JER40dgQoaJJJFTfjcWYnd1q9FDlIhylj2KJwXhOkSZ5znGM93b3GZ5VZUm8
zzMlq/RzyywDet2jyOSrBn9X3lrp0fPjfhTW+O/+2G/k6bVP29BYJ5Im+JVjjaYbYFI5pQ0CAwEA
AaNTMFEwHQYDVR0OBBYEFEghxQTreebAJEak+zywt5B/LpaLMB8GA1UdIwQYMBaAFEghxQTreebA
JEak+zywt5B/LpaLMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBACtGNnaUK4Ut
KPOSg7fOW3/tIJoPhriuSlAdqQxNRYjkdmPJsY7Gra/kikCm0dRUbttMTfOqwZwu5KdKJUo3duXK
pRVK3emFDixM8FxFbhtA19+Newzcp6mS0K0T4wqAI5jH/a+eHJb6hDWuCrm9RICxXwqpUdn3HfJr
YQzsOCuecTAy60jfxjjZeZxAMRIARkhaHQeOdX3rg5v9kWzFXRD0Y7nlqqqCa0el2gs9yZqjDYRM
Fzz8z4qN9q2NzjohhxgIwuWK+0R6qOIg3XK53kdh6YjgWqlGn1aX9z7ztqlDmiD7LIsPgWL2BARh
WOexDVOg4wTpACOt4c84VVAVt98=</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</md:KeyDescriptor>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat>
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://idp.test.es.elasticsearch.org/test/saml/redirect"/>
</md:IDPSSODescriptor>
</md:EntityDescriptor>

View File

@ -1,6 +1,11 @@
# A basic role that is used to call the IdP's APIs
idp_role:
idp_admin:
cluster:
- monitor
- "cluster:admin/idp/*"
idp_user:
applications:
- application: elastic-cloud
resources: ["ec:123456:abcdefg"]
privileges: ["role:viewer"]