From 3589ca84938ea15b1c318569c2332bfaec7e4ee9 Mon Sep 17 00:00:00 2001 From: Tim Vernum Date: Thu, 11 Apr 2019 17:13:03 +1000 Subject: [PATCH] Add test for security on basic license. This is modelled on the qa test for TLS on basic. It starts a cluster on basic with security & performs a number of security related checks. It also performs those same checks on a trial license. --- .../security/qa/security-basic/build.gradle | 33 +++++ .../security/SecurityWithBasicLicenseIT.java | 139 ++++++++++++++++++ .../src/test/resources/roles.yml | 8 + .../xpack/security/authc/InternalRealms.java | 2 +- 4 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 x-pack/plugin/security/qa/security-basic/build.gradle create mode 100644 x-pack/plugin/security/qa/security-basic/src/test/java/org/elasticsearch/xpack/security/SecurityWithBasicLicenseIT.java create mode 100644 x-pack/plugin/security/qa/security-basic/src/test/resources/roles.yml diff --git a/x-pack/plugin/security/qa/security-basic/build.gradle b/x-pack/plugin/security/qa/security-basic/build.gradle new file mode 100644 index 00000000000..3e86a93dcc2 --- /dev/null +++ b/x-pack/plugin/security/qa/security-basic/build.gradle @@ -0,0 +1,33 @@ +import org.elasticsearch.gradle.http.WaitForHttpResource + +apply plugin: 'elasticsearch.standalone-rest-test' +apply plugin: 'elasticsearch.rest-test' + +dependencies { + // "org.elasticsearch.plugin:x-pack-core:${version}" doesn't work with idea because the testArtifacts are also here + testCompile project(path: xpackModule('core'), configuration: 'default') + testCompile project(path: xpackModule('security'), configuration: 'testArtifacts') + testCompile project(path: xpackModule('core'), configuration: 'testArtifacts') +} + +integTestCluster { + numNodes=2 + + setting 'xpack.ilm.enabled', 'false' + setting 'xpack.ml.enabled', 'false' + setting 'xpack.license.self_generated.type', 'basic' + setting 'xpack.security.enabled', 'true' + setting 'xpack.security.http.ssl.enabled', 'false' + setting 'xpack.security.transport.ssl.enabled', 'false' + + extraConfigFile 'roles.yml', project.projectDir.toPath().resolve('src/test/resources/roles.yml') + setupCommand 'setupUser#admin_user', 'bin/elasticsearch-users', 'useradd', 'admin_user', '-p', 'admin-password', '-r', 'superuser' + setupCommand 'setupUser#security_test_user', 'bin/elasticsearch-users', 'useradd', 'security_test_user', '-p', 'security-test-password', '-r', 'security_test_role' + + waitCondition = { node, ant -> + WaitForHttpResource http = new WaitForHttpResource("http", node.httpUri(), numNodes) + http.setUsername("admin_user") + http.setPassword("admin-password") + return http.wait(5000) + } +} diff --git a/x-pack/plugin/security/qa/security-basic/src/test/java/org/elasticsearch/xpack/security/SecurityWithBasicLicenseIT.java b/x-pack/plugin/security/qa/security-basic/src/test/java/org/elasticsearch/xpack/security/SecurityWithBasicLicenseIT.java new file mode 100644 index 00000000000..d5684d72a11 --- /dev/null +++ b/x-pack/plugin/security/qa/security-basic/src/test/java/org/elasticsearch/xpack/security/SecurityWithBasicLicenseIT.java @@ -0,0 +1,139 @@ +/* + * 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; + +import org.elasticsearch.client.Request; +import org.elasticsearch.client.Response; +import org.elasticsearch.client.ResponseException; +import org.elasticsearch.common.settings.SecureString; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.test.rest.ESRestTestCase; +import org.elasticsearch.test.rest.yaml.ObjectPath; +import org.elasticsearch.xpack.security.authc.InternalRealms; + +import java.io.IOException; +import java.util.Arrays; +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.notNullValue; + +public class SecurityWithBasicLicenseIT extends ESRestTestCase { + + @Override + protected Settings restAdminSettings() { + String token = basicAuthHeaderValue("admin_user", new SecureString("admin-password".toCharArray())); + return Settings.builder() + .put(ThreadContext.PREFIX + ".Authorization", token) + .build(); + } + + @Override + protected Settings restClientSettings() { + String token = basicAuthHeaderValue("security_test_user", new SecureString("security-test-password".toCharArray())); + return Settings.builder() + .put(ThreadContext.PREFIX + ".Authorization", token) + .build(); + } + + public void testWithBasicLicense() throws Exception { + checkLicenseType("basic"); + checkSecurityEnabled(false); + checkAuthentication(); + checkHasPrivileges(); + checkIndexWrite(); + } + + public void testWithTrialLicense() throws Exception { + startTrial(); + try { + checkLicenseType("trial"); + checkSecurityEnabled(true); + checkAuthentication(); + checkHasPrivileges(); + checkIndexWrite(); + } finally { + revertTrial(); + } + } + + private void startTrial() throws IOException { + Response response = client().performRequest(new Request("POST", "/_license/start_trial?acknowledge=true")); + assertOK(response); + } + + private void revertTrial() throws IOException { + client().performRequest(new Request("POST", "/_license/start_basic?acknowledge=true")); + } + + private void checkLicenseType(String type) throws IOException { + Map license = getAsMap("/_license"); + assertThat(license, notNullValue()); + assertThat(ObjectPath.evaluate(license, "license.type"), equalTo(type)); + } + + private void checkSecurityEnabled(boolean allowAllRealms) throws IOException { + Map usage = getAsMap("/_xpack/usage"); + assertThat(usage, notNullValue()); + assertThat(ObjectPath.evaluate(usage, "security.available"), equalTo(true)); + assertThat(ObjectPath.evaluate(usage, "security.enabled"), equalTo(true)); + for (String realm : Arrays.asList("file", "native")) { + assertThat(ObjectPath.evaluate(usage, "security.realms." + realm + ".available"), equalTo(true)); + assertThat(ObjectPath.evaluate(usage, "security.realms." + realm + ".enabled"), equalTo(true)); + } + for (String realm : InternalRealms.getConfigurableRealmsTypes()) { + if (realm.equals("file") == false && realm.equals("native") == false) { + assertThat(ObjectPath.evaluate(usage, "security.realms." + realm + ".available"), equalTo(allowAllRealms)); + assertThat(ObjectPath.evaluate(usage, "security.realms." + realm + ".enabled"), equalTo(false)); + } + } + } + + private void checkAuthentication() throws IOException { + final Map auth = getAsMap("/_security/_authenticate"); + // From file realm, configured in build.gradle + assertThat(ObjectPath.evaluate(auth, "username"), equalTo("security_test_user")); + assertThat(ObjectPath.evaluate(auth, "roles"), contains("security_test_role")); + } + + private void checkHasPrivileges() throws IOException { + final Request request = new Request("GET", "/_security/user/_has_privileges"); + request.setJsonEntity("{" + + "\"cluster\": [ \"manage\", \"monitor\" ]," + + "\"index\": [{ \"names\": [ \"index_allowed\", \"index_denied\" ], \"privileges\": [ \"read\", \"all\" ] }]" + + "}"); + Response response = client().performRequest(request); + final Map auth = entityAsMap(response); + assertThat(ObjectPath.evaluate(auth, "username"), equalTo("security_test_user")); + assertThat(ObjectPath.evaluate(auth, "has_all_requested"), equalTo(false)); + assertThat(ObjectPath.evaluate(auth, "cluster.manage"), equalTo(false)); + assertThat(ObjectPath.evaluate(auth, "cluster.monitor"), equalTo(true)); + assertThat(ObjectPath.evaluate(auth, "index.index_allowed.read"), equalTo(true)); + assertThat(ObjectPath.evaluate(auth, "index.index_allowed.all"), equalTo(false)); + assertThat(ObjectPath.evaluate(auth, "index.index_denied.read"), equalTo(false)); + assertThat(ObjectPath.evaluate(auth, "index.index_denied.all"), equalTo(false)); + } + + private void checkIndexWrite() throws IOException { + final Request request1 = new Request("POST", "/index_allowed/_doc"); + request1.setJsonEntity("{ \"key\" : \"value\" }"); + Response response1 = client().performRequest(request1); + final Map result1 = entityAsMap(response1); + assertThat(ObjectPath.evaluate(result1, "_index"), equalTo("index_allowed")); + assertThat(ObjectPath.evaluate(result1, "result"), equalTo("created")); + + final Request request2 = new Request("POST", "/index_denied/_doc"); + request2.setJsonEntity("{ \"key\" : \"value\" }"); + ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(request2)); + assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(403)); + assertThat(e.getMessage(), containsString("unauthorized for user [security_test_user]")); + } + +} diff --git a/x-pack/plugin/security/qa/security-basic/src/test/resources/roles.yml b/x-pack/plugin/security/qa/security-basic/src/test/resources/roles.yml new file mode 100644 index 00000000000..9b2171257fc --- /dev/null +++ b/x-pack/plugin/security/qa/security-basic/src/test/resources/roles.yml @@ -0,0 +1,8 @@ +# A basic role that is used to test security +security_test_role: + cluster: + - monitor + - "cluster:admin/xpack/license/*" + indices: + - names: [ "index_allowed" ] + privileges: [ "read", "write", "create_index" ] diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java index 66206d50137..ae24c0227de 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/InternalRealms.java @@ -78,7 +78,7 @@ public final class InternalRealms { return ReservedRealm.TYPE.equals(type); } - static Collection getConfigurableRealmsTypes() { + public static Collection getConfigurableRealmsTypes() { return Collections.unmodifiableSet(XPACK_TYPES); }