mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-03-26 01:48:45 +00:00
[PURIFY] Remove the AuthorizationEnginePlugin from examples. (#26)
Signed-off-by: Peter Nied <petern@amazon.com>
This commit is contained in:
parent
033f34cfdc
commit
38e9c9750a
plugins/examples/security-authorization-engine
build.gradle
src
javaRestTest/java/org/elasticsearch/example
main
java/org/elasticsearch/example
AuthorizationEnginePlugin.javaCustomAuthorizationEngine.javaExampleAuthorizationEngineExtension.java
resources/META-INF/services
@ -1,31 +0,0 @@
|
||||
// apply plugin: 'elasticsearch.esplugin'
|
||||
// apply plugin: 'elasticsearch.java-rest-test'
|
||||
|
||||
// esplugin {
|
||||
// name 'security-authorization-engine'
|
||||
// description 'An example spi extension plugin for security that implements an Authorization Engine'
|
||||
// classname 'org.elasticsearch.example.AuthorizationEnginePlugin'
|
||||
// licenseFile rootProject.file('licenses/APACHE-LICENSE-2.0.txt')
|
||||
// noticeFile rootProject.file('NOTICE.txt')
|
||||
// }
|
||||
|
||||
// dependencies {
|
||||
// // let the javaRestTest see the classpath of main
|
||||
// javaRestTestImplementation project.sourceSets.main.runtimeClasspath
|
||||
// }
|
||||
// //no unit tests
|
||||
// test.enabled = false
|
||||
// javaRestTest {
|
||||
// dependsOn buildZip
|
||||
// systemProperty 'tests.security.manager', 'false'
|
||||
// }
|
||||
|
||||
// testClusters.javaRestTest {
|
||||
// // This is important, so that all the modules are available too.
|
||||
// // There are index templates that use token filters that are in analysis-module and
|
||||
// // processors are being used that are in ingest-common module.
|
||||
// testDistribution = 'DEFAULT'
|
||||
|
||||
// user role: 'custom_superuser'
|
||||
// }
|
||||
|
@ -1,163 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.example;
|
||||
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.elasticsearch.client.Request;
|
||||
import org.elasticsearch.client.RequestOptions;
|
||||
import org.elasticsearch.client.Response;
|
||||
import org.elasticsearch.client.ResponseException;
|
||||
import org.elasticsearch.common.network.NetworkModule;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
import org.elasticsearch.test.ESIntegTestCase;
|
||||
import org.elasticsearch.xpack.core.XPackClientPlugin;
|
||||
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
|
||||
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.xpack.core.security.client.SecurityClient;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* Integration tests for the custom authorization engine. These tests are meant to be run against
|
||||
* an external cluster with the custom authorization plugin installed to validate the functionality
|
||||
* when running as a plugin
|
||||
*/
|
||||
public class CustomAuthorizationEngineIT extends ESIntegTestCase {
|
||||
|
||||
@Override
|
||||
protected Settings externalClusterClientSettings() {
|
||||
final String token = "Basic " +
|
||||
Base64.getEncoder().encodeToString(("test_user:x-pack-test-password").getBytes(StandardCharsets.UTF_8));
|
||||
return Settings.builder()
|
||||
.put(ThreadContext.PREFIX + ".Authorization", token)
|
||||
.put(NetworkModule.TRANSPORT_TYPE_KEY, "security4")
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<? extends Plugin>> transportClientPlugins() {
|
||||
return Collections.singleton(XPackClientPlugin.class);
|
||||
}
|
||||
|
||||
public void testClusterAction() throws IOException {
|
||||
SecurityClient securityClient = new SecurityClient(client());
|
||||
securityClient.preparePutUser("custom_user", "x-pack-test-password".toCharArray(), Hasher.BCRYPT, "custom_superuser").get();
|
||||
|
||||
{
|
||||
RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder();
|
||||
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("custom_user", new SecureString("x-pack-test-password".toCharArray())));
|
||||
Request request = new Request("GET", "_cluster/health");
|
||||
request.setOptions(options);
|
||||
Response response = getRestClient().performRequest(request);
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||
}
|
||||
|
||||
{
|
||||
securityClient.preparePutUser("custom_user2", "x-pack-test-password".toCharArray(), Hasher.BCRYPT, "not_superuser").get();
|
||||
RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder();
|
||||
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("custom_user2", new SecureString("x-pack-test-password".toCharArray())));
|
||||
Request request = new Request("GET", "_cluster/health");
|
||||
request.setOptions(options);
|
||||
ResponseException e = expectThrows(ResponseException.class, () -> getRestClient().performRequest(request));
|
||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||
}
|
||||
}
|
||||
|
||||
public void testIndexAction() throws IOException {
|
||||
SecurityClient securityClient = new SecurityClient(client());
|
||||
securityClient.preparePutUser("custom_user", "x-pack-test-password".toCharArray(), Hasher.BCRYPT, "custom_superuser").get();
|
||||
|
||||
{
|
||||
RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder();
|
||||
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("custom_user", new SecureString("x-pack-test-password".toCharArray())));
|
||||
Request request = new Request("PUT", "/index");
|
||||
request.setOptions(options);
|
||||
Response response = getRestClient().performRequest(request);
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||
}
|
||||
|
||||
{
|
||||
securityClient.preparePutUser("custom_user2", "x-pack-test-password".toCharArray(), Hasher.BCRYPT, "not_superuser").get();
|
||||
RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder();
|
||||
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("custom_user2", new SecureString("x-pack-test-password".toCharArray())));
|
||||
Request request = new Request("PUT", "/index");
|
||||
request.setOptions(options);
|
||||
ResponseException e = expectThrows(ResponseException.class, () -> getRestClient().performRequest(request));
|
||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRunAs() throws IOException {
|
||||
SecurityClient securityClient = new SecurityClient(client());
|
||||
securityClient.preparePutUser("custom_user", "x-pack-test-password".toCharArray(), Hasher.BCRYPT, "custom_superuser").get();
|
||||
securityClient.preparePutUser("custom_user2", "x-pack-test-password".toCharArray(), Hasher.BCRYPT, "custom_superuser").get();
|
||||
securityClient.preparePutUser("custom_user3", "x-pack-test-password".toCharArray(), Hasher.BCRYPT, "not_superuser").get();
|
||||
|
||||
{
|
||||
RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder();
|
||||
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("custom_user", new SecureString("x-pack-test-password".toCharArray())));
|
||||
options.addHeader("es-security-runas-user", "custom_user2");
|
||||
Request request = new Request("GET", "/_security/_authenticate");
|
||||
request.setOptions(options);
|
||||
Response response = getRestClient().performRequest(request);
|
||||
assertThat(response.getStatusLine().getStatusCode(), is(200));
|
||||
String responseStr = EntityUtils.toString(response.getEntity());
|
||||
assertThat(responseStr, containsString("custom_user2"));
|
||||
}
|
||||
|
||||
{
|
||||
RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder();
|
||||
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("custom_user", new SecureString("x-pack-test-password".toCharArray())));
|
||||
options.addHeader("es-security-runas-user", "custom_user3");
|
||||
Request request = new Request("PUT", "/index");
|
||||
request.setOptions(options);
|
||||
ResponseException e = expectThrows(ResponseException.class, () -> getRestClient().performRequest(request));
|
||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||
}
|
||||
|
||||
{
|
||||
RequestOptions.Builder options = RequestOptions.DEFAULT.toBuilder();
|
||||
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
|
||||
basicAuthHeaderValue("custom_user3", new SecureString("x-pack-test-password".toCharArray())));
|
||||
options.addHeader("es-security-runas-user", "custom_user2");
|
||||
Request request = new Request("PUT", "/index");
|
||||
request.setOptions(options);
|
||||
ResponseException e = expectThrows(ResponseException.class, () -> getRestClient().performRequest(request));
|
||||
assertThat(e.getResponse().getStatusLine().getStatusCode(), is(403));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.example;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.action.search.SearchRequest;
|
||||
import org.elasticsearch.action.support.PlainActionFuture;
|
||||
import org.elasticsearch.cluster.metadata.IndexAbstraction;
|
||||
import org.elasticsearch.cluster.metadata.IndexAbstraction.Index;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetadata;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.transport.TransportRequest;
|
||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||
import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
|
||||
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo;
|
||||
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationResult;
|
||||
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.IndexAuthorizationResult;
|
||||
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.RequestInfo;
|
||||
import org.elasticsearch.xpack.core.security.authz.ResolvedIndices;
|
||||
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
|
||||
import org.elasticsearch.xpack.core.security.user.User;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
/**
|
||||
* Unit tests for the custom authorization engine. These are basic tests that validate the
|
||||
* engine's functionality outside of being used by the AuthorizationService
|
||||
*/
|
||||
public class CustomAuthorizationEngineTests extends ESTestCase {
|
||||
|
||||
public void testGetAuthorizationInfo() {
|
||||
PlainActionFuture<AuthorizationInfo> future = new PlainActionFuture<>();
|
||||
CustomAuthorizationEngine engine = new CustomAuthorizationEngine();
|
||||
engine.resolveAuthorizationInfo(getRequestInfo(), future);
|
||||
assertNotNull(future.actionGet());
|
||||
}
|
||||
|
||||
public void testAuthorizeRunAs() {
|
||||
final String action = "cluster:monitor/foo";
|
||||
final TransportRequest request = new TransportRequest() {};
|
||||
CustomAuthorizationEngine engine = new CustomAuthorizationEngine();
|
||||
// unauthorized
|
||||
{
|
||||
Authentication authentication =
|
||||
new Authentication(new User("joe", new String[]{"custom_superuser"}, new User("bar", "not_superuser")),
|
||||
new RealmRef("test", "test", "node"), new RealmRef("test", "test", "node"));
|
||||
RequestInfo info = new RequestInfo(authentication, request, action);
|
||||
PlainActionFuture<AuthorizationInfo> future = new PlainActionFuture<>();
|
||||
engine.resolveAuthorizationInfo(info, future);
|
||||
AuthorizationInfo authzInfo = future.actionGet();
|
||||
|
||||
PlainActionFuture<AuthorizationResult> resultFuture = new PlainActionFuture<>();
|
||||
engine.authorizeRunAs(info, authzInfo, resultFuture);
|
||||
AuthorizationResult result = resultFuture.actionGet();
|
||||
assertThat(result.isGranted(), is(false));
|
||||
assertThat(result.isAuditable(), is(true));
|
||||
}
|
||||
|
||||
// authorized
|
||||
{
|
||||
Authentication authentication =
|
||||
new Authentication(new User("joe", new String[]{"not_superuser"}, new User("bar", "custom_superuser")),
|
||||
new RealmRef("test", "test", "node"), new RealmRef("test", "test", "node"));
|
||||
RequestInfo info = new RequestInfo(authentication, request, action);
|
||||
PlainActionFuture<AuthorizationInfo> future = new PlainActionFuture<>();
|
||||
engine.resolveAuthorizationInfo(info, future);
|
||||
AuthorizationInfo authzInfo = future.actionGet();
|
||||
PlainActionFuture<AuthorizationResult> resultFuture = new PlainActionFuture<>();
|
||||
engine.authorizeRunAs(info, authzInfo, resultFuture);
|
||||
AuthorizationResult result = resultFuture.actionGet();
|
||||
assertThat(result.isGranted(), is(true));
|
||||
assertThat(result.isAuditable(), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAuthorizeClusterAction() {
|
||||
CustomAuthorizationEngine engine = new CustomAuthorizationEngine();
|
||||
RequestInfo requestInfo = getRequestInfo();
|
||||
// authorized
|
||||
{
|
||||
PlainActionFuture<AuthorizationInfo> future = new PlainActionFuture<>();
|
||||
engine.resolveAuthorizationInfo(requestInfo, future);
|
||||
AuthorizationInfo authzInfo = future.actionGet();
|
||||
|
||||
PlainActionFuture<AuthorizationResult> resultFuture = new PlainActionFuture<>();
|
||||
engine.authorizeClusterAction(requestInfo, authzInfo, resultFuture);
|
||||
AuthorizationResult result = resultFuture.actionGet();
|
||||
assertThat(result.isGranted(), is(true));
|
||||
assertThat(result.isAuditable(), is(true));
|
||||
}
|
||||
|
||||
// unauthorized
|
||||
{
|
||||
RequestInfo unauthReqInfo =
|
||||
new RequestInfo(new Authentication(new User("joe", "not_superuser"), new RealmRef("test", "test", "node"), null),
|
||||
requestInfo.getRequest(), requestInfo.getAction());
|
||||
PlainActionFuture<AuthorizationInfo> future = new PlainActionFuture<>();
|
||||
engine.resolveAuthorizationInfo(unauthReqInfo, future);
|
||||
AuthorizationInfo authzInfo = future.actionGet();
|
||||
|
||||
PlainActionFuture<AuthorizationResult> resultFuture = new PlainActionFuture<>();
|
||||
engine.authorizeClusterAction(unauthReqInfo, authzInfo, resultFuture);
|
||||
AuthorizationResult result = resultFuture.actionGet();
|
||||
assertThat(result.isGranted(), is(false));
|
||||
assertThat(result.isAuditable(), is(true));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAuthorizeIndexAction() {
|
||||
CustomAuthorizationEngine engine = new CustomAuthorizationEngine();
|
||||
Map<String, IndexAbstraction> indicesMap = new HashMap<>();
|
||||
indicesMap.put("index", new Index(IndexMetadata.builder("index")
|
||||
.settings(Settings.builder().put("index.version.created", Version.CURRENT))
|
||||
.numberOfShards(1)
|
||||
.numberOfReplicas(0)
|
||||
.build()));
|
||||
// authorized
|
||||
{
|
||||
RequestInfo requestInfo =
|
||||
new RequestInfo(new Authentication(new User("joe", "custom_superuser"), new RealmRef("test", "test", "node"), null),
|
||||
new SearchRequest(), "indices:data/read/search");
|
||||
PlainActionFuture<AuthorizationInfo> future = new PlainActionFuture<>();
|
||||
engine.resolveAuthorizationInfo(requestInfo, future);
|
||||
AuthorizationInfo authzInfo = future.actionGet();
|
||||
|
||||
PlainActionFuture<IndexAuthorizationResult> resultFuture = new PlainActionFuture<>();
|
||||
engine.authorizeIndexAction(requestInfo, authzInfo,
|
||||
listener -> listener.onResponse(new ResolvedIndices(Collections.singletonList("index"), Collections.emptyList())),
|
||||
indicesMap, resultFuture);
|
||||
IndexAuthorizationResult result = resultFuture.actionGet();
|
||||
assertThat(result.isGranted(), is(true));
|
||||
assertThat(result.isAuditable(), is(true));
|
||||
IndicesAccessControl indicesAccessControl = result.getIndicesAccessControl();
|
||||
assertNotNull(indicesAccessControl.getIndexPermissions("index"));
|
||||
assertThat(indicesAccessControl.getIndexPermissions("index").isGranted(), is(true));
|
||||
}
|
||||
|
||||
// unauthorized
|
||||
{
|
||||
RequestInfo requestInfo =
|
||||
new RequestInfo(new Authentication(new User("joe", "not_superuser"), new RealmRef("test", "test", "node"), null),
|
||||
new SearchRequest(), "indices:data/read/search");
|
||||
PlainActionFuture<AuthorizationInfo> future = new PlainActionFuture<>();
|
||||
engine.resolveAuthorizationInfo(requestInfo, future);
|
||||
AuthorizationInfo authzInfo = future.actionGet();
|
||||
|
||||
PlainActionFuture<IndexAuthorizationResult> resultFuture = new PlainActionFuture<>();
|
||||
engine.authorizeIndexAction(requestInfo, authzInfo,
|
||||
listener -> listener.onResponse(new ResolvedIndices(Collections.singletonList("index"), Collections.emptyList())),
|
||||
indicesMap, resultFuture);
|
||||
IndexAuthorizationResult result = resultFuture.actionGet();
|
||||
assertThat(result.isGranted(), is(false));
|
||||
assertThat(result.isAuditable(), is(true));
|
||||
IndicesAccessControl indicesAccessControl = result.getIndicesAccessControl();
|
||||
assertNull(indicesAccessControl.getIndexPermissions("index"));
|
||||
}
|
||||
}
|
||||
|
||||
private RequestInfo getRequestInfo() {
|
||||
final String action = "cluster:monitor/foo";
|
||||
final TransportRequest request = new TransportRequest() {};
|
||||
final Authentication authentication =
|
||||
new Authentication(new User("joe", "custom_superuser"), new RealmRef("test", "test", "node"), null);
|
||||
return new RequestInfo(authentication, request, action);
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.example;
|
||||
|
||||
import org.elasticsearch.plugins.ActionPlugin;
|
||||
import org.elasticsearch.plugins.Plugin;
|
||||
|
||||
/**
|
||||
* Plugin class that is required so that the code contained here may be loaded as a plugin.
|
||||
* Additional items such as settings and actions can be registered using this plugin class.
|
||||
*/
|
||||
public class AuthorizationEnginePlugin extends Plugin implements ActionPlugin {
|
||||
}
|
@ -1,238 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.example;
|
||||
|
||||
import org.elasticsearch.action.ActionListener;
|
||||
import org.elasticsearch.cluster.metadata.IndexAbstraction;
|
||||
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesRequest;
|
||||
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesResponse;
|
||||
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesResponse.Indices;
|
||||
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesRequest;
|
||||
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesResponse;
|
||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
|
||||
import org.elasticsearch.xpack.core.security.authz.ResolvedIndices;
|
||||
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
|
||||
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor.IndicesPrivileges;
|
||||
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl;
|
||||
import org.elasticsearch.xpack.core.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
|
||||
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions;
|
||||
import org.elasticsearch.xpack.core.security.authz.permission.ResourcePrivileges;
|
||||
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilegeDescriptor;
|
||||
import org.elasticsearch.xpack.core.security.authz.privilege.ConfigurableClusterPrivilege;
|
||||
import org.elasticsearch.xpack.core.security.user.User;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A custom implementation of an authorization engine. This engine is extremely basic in that it
|
||||
* authorizes based upon the name of a single role. If users have this role they are granted access.
|
||||
*/
|
||||
public class CustomAuthorizationEngine implements AuthorizationEngine {
|
||||
|
||||
@Override
|
||||
public void resolveAuthorizationInfo(RequestInfo requestInfo, ActionListener<AuthorizationInfo> listener) {
|
||||
final Authentication authentication = requestInfo.getAuthentication();
|
||||
if (authentication.getUser().isRunAs()) {
|
||||
final CustomAuthorizationInfo authenticatedUserAuthzInfo =
|
||||
new CustomAuthorizationInfo(authentication.getUser().authenticatedUser().roles(), null);
|
||||
listener.onResponse(new CustomAuthorizationInfo(authentication.getUser().roles(), authenticatedUserAuthzInfo));
|
||||
} else {
|
||||
listener.onResponse(new CustomAuthorizationInfo(authentication.getUser().roles(), null));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authorizeRunAs(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, ActionListener<AuthorizationResult> listener) {
|
||||
if (isSuperuser(requestInfo.getAuthentication().getUser().authenticatedUser())) {
|
||||
listener.onResponse(AuthorizationResult.granted());
|
||||
} else {
|
||||
listener.onResponse(AuthorizationResult.deny());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authorizeClusterAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
|
||||
ActionListener<AuthorizationResult> listener) {
|
||||
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
|
||||
listener.onResponse(AuthorizationResult.granted());
|
||||
} else {
|
||||
listener.onResponse(AuthorizationResult.deny());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authorizeIndexAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
|
||||
AsyncSupplier<ResolvedIndices> indicesAsyncSupplier,
|
||||
Map<String, IndexAbstraction> aliasOrIndexLookup,
|
||||
ActionListener<IndexAuthorizationResult> listener) {
|
||||
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
|
||||
indicesAsyncSupplier.getAsync(ActionListener.wrap(resolvedIndices -> {
|
||||
Map<String, IndexAccessControl> indexAccessControlMap = new HashMap<>();
|
||||
for (String name : resolvedIndices.getLocal()) {
|
||||
indexAccessControlMap.put(name, new IndexAccessControl(true, FieldPermissions.DEFAULT, null));
|
||||
}
|
||||
IndicesAccessControl indicesAccessControl =
|
||||
new IndicesAccessControl(true, Collections.unmodifiableMap(indexAccessControlMap));
|
||||
listener.onResponse(new IndexAuthorizationResult(true, indicesAccessControl));
|
||||
}, listener::onFailure));
|
||||
} else {
|
||||
listener.onResponse(new IndexAuthorizationResult(true, IndicesAccessControl.DENIED));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadAuthorizedIndices(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
|
||||
Map<String, IndexAbstraction> indicesLookup, ActionListener<List<String>> listener) {
|
||||
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
|
||||
listener.onResponse(new ArrayList<>(indicesLookup.keySet()));
|
||||
} else {
|
||||
listener.onResponse(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateIndexPermissionsAreSubset(RequestInfo requestInfo, AuthorizationInfo authorizationInfo,
|
||||
Map<String, List<String>> indexNameToNewNames,
|
||||
ActionListener<AuthorizationResult> listener) {
|
||||
if (isSuperuser(requestInfo.getAuthentication().getUser())) {
|
||||
listener.onResponse(AuthorizationResult.granted());
|
||||
} else {
|
||||
listener.onResponse(AuthorizationResult.deny());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkPrivileges(Authentication authentication, AuthorizationInfo authorizationInfo,
|
||||
HasPrivilegesRequest hasPrivilegesRequest,
|
||||
Collection<ApplicationPrivilegeDescriptor> applicationPrivilegeDescriptors,
|
||||
ActionListener<HasPrivilegesResponse> listener) {
|
||||
if (isSuperuser(authentication.getUser())) {
|
||||
listener.onResponse(getHasPrivilegesResponse(authentication, hasPrivilegesRequest, true));
|
||||
} else {
|
||||
listener.onResponse(getHasPrivilegesResponse(authentication, hasPrivilegesRequest, false));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getUserPrivileges(Authentication authentication, AuthorizationInfo authorizationInfo, GetUserPrivilegesRequest request,
|
||||
ActionListener<GetUserPrivilegesResponse> listener) {
|
||||
if (isSuperuser(authentication.getUser())) {
|
||||
listener.onResponse(getUserPrivilegesResponse(true));
|
||||
} else {
|
||||
listener.onResponse(getUserPrivilegesResponse(false));
|
||||
}
|
||||
}
|
||||
|
||||
private HasPrivilegesResponse getHasPrivilegesResponse(Authentication authentication, HasPrivilegesRequest hasPrivilegesRequest,
|
||||
boolean authorized) {
|
||||
Map<String, Boolean> clusterPrivMap = new HashMap<>();
|
||||
for (String clusterPriv : hasPrivilegesRequest.clusterPrivileges()) {
|
||||
clusterPrivMap.put(clusterPriv, authorized);
|
||||
}
|
||||
final Map<String, ResourcePrivileges> indices = new LinkedHashMap<>();
|
||||
for (IndicesPrivileges check : hasPrivilegesRequest.indexPrivileges()) {
|
||||
for (String index : check.getIndices()) {
|
||||
final Map<String, Boolean> privileges = new HashMap<>();
|
||||
final ResourcePrivileges existing = indices.get(index);
|
||||
if (existing != null) {
|
||||
privileges.putAll(existing.getPrivileges());
|
||||
}
|
||||
for (String privilege : check.getPrivileges()) {
|
||||
privileges.put(privilege, authorized);
|
||||
}
|
||||
indices.put(index, ResourcePrivileges.builder(index).addPrivileges(privileges).build());
|
||||
}
|
||||
}
|
||||
final Map<String, Collection<ResourcePrivileges>> privilegesByApplication = new HashMap<>();
|
||||
Set<String> applicationNames = Arrays.stream(hasPrivilegesRequest.applicationPrivileges())
|
||||
.map(RoleDescriptor.ApplicationResourcePrivileges::getApplication)
|
||||
.collect(Collectors.toSet());
|
||||
for (String applicationName : applicationNames) {
|
||||
final Map<String, ResourcePrivileges> appPrivilegesByResource = new LinkedHashMap<>();
|
||||
for (RoleDescriptor.ApplicationResourcePrivileges p : hasPrivilegesRequest.applicationPrivileges()) {
|
||||
if (applicationName.equals(p.getApplication())) {
|
||||
for (String resource : p.getResources()) {
|
||||
final Map<String, Boolean> privileges = new HashMap<>();
|
||||
final ResourcePrivileges existing = appPrivilegesByResource.get(resource);
|
||||
if (existing != null) {
|
||||
privileges.putAll(existing.getPrivileges());
|
||||
}
|
||||
for (String privilege : p.getPrivileges()) {
|
||||
privileges.put(privilege, authorized);
|
||||
}
|
||||
appPrivilegesByResource.put(resource, ResourcePrivileges.builder(resource).addPrivileges(privileges).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
privilegesByApplication.put(applicationName, appPrivilegesByResource.values());
|
||||
}
|
||||
return new HasPrivilegesResponse(authentication.getUser().principal(), authorized, clusterPrivMap, indices.values(),
|
||||
privilegesByApplication);
|
||||
}
|
||||
|
||||
private GetUserPrivilegesResponse getUserPrivilegesResponse(boolean isSuperuser) {
|
||||
final Set<String> cluster = isSuperuser ? Collections.singleton("ALL") : Collections.emptySet();
|
||||
final Set<ConfigurableClusterPrivilege> conditionalCluster = Collections.emptySet();
|
||||
final Set<GetUserPrivilegesResponse.Indices> indices = isSuperuser ? Collections.singleton(new Indices(Collections.singleton("*"),
|
||||
Collections.singleton("*"), Collections.emptySet(), Collections.emptySet(), true)) : Collections.emptySet();
|
||||
|
||||
final Set<RoleDescriptor.ApplicationResourcePrivileges> application = isSuperuser ?
|
||||
Collections.singleton(
|
||||
RoleDescriptor.ApplicationResourcePrivileges.builder().application("*").privileges("*").resources("*").build()) :
|
||||
Collections.emptySet();
|
||||
final Set<String> runAs = isSuperuser ? Collections.singleton("*") : Collections.emptySet();
|
||||
return new GetUserPrivilegesResponse(cluster, conditionalCluster, indices, application, runAs);
|
||||
}
|
||||
|
||||
public static class CustomAuthorizationInfo implements AuthorizationInfo {
|
||||
|
||||
private final String[] roles;
|
||||
private final CustomAuthorizationInfo authenticatedAuthzInfo;
|
||||
|
||||
CustomAuthorizationInfo(String[] roles, CustomAuthorizationInfo authenticatedAuthzInfo) {
|
||||
this.roles = roles;
|
||||
this.authenticatedAuthzInfo = authenticatedAuthzInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> asMap() {
|
||||
return Collections.singletonMap("roles", roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAuthorizationInfo getAuthenticatedUserAuthorizationInfo() {
|
||||
return authenticatedAuthzInfo;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSuperuser(User user) {
|
||||
return Arrays.asList(user.roles()).contains("custom_superuser");
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Licensed to Elasticsearch under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.example;
|
||||
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.xpack.core.security.SecurityExtension;
|
||||
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
|
||||
|
||||
/**
|
||||
* Security extension class that registers the custom authorization engine to be used
|
||||
*/
|
||||
public class ExampleAuthorizationEngineExtension implements SecurityExtension {
|
||||
|
||||
@Override
|
||||
public AuthorizationEngine getAuthorizationEngine(Settings settings) {
|
||||
return new CustomAuthorizationEngine();
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
org.elasticsearch.example.ExampleAuthorizationEngineExtension
|
Loading…
x
Reference in New Issue
Block a user