Add negative tests for security features in basic
Assert that API Keys, Tokens, DLS/FLS do not work in basic
This commit is contained in:
parent
3589ca8493
commit
8426130553
|
@ -19,6 +19,8 @@ integTestCluster {
|
||||||
setting 'xpack.security.enabled', 'true'
|
setting 'xpack.security.enabled', 'true'
|
||||||
setting 'xpack.security.http.ssl.enabled', 'false'
|
setting 'xpack.security.http.ssl.enabled', 'false'
|
||||||
setting 'xpack.security.transport.ssl.enabled', 'false'
|
setting 'xpack.security.transport.ssl.enabled', 'false'
|
||||||
|
setting 'xpack.security.authc.token.enabled', 'true'
|
||||||
|
setting 'xpack.security.authc.api_key.enabled', 'true'
|
||||||
|
|
||||||
extraConfigFile 'roles.yml', project.projectDir.toPath().resolve('src/test/resources/roles.yml')
|
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#admin_user', 'bin/elasticsearch-users', 'useradd', 'admin_user', '-p', 'admin-password', '-r', 'superuser'
|
||||||
|
|
|
@ -5,9 +5,12 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security;
|
package org.elasticsearch.xpack.security;
|
||||||
|
|
||||||
|
import org.apache.http.HttpHeaders;
|
||||||
import org.elasticsearch.client.Request;
|
import org.elasticsearch.client.Request;
|
||||||
|
import org.elasticsearch.client.RequestOptions;
|
||||||
import org.elasticsearch.client.Response;
|
import org.elasticsearch.client.Response;
|
||||||
import org.elasticsearch.client.ResponseException;
|
import org.elasticsearch.client.ResponseException;
|
||||||
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
import org.elasticsearch.common.settings.SecureString;
|
import org.elasticsearch.common.settings.SecureString;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
|
@ -16,7 +19,9 @@ import org.elasticsearch.test.rest.yaml.ObjectPath;
|
||||||
import org.elasticsearch.xpack.security.authc.InternalRealms;
|
import org.elasticsearch.xpack.security.authc.InternalRealms;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||||
|
@ -49,18 +54,36 @@ public class SecurityWithBasicLicenseIT extends ESRestTestCase {
|
||||||
checkAuthentication();
|
checkAuthentication();
|
||||||
checkHasPrivileges();
|
checkHasPrivileges();
|
||||||
checkIndexWrite();
|
checkIndexWrite();
|
||||||
|
assertFailToGetToken();
|
||||||
|
assertFailToGetApiKey();
|
||||||
|
assertAddRoleWithDLS(false);
|
||||||
|
assertAddRoleWithFLS(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWithTrialLicense() throws Exception {
|
public void testWithTrialLicense() throws Exception {
|
||||||
startTrial();
|
startTrial();
|
||||||
|
String accessToken = null;
|
||||||
|
Tuple<String, String> keyAndId = null;
|
||||||
try {
|
try {
|
||||||
checkLicenseType("trial");
|
checkLicenseType("trial");
|
||||||
checkSecurityEnabled(true);
|
checkSecurityEnabled(true);
|
||||||
checkAuthentication();
|
checkAuthentication();
|
||||||
checkHasPrivileges();
|
checkHasPrivileges();
|
||||||
checkIndexWrite();
|
checkIndexWrite();
|
||||||
|
accessToken = getAccessToken();
|
||||||
|
keyAndId = getApiKeyAndId();
|
||||||
|
assertAuthenticateWithToken(accessToken, true);
|
||||||
|
assertAuthenticateWithApiKey(keyAndId, true);
|
||||||
|
assertAddRoleWithDLS(true);
|
||||||
|
assertAddRoleWithFLS(true);
|
||||||
} finally {
|
} finally {
|
||||||
revertTrial();
|
revertTrial();
|
||||||
|
assertAuthenticateWithToken(accessToken, false);
|
||||||
|
assertAuthenticateWithApiKey(keyAndId, false);
|
||||||
|
assertFailToGetToken();
|
||||||
|
assertFailToGetApiKey();
|
||||||
|
assertAddRoleWithDLS(false);
|
||||||
|
assertAddRoleWithFLS(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,4 +159,138 @@ public class SecurityWithBasicLicenseIT extends ESRestTestCase {
|
||||||
assertThat(e.getMessage(), containsString("unauthorized for user [security_test_user]"));
|
assertThat(e.getMessage(), containsString("unauthorized for user [security_test_user]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Request buildGetTokenRequest() {
|
||||||
|
final Request getToken = new Request("POST", "/_security/oauth2/token");
|
||||||
|
getToken.setJsonEntity("{\"grant_type\" : \"password\",\n" +
|
||||||
|
" \"username\" : \"security_test_user\",\n" +
|
||||||
|
" \"password\" : \"security-test-password\"\n" +
|
||||||
|
"}");
|
||||||
|
return getToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Request buildGetApiKeyRequest() {
|
||||||
|
final Request getApiKey = new Request("POST", "/_security/api_key");
|
||||||
|
getApiKey.setJsonEntity("{\"name\" : \"my-api-key\",\n" +
|
||||||
|
" \"expiration\" : \"2d\",\n" +
|
||||||
|
" \"role_descriptors\" : {} \n" +
|
||||||
|
"}");
|
||||||
|
return getApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAccessToken() throws IOException {
|
||||||
|
Response getTokenResponse = adminClient().performRequest(buildGetTokenRequest());
|
||||||
|
assertThat(getTokenResponse.getStatusLine().getStatusCode(), equalTo(200));
|
||||||
|
final Map<String, Object> tokens = entityAsMap(getTokenResponse);
|
||||||
|
return ObjectPath.evaluate(tokens, "access_token").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Tuple<String, String> getApiKeyAndId() throws IOException {
|
||||||
|
Response getApiKeyResponse = adminClient().performRequest(buildGetApiKeyRequest());
|
||||||
|
assertThat(getApiKeyResponse.getStatusLine().getStatusCode(), equalTo(200));
|
||||||
|
final Map<String, Object> apiKeyResponseMap = entityAsMap(getApiKeyResponse);
|
||||||
|
assertOK(getApiKeyResponse);
|
||||||
|
return new Tuple<>(ObjectPath.evaluate(apiKeyResponseMap, "api_key").toString(),
|
||||||
|
ObjectPath.evaluate(apiKeyResponseMap, "id").toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertFailToGetToken() {
|
||||||
|
ResponseException e = expectThrows(ResponseException.class, () -> adminClient().performRequest(buildGetTokenRequest()));
|
||||||
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(403));
|
||||||
|
assertThat(e.getMessage(), containsString("current license is non-compliant for [security tokens]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertFailToGetApiKey() {
|
||||||
|
ResponseException e = expectThrows(ResponseException.class, () -> adminClient().performRequest(buildGetApiKeyRequest()));
|
||||||
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(403));
|
||||||
|
assertThat(e.getMessage(), containsString("current license is non-compliant for [api keys]"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAuthenticateWithToken(String accessToken, boolean shouldSucceed) throws IOException {
|
||||||
|
assertNotNull("access token cannot be null", accessToken);
|
||||||
|
Request request = new Request("GET", "/_security/_authenticate");
|
||||||
|
RequestOptions.Builder options = request.getOptions().toBuilder();
|
||||||
|
options.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
|
||||||
|
request.setOptions(options);
|
||||||
|
if (shouldSucceed) {
|
||||||
|
Response authenticateResponse = client().performRequest(request);
|
||||||
|
assertOK(authenticateResponse);
|
||||||
|
assertEquals("security_test_user", entityAsMap(authenticateResponse).get("username"));
|
||||||
|
} else {
|
||||||
|
ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(request));
|
||||||
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(401));
|
||||||
|
assertThat(e.getMessage(), containsString("missing authentication credentials for REST request"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAuthenticateWithApiKey(Tuple<String, String> keyAndId, boolean shouldSucceed) throws IOException {
|
||||||
|
assertNotNull("API Key and Id cannot be null", keyAndId);
|
||||||
|
Request request = new Request("GET", "/_security/_authenticate");
|
||||||
|
RequestOptions.Builder options = request.getOptions().toBuilder();
|
||||||
|
String headerValue = Base64.getEncoder().encodeToString((keyAndId.v2() + ":" + keyAndId.v1()).getBytes(StandardCharsets.UTF_8));
|
||||||
|
options.addHeader(HttpHeaders.AUTHORIZATION, "ApiKey " + headerValue);
|
||||||
|
request.setOptions(options);
|
||||||
|
if (shouldSucceed) {
|
||||||
|
Response authenticateResponse = client().performRequest(request);
|
||||||
|
assertOK(authenticateResponse);
|
||||||
|
assertEquals("admin_user", entityAsMap(authenticateResponse).get("username"));
|
||||||
|
} else {
|
||||||
|
ResponseException e = expectThrows(ResponseException.class, () -> client().performRequest(request));
|
||||||
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(401));
|
||||||
|
assertThat(e.getMessage(), containsString("missing authentication credentials for REST request"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAddRoleWithDLS(boolean shouldSucceed) throws IOException {
|
||||||
|
final Request addRole = new Request("POST", "/_security/role/dlsrole");
|
||||||
|
addRole.setJsonEntity("{\n" +
|
||||||
|
" \"cluster\": [\"all\"],\n" +
|
||||||
|
" \"indices\": [\n" +
|
||||||
|
" {\n" +
|
||||||
|
" \"names\": [ \"index1\", \"index2\" ],\n" +
|
||||||
|
" \"privileges\": [\"all\"],\n" +
|
||||||
|
" \"query\": \"{\\\"match\\\": {\\\"title\\\": \\\"foo\\\"}}\" \n" +
|
||||||
|
" }\n" +
|
||||||
|
" ],\n" +
|
||||||
|
" \"run_as\": [ \"other_user\" ],\n" +
|
||||||
|
" \"metadata\" : { // optional\n" +
|
||||||
|
" \"version\" : 1\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}");
|
||||||
|
if (shouldSucceed) {
|
||||||
|
Response addRoleResponse = adminClient().performRequest(addRole);
|
||||||
|
assertThat(addRoleResponse.getStatusLine().getStatusCode(), equalTo(200));
|
||||||
|
} else {
|
||||||
|
ResponseException e = expectThrows(ResponseException.class, () -> adminClient().performRequest(addRole));
|
||||||
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(403));
|
||||||
|
assertThat(e.getMessage(), containsString("current license is non-compliant for [field and document level security]"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAddRoleWithFLS(boolean shouldSucceed) throws IOException {
|
||||||
|
final Request addRole = new Request("POST", "/_security/role/dlsrole");
|
||||||
|
addRole.setJsonEntity("{\n" +
|
||||||
|
" \"cluster\": [\"all\"],\n" +
|
||||||
|
" \"indices\": [\n" +
|
||||||
|
" {\n" +
|
||||||
|
" \"names\": [ \"index1\", \"index2\" ],\n" +
|
||||||
|
" \"privileges\": [\"all\"],\n" +
|
||||||
|
" \"field_security\" : { // optional\n" +
|
||||||
|
" \"grant\" : [ \"title\", \"body\" ]\n" +
|
||||||
|
" }\n" +
|
||||||
|
" }\n" +
|
||||||
|
" ],\n" +
|
||||||
|
" \"run_as\": [ \"other_user\" ],\n" +
|
||||||
|
" \"metadata\" : { // optional\n" +
|
||||||
|
" \"version\" : 1\n" +
|
||||||
|
" }\n" +
|
||||||
|
"}");
|
||||||
|
if (shouldSucceed) {
|
||||||
|
Response addRoleResponse = adminClient().performRequest(addRole);
|
||||||
|
assertThat(addRoleResponse.getStatusLine().getStatusCode(), equalTo(200));
|
||||||
|
} else {
|
||||||
|
ResponseException e = expectThrows(ResponseException.class, () -> adminClient().performRequest(addRole));
|
||||||
|
assertThat(e.getResponse().getStatusLine().getStatusCode(), equalTo(403));
|
||||||
|
assertThat(e.getMessage(), containsString("current license is non-compliant for [field and document level security]"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue