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:
parent
1d0a9a1b27
commit
c9ffa379ba
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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()]));
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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"]
|
||||
|
|
Loading…
Reference in New Issue