Merge branch 'master' into enhancement/use_shard_bulk_for_single_ops
Original commit: elastic/x-pack-elasticsearch@98f4e74d2e
This commit is contained in:
commit
8326b6d83b
|
@ -241,7 +241,7 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Object> components = new ArrayList<>();
|
List<Object> components = new ArrayList<>();
|
||||||
final SecurityContext securityContext = new SecurityContext(settings, threadPool, cryptoService);
|
final SecurityContext securityContext = new SecurityContext(settings, threadPool.getThreadContext(), cryptoService);
|
||||||
components.add(securityContext);
|
components.add(securityContext);
|
||||||
|
|
||||||
// realms construction
|
// realms construction
|
||||||
|
|
|
@ -37,9 +37,9 @@ public class SecurityContext {
|
||||||
* If cryptoService is null, security is disabled and {@link #getUser()}
|
* If cryptoService is null, security is disabled and {@link #getUser()}
|
||||||
* and {@link #getAuthentication()} will always return null.
|
* and {@link #getAuthentication()} will always return null.
|
||||||
*/
|
*/
|
||||||
public SecurityContext(Settings settings, ThreadPool threadPool, CryptoService cryptoService) {
|
public SecurityContext(Settings settings, ThreadContext threadContext, CryptoService cryptoService) {
|
||||||
this.logger = Loggers.getLogger(getClass(), settings);
|
this.logger = Loggers.getLogger(getClass(), settings);
|
||||||
this.threadContext = threadPool.getThreadContext();
|
this.threadContext = threadContext;
|
||||||
this.cryptoService = cryptoService;
|
this.cryptoService = cryptoService;
|
||||||
this.signUserHeader = AuthenticationService.SIGN_USER_HEADER.get(settings);
|
this.signUserHeader = AuthenticationService.SIGN_USER_HEADER.get(settings);
|
||||||
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
|
||||||
|
|
|
@ -25,11 +25,13 @@ public class Authentication {
|
||||||
private final User user;
|
private final User user;
|
||||||
private final RealmRef authenticatedBy;
|
private final RealmRef authenticatedBy;
|
||||||
private final RealmRef lookedUpBy;
|
private final RealmRef lookedUpBy;
|
||||||
|
private final Version version;
|
||||||
|
|
||||||
public Authentication(User user, RealmRef authenticatedBy, RealmRef lookedUpBy) {
|
public Authentication(User user, RealmRef authenticatedBy, RealmRef lookedUpBy) {
|
||||||
this.user = Objects.requireNonNull(user);
|
this.user = Objects.requireNonNull(user);
|
||||||
this.authenticatedBy = Objects.requireNonNull(authenticatedBy);
|
this.authenticatedBy = Objects.requireNonNull(authenticatedBy);
|
||||||
this.lookedUpBy = lookedUpBy;
|
this.lookedUpBy = lookedUpBy;
|
||||||
|
this.version = Version.CURRENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Authentication(StreamInput in) throws IOException {
|
public Authentication(StreamInput in) throws IOException {
|
||||||
|
@ -40,6 +42,7 @@ public class Authentication {
|
||||||
} else {
|
} else {
|
||||||
this.lookedUpBy = null;
|
this.lookedUpBy = null;
|
||||||
}
|
}
|
||||||
|
this.version = in.getVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public User getUser() {
|
public User getUser() {
|
||||||
|
@ -70,6 +73,10 @@ public class Authentication {
|
||||||
return lookedUpBy;
|
return lookedUpBy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Version getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
public static Authentication readFromContext(ThreadContext ctx, CryptoService cryptoService, boolean sign)
|
public static Authentication readFromContext(ThreadContext ctx, CryptoService cryptoService, boolean sign)
|
||||||
throws IOException, IllegalArgumentException {
|
throws IOException, IllegalArgumentException {
|
||||||
Authentication authentication = ctx.getTransient(AUTHENTICATION_KEY);
|
Authentication authentication = ctx.getTransient(AUTHENTICATION_KEY);
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.elasticsearch.xpack.security.authz.RoleDescriptor;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.Role;
|
import org.elasticsearch.xpack.security.authz.permission.Role;
|
||||||
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||||
|
|
||||||
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
|
|
||||||
public class ReservedRolesStore {
|
public class ReservedRolesStore {
|
||||||
|
@ -53,7 +54,7 @@ public class ReservedRolesStore {
|
||||||
.put("reporting_user", new RoleDescriptor("reporting_user", null, new RoleDescriptor.IndicesPrivileges[] {
|
.put("reporting_user", new RoleDescriptor("reporting_user", null, new RoleDescriptor.IndicesPrivileges[] {
|
||||||
RoleDescriptor.IndicesPrivileges.builder().indices(".reporting-*").privileges("read", "write").build() },
|
RoleDescriptor.IndicesPrivileges.builder().indices(".reporting-*").privileges("read", "write").build() },
|
||||||
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
|
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
|
||||||
.put("kibana", new RoleDescriptor("kibana", new String[] { "monitor", MonitoringBulkAction.NAME},
|
.put(KibanaUser.ROLE_NAME, new RoleDescriptor(KibanaUser.ROLE_NAME, new String[] { "monitor", MonitoringBulkAction.NAME},
|
||||||
new RoleDescriptor.IndicesPrivileges[] {
|
new RoleDescriptor.IndicesPrivileges[] {
|
||||||
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*", ".reporting-*").privileges("all").build() },
|
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*", ".reporting-*").privileges("all").build() },
|
||||||
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
|
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.transport;
|
package org.elasticsearch.xpack.security.transport;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.support.DestructiveOperations;
|
import org.elasticsearch.action.support.DestructiveOperations;
|
||||||
import org.elasticsearch.common.CheckedConsumer;
|
import org.elasticsearch.common.CheckedConsumer;
|
||||||
|
@ -24,13 +25,16 @@ import org.elasticsearch.transport.TransportResponse;
|
||||||
import org.elasticsearch.transport.TransportResponseHandler;
|
import org.elasticsearch.transport.TransportResponseHandler;
|
||||||
import org.elasticsearch.transport.TransportService;
|
import org.elasticsearch.transport.TransportService;
|
||||||
import org.elasticsearch.transport.TransportSettings;
|
import org.elasticsearch.transport.TransportSettings;
|
||||||
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.security.SecurityContext;
|
import org.elasticsearch.xpack.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
|
import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
|
||||||
import org.elasticsearch.xpack.security.authz.accesscontrol.RequestContext;
|
import org.elasticsearch.xpack.security.authz.accesscontrol.RequestContext;
|
||||||
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport;
|
import org.elasticsearch.xpack.security.transport.netty4.SecurityNetty4Transport;
|
||||||
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.elasticsearch.xpack.ssl.SSLService;
|
import org.elasticsearch.xpack.ssl.SSLService;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -56,6 +60,7 @@ public class SecurityServerTransportInterceptor implements TransportInterceptor
|
||||||
private final ThreadPool threadPool;
|
private final ThreadPool threadPool;
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
private final SecurityContext securityContext;
|
private final SecurityContext securityContext;
|
||||||
|
private final boolean reservedRealmEnabled;
|
||||||
|
|
||||||
public SecurityServerTransportInterceptor(Settings settings,
|
public SecurityServerTransportInterceptor(Settings settings,
|
||||||
ThreadPool threadPool,
|
ThreadPool threadPool,
|
||||||
|
@ -73,6 +78,7 @@ public class SecurityServerTransportInterceptor implements TransportInterceptor
|
||||||
this.sslService = sslService;
|
this.sslService = sslService;
|
||||||
this.securityContext = securityContext;
|
this.securityContext = securityContext;
|
||||||
this.profileFilters = initializeProfileFilters(destructiveOperations);
|
this.profileFilters = initializeProfileFilters(destructiveOperations);
|
||||||
|
this.reservedRealmEnabled = XPackSettings.RESERVED_REALM_ENABLED_SETTING.get(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,6 +93,13 @@ public class SecurityServerTransportInterceptor implements TransportInterceptor
|
||||||
if (AuthorizationUtils.shouldReplaceUserWithSystem(threadPool.getThreadContext(), action)) {
|
if (AuthorizationUtils.shouldReplaceUserWithSystem(threadPool.getThreadContext(), action)) {
|
||||||
securityContext.executeAsUser(SystemUser.INSTANCE, (original) -> sendWithUser(connection, action, request, options,
|
securityContext.executeAsUser(SystemUser.INSTANCE, (original) -> sendWithUser(connection, action, request, options,
|
||||||
new ContextRestoreResponseHandler<>(threadPool.getThreadContext(), original, handler), sender));
|
new ContextRestoreResponseHandler<>(threadPool.getThreadContext(), original, handler), sender));
|
||||||
|
} else if (reservedRealmEnabled && connection.getVersion().before(Version.V_5_2_0_UNRELEASED) &&
|
||||||
|
KibanaUser.NAME.equals(securityContext.getUser().principal())) {
|
||||||
|
final User kibanaUser = securityContext.getUser();
|
||||||
|
final User bwcKibanaUser = new User(kibanaUser.principal(), new String[] { "kibana" }, kibanaUser.fullName(),
|
||||||
|
kibanaUser.email(), kibanaUser.metadata(), kibanaUser.enabled());
|
||||||
|
securityContext.executeAsUser(bwcKibanaUser, (original) -> sendWithUser(connection, action, request, options,
|
||||||
|
new ContextRestoreResponseHandler<>(threadPool.getThreadContext(), original, handler), sender));
|
||||||
} else {
|
} else {
|
||||||
sendWithUser(connection, action, request, options, handler, sender);
|
sendWithUser(connection, action, request, options, handler, sender);
|
||||||
}
|
}
|
||||||
|
@ -134,11 +147,13 @@ public class SecurityServerTransportInterceptor implements TransportInterceptor
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "client":
|
case "client":
|
||||||
profileFilters.put(entry.getKey(), new ServerTransportFilter.ClientProfile(authcService, authzService,
|
profileFilters.put(entry.getKey(), new ServerTransportFilter.ClientProfile(authcService, authzService,
|
||||||
threadPool.getThreadContext(), extractClientCert, destructiveOperations));
|
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled,
|
||||||
|
securityContext));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
profileFilters.put(entry.getKey(), new ServerTransportFilter.NodeProfile(authcService, authzService,
|
profileFilters.put(entry.getKey(), new ServerTransportFilter.NodeProfile(authcService, authzService,
|
||||||
threadPool.getThreadContext(), extractClientCert, destructiveOperations));
|
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled,
|
||||||
|
securityContext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +162,7 @@ public class SecurityServerTransportInterceptor implements TransportInterceptor
|
||||||
final boolean clientAuth = sslService.isSSLClientAuthEnabled(transportSSLSettings);
|
final boolean clientAuth = sslService.isSSLClientAuthEnabled(transportSSLSettings);
|
||||||
final boolean extractClientCert = profileSsl && clientAuth;
|
final boolean extractClientCert = profileSsl && clientAuth;
|
||||||
profileFilters.put(TransportSettings.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService,
|
profileFilters.put(TransportSettings.DEFAULT_PROFILE, new ServerTransportFilter.NodeProfile(authcService, authzService,
|
||||||
threadPool.getThreadContext(), extractClientCert, destructiveOperations));
|
threadPool.getThreadContext(), extractClientCert, destructiveOperations, reservedRealmEnabled, securityContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Collections.unmodifiableMap(profileFilters);
|
return Collections.unmodifiableMap(profileFilters);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import io.netty.handler.ssl.SslHandler;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.logging.log4j.message.ParameterizedMessage;
|
import org.apache.logging.log4j.message.ParameterizedMessage;
|
||||||
import org.apache.logging.log4j.util.Supplier;
|
import org.apache.logging.log4j.util.Supplier;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.IndicesRequest;
|
import org.elasticsearch.action.IndicesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.close.CloseIndexAction;
|
import org.elasticsearch.action.admin.indices.close.CloseIndexAction;
|
||||||
|
@ -22,11 +23,16 @@ import org.elasticsearch.transport.DelegatingTransportChannel;
|
||||||
import org.elasticsearch.transport.TcpTransportChannel;
|
import org.elasticsearch.transport.TcpTransportChannel;
|
||||||
import org.elasticsearch.transport.TransportChannel;
|
import org.elasticsearch.transport.TransportChannel;
|
||||||
import org.elasticsearch.transport.TransportRequest;
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
|
import org.elasticsearch.xpack.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
|
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
|
||||||
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
|
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
|
import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
|
||||||
|
import org.elasticsearch.xpack.security.transport.SecurityServerTransportInterceptor.ContextRestoreResponseHandler;
|
||||||
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException;
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
||||||
|
@ -65,14 +71,19 @@ public interface ServerTransportFilter {
|
||||||
private final ThreadContext threadContext;
|
private final ThreadContext threadContext;
|
||||||
private final boolean extractClientCert;
|
private final boolean extractClientCert;
|
||||||
private final DestructiveOperations destructiveOperations;
|
private final DestructiveOperations destructiveOperations;
|
||||||
|
private final boolean reservedRealmEnabled;
|
||||||
|
private final SecurityContext securityContext;
|
||||||
|
|
||||||
public NodeProfile(AuthenticationService authcService, AuthorizationService authzService,
|
NodeProfile(AuthenticationService authcService, AuthorizationService authzService,
|
||||||
ThreadContext threadContext, boolean extractClientCert, DestructiveOperations destructiveOperations) {
|
ThreadContext threadContext, boolean extractClientCert, DestructiveOperations destructiveOperations,
|
||||||
|
boolean reservedRealmEnabled, SecurityContext securityContext) {
|
||||||
this.authcService = authcService;
|
this.authcService = authcService;
|
||||||
this.authzService = authzService;
|
this.authzService = authzService;
|
||||||
this.threadContext = threadContext;
|
this.threadContext = threadContext;
|
||||||
this.extractClientCert = extractClientCert;
|
this.extractClientCert = extractClientCert;
|
||||||
this.destructiveOperations = destructiveOperations;
|
this.destructiveOperations = destructiveOperations;
|
||||||
|
this.reservedRealmEnabled = reservedRealmEnabled;
|
||||||
|
this.securityContext = securityContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -112,12 +123,31 @@ public interface ServerTransportFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
authcService.authenticate(securityAction, request, null, ActionListener.wrap((authentication) -> {
|
authcService.authenticate(securityAction, request, null, ActionListener.wrap((authentication) -> {
|
||||||
|
if (reservedRealmEnabled && authentication.getVersion().before(Version.V_5_2_0_UNRELEASED)
|
||||||
|
&& KibanaUser.NAME.equals(authentication.getUser().principal())) {
|
||||||
|
// the authentication came from an older node - so let's replace the user with our version
|
||||||
|
final User kibanaUser = new KibanaUser(authentication.getUser().enabled());
|
||||||
|
if (kibanaUser.enabled()) {
|
||||||
|
securityContext.executeAsUser(kibanaUser, (original) -> {
|
||||||
|
final Authentication replacedUserAuth = Authentication.getAuthentication(threadContext);
|
||||||
|
final AuthorizationUtils.AsyncAuthorizer asyncAuthorizer =
|
||||||
|
new AuthorizationUtils.AsyncAuthorizer(replacedUserAuth, listener, (userRoles, runAsRoles) -> {
|
||||||
|
authzService.authorize(replacedUserAuth, securityAction, request, userRoles, runAsRoles);
|
||||||
|
listener.onResponse(null);
|
||||||
|
});
|
||||||
|
asyncAuthorizer.authorize(authzService);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("a disabled user should never be sent. " + kibanaUser);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
final AuthorizationUtils.AsyncAuthorizer asyncAuthorizer =
|
final AuthorizationUtils.AsyncAuthorizer asyncAuthorizer =
|
||||||
new AuthorizationUtils.AsyncAuthorizer(authentication, listener, (userRoles, runAsRoles) -> {
|
new AuthorizationUtils.AsyncAuthorizer(authentication, listener, (userRoles, runAsRoles) -> {
|
||||||
authzService.authorize(authentication, securityAction, request, userRoles, runAsRoles);
|
authzService.authorize(authentication, securityAction, request, userRoles, runAsRoles);
|
||||||
listener.onResponse(null);
|
listener.onResponse(null);
|
||||||
});
|
});
|
||||||
asyncAuthorizer.authorize(authzService);
|
asyncAuthorizer.authorize(authzService);
|
||||||
|
}
|
||||||
}, listener::onFailure));
|
}, listener::onFailure));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,9 +181,11 @@ public interface ServerTransportFilter {
|
||||||
*/
|
*/
|
||||||
class ClientProfile extends NodeProfile {
|
class ClientProfile extends NodeProfile {
|
||||||
|
|
||||||
public ClientProfile(AuthenticationService authcService, AuthorizationService authzService,
|
ClientProfile(AuthenticationService authcService, AuthorizationService authzService,
|
||||||
ThreadContext threadContext, boolean extractClientCert, DestructiveOperations destructiveOperations) {
|
ThreadContext threadContext, boolean extractClientCert, DestructiveOperations destructiveOperations,
|
||||||
super(authcService, authzService, threadContext, extractClientCert, destructiveOperations);
|
boolean reservedRealmEnabled, SecurityContext securityContext) {
|
||||||
|
super(authcService, authzService, threadContext, extractClientCert, destructiveOperations, reservedRealmEnabled,
|
||||||
|
securityContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -13,7 +13,7 @@ import org.elasticsearch.xpack.security.support.MetadataUtils;
|
||||||
public class KibanaUser extends User {
|
public class KibanaUser extends User {
|
||||||
|
|
||||||
public static final String NAME = "kibana";
|
public static final String NAME = "kibana";
|
||||||
public static final String ROLE_NAME = "kibana";
|
public static final String ROLE_NAME = "kibana_system";
|
||||||
|
|
||||||
public KibanaUser(boolean enabled) {
|
public KibanaUser(boolean enabled) {
|
||||||
super(NAME, new String[]{ ROLE_NAME }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, enabled);
|
super(NAME, new String[]{ ROLE_NAME }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, enabled);
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class WatcherSearchTemplateService extends AbstractComponent {
|
||||||
if (source != null && source.length() > 0) {
|
if (source != null && source.length() > 0) {
|
||||||
try (XContentParser parser = XContentFactory.xContent(source).createParser(xContentRegistry, source)) {
|
try (XContentParser parser = XContentFactory.xContent(source).createParser(xContentRegistry, source)) {
|
||||||
sourceBuilder.parseXContent(new QueryParseContext(parser, parseFieldMatcher),
|
sourceBuilder.parseXContent(new QueryParseContext(parser, parseFieldMatcher),
|
||||||
searchRequestParsers.aggParsers, searchRequestParsers.suggesters);
|
searchRequestParsers.suggesters);
|
||||||
searchRequest.source(sourceBuilder);
|
searchRequest.source(sourceBuilder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||||
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
|
import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication;
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
|
@ -22,9 +21,6 @@ import org.junit.Before;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class SecurityContextTests extends ESTestCase {
|
public class SecurityContextTests extends ESTestCase {
|
||||||
|
|
||||||
private boolean signHeader;
|
private boolean signHeader;
|
||||||
|
@ -42,9 +38,7 @@ public class SecurityContextTests extends ESTestCase {
|
||||||
.build();
|
.build();
|
||||||
threadContext = new ThreadContext(settings);
|
threadContext = new ThreadContext(settings);
|
||||||
cryptoService = new CryptoService(settings, new Environment(settings));
|
cryptoService = new CryptoService(settings, new Environment(settings));
|
||||||
ThreadPool threadPool = mock(ThreadPool.class);
|
securityContext = new SecurityContext(settings, threadContext, cryptoService);
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
|
||||||
securityContext = new SecurityContext(settings, threadPool, cryptoService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetAuthenticationAndUserInEmptyContext() throws IOException {
|
public void testGetAuthenticationAndUserInEmptyContext() throws IOException {
|
||||||
|
|
|
@ -421,7 +421,7 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
// the system role will always be checked first
|
// the system role will always be checked first
|
||||||
assertThat(events.get(0), containsString("Role [_system] is reserved"));
|
assertThat(events.get(0), containsString("Role [_system] is reserved"));
|
||||||
assertThat(events.get(1), containsString("Role [superuser] is reserved"));
|
assertThat(events.get(1), containsString("Role [superuser] is reserved"));
|
||||||
assertThat(events.get(2), containsString("Role [kibana] is reserved"));
|
assertThat(events.get(2), containsString("Role [kibana_system] is reserved"));
|
||||||
assertThat(events.get(3), containsString("Role [transport_client] is reserved"));
|
assertThat(events.get(3), containsString("Role [transport_client] is reserved"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ import static org.hamcrest.Matchers.is;
|
||||||
public class ReservedRolesStoreTests extends ESTestCase {
|
public class ReservedRolesStoreTests extends ESTestCase {
|
||||||
|
|
||||||
public void testIsReserved() {
|
public void testIsReserved() {
|
||||||
assertThat(ReservedRolesStore.isReserved("kibana"), is(true));
|
assertThat(ReservedRolesStore.isReserved("kibana_system"), is(true));
|
||||||
assertThat(ReservedRolesStore.isReserved("superuser"), is(true));
|
assertThat(ReservedRolesStore.isReserved("superuser"), is(true));
|
||||||
assertThat(ReservedRolesStore.isReserved("foobar"), is(false));
|
assertThat(ReservedRolesStore.isReserved("foobar"), is(false));
|
||||||
assertThat(ReservedRolesStore.isReserved(SystemUser.ROLE_NAME), is(true));
|
assertThat(ReservedRolesStore.isReserved(SystemUser.ROLE_NAME), is(true));
|
||||||
|
@ -92,7 +92,7 @@ public class ReservedRolesStoreTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testKibanaRole() {
|
public void testKibanaRole() {
|
||||||
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("kibana");
|
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("kibana_system");
|
||||||
assertNotNull(roleDescriptor);
|
assertNotNull(roleDescriptor);
|
||||||
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
|
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.xpack.security.transport;
|
package org.elasticsearch.xpack.security.transport;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.support.DestructiveOperations;
|
import org.elasticsearch.action.support.DestructiveOperations;
|
||||||
import org.elasticsearch.common.settings.ClusterSettings;
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
@ -21,6 +22,7 @@ import org.elasticsearch.transport.TransportRequestOptions;
|
||||||
import org.elasticsearch.transport.TransportResponse;
|
import org.elasticsearch.transport.TransportResponse;
|
||||||
import org.elasticsearch.transport.TransportResponse.Empty;
|
import org.elasticsearch.transport.TransportResponse.Empty;
|
||||||
import org.elasticsearch.transport.TransportResponseHandler;
|
import org.elasticsearch.transport.TransportResponseHandler;
|
||||||
|
import org.elasticsearch.xpack.XPackSettings;
|
||||||
import org.elasticsearch.xpack.security.SecurityContext;
|
import org.elasticsearch.xpack.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication;
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
||||||
|
@ -28,6 +30,7 @@ import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
||||||
import org.elasticsearch.xpack.security.transport.SecurityServerTransportInterceptor.ContextRestoreResponseHandler;
|
import org.elasticsearch.xpack.security.transport.SecurityServerTransportInterceptor.ContextRestoreResponseHandler;
|
||||||
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.elasticsearch.xpack.ssl.SSLService;
|
import org.elasticsearch.xpack.ssl.SSLService;
|
||||||
|
@ -37,10 +40,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.arrayContaining;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
|
@ -63,7 +68,7 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
||||||
threadContext = new ThreadContext(settings);
|
threadContext = new ThreadContext(settings);
|
||||||
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
when(threadPool.getThreadContext()).thenReturn(threadContext);
|
||||||
cryptoService = new CryptoService(settings, new Environment(settings));
|
cryptoService = new CryptoService(settings, new Environment(settings));
|
||||||
securityContext = spy(new SecurityContext(settings, threadPool, cryptoService));
|
securityContext = spy(new SecurityContext(settings, threadPool.getThreadContext(), cryptoService));
|
||||||
xPackLicenseState = mock(XPackLicenseState.class);
|
xPackLicenseState = mock(XPackLicenseState.class);
|
||||||
when(xPackLicenseState.isAuthAllowed()).thenReturn(true);
|
when(xPackLicenseState.isAuthAllowed()).thenReturn(true);
|
||||||
}
|
}
|
||||||
|
@ -112,7 +117,9 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
||||||
sendingUser.set(securityContext.getUser());
|
sendingUser.set(securityContext.getUser());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sender.sendRequest(null, "indices:foo", null, null, null);
|
Transport.Connection connection = mock(Transport.Connection.class);
|
||||||
|
when(connection.getVersion()).thenReturn(Version.CURRENT);
|
||||||
|
sender.sendRequest(connection, "indices:foo", null, null, null);
|
||||||
assertTrue(calledWrappedSender.get());
|
assertTrue(calledWrappedSender.get());
|
||||||
assertEquals(user, sendingUser.get());
|
assertEquals(user, sendingUser.get());
|
||||||
assertEquals(user, securityContext.getUser());
|
assertEquals(user, securityContext.getUser());
|
||||||
|
@ -168,8 +175,10 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
||||||
fail("sender should not be called!");
|
fail("sender should not be called!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Transport.Connection connection = mock(Transport.Connection.class);
|
||||||
|
when(connection.getVersion()).thenReturn(Version.CURRENT);
|
||||||
IllegalStateException e =
|
IllegalStateException e =
|
||||||
expectThrows(IllegalStateException.class, () -> sender.sendRequest(null, "indices:foo", null, null, null));
|
expectThrows(IllegalStateException.class, () -> sender.sendRequest(connection, "indices:foo", null, null, null));
|
||||||
assertEquals("there should always be a user when sending a message", e.getMessage());
|
assertEquals("there should always be a user when sending a message", e.getMessage());
|
||||||
assertNull(securityContext.getUser());
|
assertNull(securityContext.getUser());
|
||||||
verify(xPackLicenseState).isAuthAllowed();
|
verify(xPackLicenseState).isAuthAllowed();
|
||||||
|
@ -177,6 +186,66 @@ public class SecurityServerTransportInterceptorTests extends ESTestCase {
|
||||||
verifyNoMoreInteractions(xPackLicenseState);
|
verifyNoMoreInteractions(xPackLicenseState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testSendWithKibanaUser() throws Exception {
|
||||||
|
final User user = new KibanaUser(true);
|
||||||
|
final Authentication authentication = new Authentication(user, new RealmRef("reserved", "reserved", "node1"), null);
|
||||||
|
authentication.writeToContext(threadContext, cryptoService, AuthenticationService.SIGN_USER_HEADER.get(settings));
|
||||||
|
threadContext.putTransient(AuthorizationService.ORIGINATING_ACTION_KEY, "indices:foo");
|
||||||
|
|
||||||
|
SecurityServerTransportInterceptor interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
||||||
|
mock(AuthenticationService.class), mock(AuthorizationService.class), xPackLicenseState, mock(SSLService.class),
|
||||||
|
securityContext, new DestructiveOperations(Settings.EMPTY, new ClusterSettings(Settings.EMPTY,
|
||||||
|
Collections.singleton(DestructiveOperations.REQUIRES_NAME_SETTING))));
|
||||||
|
|
||||||
|
AtomicBoolean calledWrappedSender = new AtomicBoolean(false);
|
||||||
|
AtomicReference<User> sendingUser = new AtomicReference<>();
|
||||||
|
AsyncSender intercepted = new AsyncSender() {
|
||||||
|
@Override
|
||||||
|
public <T extends TransportResponse> void sendRequest(Transport.Connection connection, String action, TransportRequest request,
|
||||||
|
TransportRequestOptions options, TransportResponseHandler<T> handler) {
|
||||||
|
if (calledWrappedSender.compareAndSet(false, true) == false) {
|
||||||
|
fail("sender called more than once!");
|
||||||
|
}
|
||||||
|
sendingUser.set(securityContext.getUser());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
AsyncSender sender = interceptor.interceptSender(intercepted);
|
||||||
|
Transport.Connection connection = mock(Transport.Connection.class);
|
||||||
|
when(connection.getVersion()).thenReturn(Version.fromId(randomIntBetween(Version.V_5_0_0_ID, Version.V_5_2_0_ID_UNRELEASED - 100)));
|
||||||
|
sender.sendRequest(connection, "indices:foo[s]", null, null, null);
|
||||||
|
assertTrue(calledWrappedSender.get());
|
||||||
|
assertNotEquals(user, sendingUser.get());
|
||||||
|
assertEquals(KibanaUser.NAME, sendingUser.get().principal());
|
||||||
|
assertThat(sendingUser.get().roles(), arrayContaining("kibana"));
|
||||||
|
assertEquals(user, securityContext.getUser());
|
||||||
|
|
||||||
|
// reset and test with version that was changed
|
||||||
|
calledWrappedSender.set(false);
|
||||||
|
sendingUser.set(null);
|
||||||
|
when(connection.getVersion()).thenReturn(Version.V_5_2_0_UNRELEASED);
|
||||||
|
sender.sendRequest(connection, "indices:foo[s]", null, null, null);
|
||||||
|
assertTrue(calledWrappedSender.get());
|
||||||
|
assertEquals(user, sendingUser.get());
|
||||||
|
|
||||||
|
// reset and disable reserved realm
|
||||||
|
calledWrappedSender.set(false);
|
||||||
|
sendingUser.set(null);
|
||||||
|
when(connection.getVersion()).thenReturn(Version.V_5_0_0);
|
||||||
|
settings = Settings.builder().put(settings).put(XPackSettings.RESERVED_REALM_ENABLED_SETTING.getKey(), false).build();
|
||||||
|
interceptor = new SecurityServerTransportInterceptor(settings, threadPool,
|
||||||
|
mock(AuthenticationService.class), mock(AuthorizationService.class), xPackLicenseState, mock(SSLService.class),
|
||||||
|
securityContext, new DestructiveOperations(Settings.EMPTY, new ClusterSettings(Settings.EMPTY,
|
||||||
|
Collections.singleton(DestructiveOperations.REQUIRES_NAME_SETTING))));
|
||||||
|
sender = interceptor.interceptSender(intercepted);
|
||||||
|
sender.sendRequest(connection, "indices:foo[s]", null, null, null);
|
||||||
|
assertTrue(calledWrappedSender.get());
|
||||||
|
assertEquals(user, sendingUser.get());
|
||||||
|
|
||||||
|
verify(xPackLicenseState, times(3)).isAuthAllowed();
|
||||||
|
verify(securityContext, times(1)).executeAsUser(any(User.class), any(Consumer.class));
|
||||||
|
verifyNoMoreInteractions(xPackLicenseState);
|
||||||
|
}
|
||||||
|
|
||||||
public void testContextRestoreResponseHandler() throws Exception {
|
public void testContextRestoreResponseHandler() throws Exception {
|
||||||
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
|
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.xpack.security.transport;
|
package org.elasticsearch.xpack.security.transport;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchSecurityException;
|
import org.elasticsearch.ElasticsearchSecurityException;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.MockIndicesRequest;
|
import org.elasticsearch.action.MockIndicesRequest;
|
||||||
import org.elasticsearch.action.admin.indices.close.CloseIndexAction;
|
import org.elasticsearch.action.admin.indices.close.CloseIndexAction;
|
||||||
|
@ -17,26 +18,33 @@ import org.elasticsearch.action.support.PlainActionFuture;
|
||||||
import org.elasticsearch.common.settings.ClusterSettings;
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
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;
|
||||||
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.transport.TransportChannel;
|
import org.elasticsearch.transport.TransportChannel;
|
||||||
import org.elasticsearch.transport.TransportRequest;
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
import org.elasticsearch.transport.TransportSettings;
|
import org.elasticsearch.transport.TransportSettings;
|
||||||
|
import org.elasticsearch.xpack.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication;
|
import org.elasticsearch.xpack.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
import org.elasticsearch.xpack.security.authz.permission.Role;
|
import org.elasticsearch.xpack.security.authz.permission.Role;
|
||||||
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
|
||||||
|
import org.elasticsearch.xpack.security.crypto.CryptoService;
|
||||||
|
import org.elasticsearch.xpack.security.user.KibanaUser;
|
||||||
import org.elasticsearch.xpack.security.user.SystemUser;
|
import org.elasticsearch.xpack.security.user.SystemUser;
|
||||||
import org.elasticsearch.xpack.security.user.User;
|
import org.elasticsearch.xpack.security.user.User;
|
||||||
import org.elasticsearch.xpack.security.user.XPackUser;
|
import org.elasticsearch.xpack.security.user.XPackUser;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static org.elasticsearch.mock.orig.Mockito.times;
|
import static org.elasticsearch.mock.orig.Mockito.times;
|
||||||
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
|
import static org.elasticsearch.xpack.security.support.Exceptions.authenticationError;
|
||||||
import static org.elasticsearch.xpack.security.support.Exceptions.authorizationError;
|
import static org.elasticsearch.xpack.security.support.Exceptions.authorizationError;
|
||||||
|
import static org.hamcrest.Matchers.arrayContaining;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
|
@ -50,6 +58,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class ServerTransportFilterTests extends ESTestCase {
|
public class ServerTransportFilterTests extends ESTestCase {
|
||||||
|
|
||||||
private AuthenticationService authcService;
|
private AuthenticationService authcService;
|
||||||
private AuthorizationService authzService;
|
private AuthorizationService authzService;
|
||||||
private TransportChannel channel;
|
private TransportChannel channel;
|
||||||
|
@ -72,6 +81,7 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
public void testInbound() throws Exception {
|
public void testInbound() throws Exception {
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
Authentication authentication = mock(Authentication.class);
|
Authentication authentication = mock(Authentication.class);
|
||||||
|
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
||||||
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
||||||
when(authentication.getRunAsUser()).thenReturn(SystemUser.INSTANCE);
|
when(authentication.getRunAsUser()).thenReturn(SystemUser.INSTANCE);
|
||||||
doAnswer((i) -> {
|
doAnswer((i) -> {
|
||||||
|
@ -93,6 +103,7 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()),
|
IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()),
|
||||||
randomFrom("*", "_all", "test*"));
|
randomFrom("*", "_all", "test*"));
|
||||||
Authentication authentication = mock(Authentication.class);
|
Authentication authentication = mock(Authentication.class);
|
||||||
|
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
||||||
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
||||||
doAnswer((i) -> {
|
doAnswer((i) -> {
|
||||||
ActionListener callback =
|
ActionListener callback =
|
||||||
|
@ -149,6 +160,7 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
callback.onResponse(empty);
|
callback.onResponse(empty);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authzService).roles(any(User.class), any(ActionListener.class));
|
}).when(authzService).roles(any(User.class), any(ActionListener.class));
|
||||||
|
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
||||||
when(authentication.getUser()).thenReturn(XPackUser.INSTANCE);
|
when(authentication.getUser()).thenReturn(XPackUser.INSTANCE);
|
||||||
when(authentication.getRunAsUser()).thenReturn(XPackUser.INSTANCE);
|
when(authentication.getRunAsUser()).thenReturn(XPackUser.INSTANCE);
|
||||||
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
||||||
|
@ -163,7 +175,7 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
|
|
||||||
public void testClientProfileRejectsNodeActions() throws Exception {
|
public void testClientProfileRejectsNodeActions() throws Exception {
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
ServerTransportFilter filter = getClientFilter();
|
ServerTransportFilter filter = getClientFilter(true);
|
||||||
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class,
|
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class,
|
||||||
() -> filter.inbound("internal:foo/bar", request, channel, new PlainActionFuture<>()));
|
() -> filter.inbound("internal:foo/bar", request, channel, new PlainActionFuture<>()));
|
||||||
assertEquals("executing internal/shard actions is considered malicious and forbidden", e.getMessage());
|
assertEquals("executing internal/shard actions is considered malicious and forbidden", e.getMessage());
|
||||||
|
@ -177,7 +189,7 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
public void testNodeProfileAllowsNodeActions() throws Exception {
|
public void testNodeProfileAllowsNodeActions() throws Exception {
|
||||||
final String internalAction = "internal:foo/bar";
|
final String internalAction = "internal:foo/bar";
|
||||||
final String nodeOrShardAction = "indices:action" + randomFrom("[s]", "[p]", "[r]", "[n]", "[s][p]", "[s][r]", "[f]");
|
final String nodeOrShardAction = "indices:action" + randomFrom("[s]", "[p]", "[r]", "[n]", "[s][p]", "[s][r]", "[f]");
|
||||||
ServerTransportFilter filter = getNodeFilter();
|
ServerTransportFilter filter = getNodeFilter(true);
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
Authentication authentication = new Authentication(new User("test", "superuser"), new RealmRef("test", "test", "node1"), null);
|
Authentication authentication = new Authentication(new User("test", "superuser"), new RealmRef("test", "test", "node1"), null);
|
||||||
doAnswer((i) -> {
|
doAnswer((i) -> {
|
||||||
|
@ -211,17 +223,65 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
verifyNoMoreInteractions(authcService, authzService);
|
verifyNoMoreInteractions(authcService, authzService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServerTransportFilter getClientOrNodeFilter() {
|
public void testHandlesKibanaUserCompatibility() throws Exception {
|
||||||
return randomBoolean() ? getNodeFilter() : getClientFilter();
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
|
User user = new User("kibana", "kibana");
|
||||||
|
Authentication authentication = mock(Authentication.class);
|
||||||
|
when(authentication.getVersion())
|
||||||
|
.thenReturn(Version.fromId(randomIntBetween(Version.V_5_0_0_ID, Version.V_5_2_0_ID_UNRELEASED - 100)));
|
||||||
|
when(authentication.getUser()).thenReturn(user);
|
||||||
|
when(authentication.getRunAsUser()).thenReturn(user);
|
||||||
|
doAnswer((i) -> {
|
||||||
|
ActionListener callback =
|
||||||
|
(ActionListener) i.getArguments()[3];
|
||||||
|
callback.onResponse(authentication);
|
||||||
|
return Void.TYPE;
|
||||||
|
}).when(authcService).authenticate(eq("_action"), eq(request), eq(null), any(ActionListener.class));
|
||||||
|
AtomicReference<String[]> rolesRef = new AtomicReference<>();
|
||||||
|
final Role empty = Role.EMPTY;
|
||||||
|
doAnswer((i) -> {
|
||||||
|
ActionListener callback =
|
||||||
|
(ActionListener) i.getArguments()[1];
|
||||||
|
rolesRef.set(((User) i.getArguments()[0]).roles());
|
||||||
|
callback.onResponse(empty);
|
||||||
|
return Void.TYPE;
|
||||||
|
}).when(authzService).roles(any(User.class), any(ActionListener.class));
|
||||||
|
ServerTransportFilter filter = getClientOrNodeFilter();
|
||||||
|
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
||||||
|
filter.inbound("_action", request, channel, future);
|
||||||
|
assertNotNull(rolesRef.get());
|
||||||
|
assertThat(rolesRef.get(), arrayContaining("kibana_system"));
|
||||||
|
|
||||||
|
// test with a version that doesn't need changing
|
||||||
|
filter = getClientOrNodeFilter();
|
||||||
|
rolesRef.set(null);
|
||||||
|
user = new KibanaUser(true);
|
||||||
|
when(authentication.getUser()).thenReturn(user);
|
||||||
|
when(authentication.getRunAsUser()).thenReturn(user);
|
||||||
|
when(authentication.getVersion()).thenReturn(Version.V_5_2_0_UNRELEASED);
|
||||||
|
future = new PlainActionFuture<>();
|
||||||
|
filter.inbound("_action", request, channel, future);
|
||||||
|
assertNotNull(rolesRef.get());
|
||||||
|
assertThat(rolesRef.get(), arrayContaining("kibana_system"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServerTransportFilter.ClientProfile getClientFilter() {
|
private ServerTransportFilter getClientOrNodeFilter() throws IOException {
|
||||||
return new ServerTransportFilter.ClientProfile(authcService, authzService, new ThreadContext(Settings.EMPTY), false,
|
return randomBoolean() ? getNodeFilter(true) : getClientFilter(true);
|
||||||
destructiveOperations);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServerTransportFilter.NodeProfile getNodeFilter() {
|
private ServerTransportFilter.ClientProfile getClientFilter(boolean reservedRealmEnabled) throws IOException {
|
||||||
return new ServerTransportFilter.NodeProfile(authcService, authzService, new ThreadContext(Settings.EMPTY), false,
|
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
|
||||||
destructiveOperations);
|
ThreadContext threadContext = new ThreadContext(settings);
|
||||||
|
return new ServerTransportFilter.ClientProfile(authcService, authzService, threadContext, false, destructiveOperations,
|
||||||
|
reservedRealmEnabled,
|
||||||
|
new SecurityContext(settings, threadContext, new CryptoService(Settings.EMPTY, new Environment(settings))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerTransportFilter.NodeProfile getNodeFilter(boolean reservedRealmEnabled) throws IOException {
|
||||||
|
Settings settings = Settings.builder().put("path.home", createTempDir()).build();
|
||||||
|
ThreadContext threadContext = new ThreadContext(settings);
|
||||||
|
return new ServerTransportFilter.NodeProfile(authcService, authzService, threadContext, false, destructiveOperations,
|
||||||
|
reservedRealmEnabled,
|
||||||
|
new SecurityContext(settings, threadContext, new CryptoService(Settings.EMPTY, new Environment(settings))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ public class SearchInputTests extends ESIntegTestCase {
|
||||||
XContentParser parser = createParser(builder);
|
XContentParser parser = createParser(builder);
|
||||||
parser.nextToken();
|
parser.nextToken();
|
||||||
|
|
||||||
SearchRequestParsers searchParsers = new SearchRequestParsers(null, null);
|
SearchRequestParsers searchParsers = new SearchRequestParsers(null);
|
||||||
SearchInputFactory factory = new SearchInputFactory(Settings.EMPTY, WatcherClientProxy.of(client()),
|
SearchInputFactory factory = new SearchInputFactory(Settings.EMPTY, WatcherClientProxy.of(client()),
|
||||||
searchParsers, xContentRegistry(), scriptService());
|
searchParsers, xContentRegistry(), scriptService());
|
||||||
|
|
||||||
|
|
|
@ -288,7 +288,7 @@ public class WatchTests extends ESTestCase {
|
||||||
ActionRegistry actionRegistry = registry(Collections.emptyList(), conditionRegistry, transformRegistry);
|
ActionRegistry actionRegistry = registry(Collections.emptyList(), conditionRegistry, transformRegistry);
|
||||||
Watch.Parser watchParser = new Watch.Parser(settings, triggerService, actionRegistry, inputRegistry, null, Clock.systemUTC());
|
Watch.Parser watchParser = new Watch.Parser(settings, triggerService, actionRegistry, inputRegistry, null, Clock.systemUTC());
|
||||||
|
|
||||||
SearchRequestParsers searchParsers = new SearchRequestParsers(null, null);
|
SearchRequestParsers searchParsers = new SearchRequestParsers(null);
|
||||||
WatcherSearchTemplateService searchTemplateService = new WatcherSearchTemplateService(settings, scriptService, searchParsers,
|
WatcherSearchTemplateService searchTemplateService = new WatcherSearchTemplateService(settings, scriptService, searchParsers,
|
||||||
xContentRegistry());
|
xContentRegistry());
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ public class WatchTests extends ESTestCase {
|
||||||
Map<String, InputFactory> parsers = new HashMap<>();
|
Map<String, InputFactory> parsers = new HashMap<>();
|
||||||
switch (inputType) {
|
switch (inputType) {
|
||||||
case SearchInput.TYPE:
|
case SearchInput.TYPE:
|
||||||
SearchRequestParsers searchParsers = new SearchRequestParsers(null, null);
|
SearchRequestParsers searchParsers = new SearchRequestParsers(null);
|
||||||
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, searchParsers, xContentRegistry(), scriptService));
|
parsers.put(SearchInput.TYPE, new SearchInputFactory(settings, client, searchParsers, xContentRegistry(), scriptService));
|
||||||
return new InputRegistry(Settings.EMPTY, parsers);
|
return new InputRegistry(Settings.EMPTY, parsers);
|
||||||
default:
|
default:
|
||||||
|
@ -457,7 +457,7 @@ public class WatchTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransformRegistry transformRegistry() {
|
private TransformRegistry transformRegistry() {
|
||||||
SearchRequestParsers searchParsers = new SearchRequestParsers(null, null);
|
SearchRequestParsers searchParsers = new SearchRequestParsers(null);
|
||||||
Map<String, TransformFactory> factories = new HashMap<>();
|
Map<String, TransformFactory> factories = new HashMap<>();
|
||||||
factories.put(ScriptTransform.TYPE, new ScriptTransformFactory(settings, scriptService));
|
factories.put(ScriptTransform.TYPE, new ScriptTransformFactory(settings, scriptService));
|
||||||
factories.put(SearchTransform.TYPE, new SearchTransformFactory(settings, client, searchParsers, xContentRegistry(), scriptService));
|
factories.put(SearchTransform.TYPE, new SearchTransformFactory(settings, client, searchParsers, xContentRegistry(), scriptService));
|
||||||
|
|
|
@ -23,7 +23,7 @@ superuser:
|
||||||
run_as:
|
run_as:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
kibana:
|
kibana_system:
|
||||||
cluster:
|
cluster:
|
||||||
- all
|
- all
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,12 @@ task oldClusterTest(type: RestIntegTestTask) {
|
||||||
cluster {
|
cluster {
|
||||||
plugin ':x-pack:elasticsearch'
|
plugin ':x-pack:elasticsearch'
|
||||||
distribution = 'zip'
|
distribution = 'zip'
|
||||||
bwcVersion = '6.0.0-alpha1-SNAPSHOT' // TODO: either randomize, or make this settable with sysprop
|
bwcVersion = '5.3.0-SNAPSHOT' // TODO: either randomize, or make this settable with sysprop
|
||||||
numBwcNodes = 2
|
numBwcNodes = 2
|
||||||
numNodes = 2
|
numNodes = 2
|
||||||
clusterName = 'rolling-upgrade'
|
clusterName = 'rolling-upgrade'
|
||||||
waitCondition = waitWithAuth
|
waitCondition = waitWithAuth
|
||||||
systemProperty 'es.logger.org.elasticsearch.xpack.security', 'TRACE'
|
setting 'logger.org.elasticsearch.xpack.security', 'TRACE'
|
||||||
}
|
}
|
||||||
systemProperty 'tests.rest.suite', 'old_cluster'
|
systemProperty 'tests.rest.suite', 'old_cluster'
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,15 @@ dependencies {
|
||||||
|
|
||||||
// copy x-pack plugin info so it is on the classpath and security manager has the right permissions
|
// copy x-pack plugin info so it is on the classpath and security manager has the right permissions
|
||||||
String outputDir = "generated-resources/${project.name}"
|
String outputDir = "generated-resources/${project.name}"
|
||||||
|
task copyXPackRestSpec(type: Copy) {
|
||||||
|
dependsOn(project.configurations.restSpec, 'processTestResources')
|
||||||
|
from project(':x-pack:elasticsearch').sourceSets.test.resources
|
||||||
|
include 'rest-api-spec/api/**'
|
||||||
|
into project.sourceSets.test.output.resourcesDir
|
||||||
|
}
|
||||||
|
|
||||||
task copyXPackPluginProps(type: Copy) {
|
task copyXPackPluginProps(type: Copy) {
|
||||||
|
dependsOn(copyXPackRestSpec)
|
||||||
from project(':x-pack:elasticsearch').file('src/main/plugin-metadata')
|
from project(':x-pack:elasticsearch').file('src/main/plugin-metadata')
|
||||||
from project(':x-pack:elasticsearch').tasks.pluginProperties
|
from project(':x-pack:elasticsearch').tasks.pluginProperties
|
||||||
into outputDir
|
into outputDir
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
---
|
||||||
|
"Verify kibana user role works in mixed cluster":
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic a2liYW5hOmNoYW5nZW1l"
|
||||||
|
cluster.health:
|
||||||
|
wait_for_status: yellow
|
||||||
|
wait_for_nodes: 2
|
||||||
|
timeout: 25s
|
||||||
|
- match: { timed_out: false }
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic a2liYW5hOmNoYW5nZW1l"
|
||||||
|
indices.create:
|
||||||
|
index: .kibana-foo
|
||||||
|
wait_for_active_shards : all
|
||||||
|
body:
|
||||||
|
settings:
|
||||||
|
index:
|
||||||
|
number_of_replicas: 1
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic a2liYW5hOmNoYW5nZW1l"
|
||||||
|
bulk:
|
||||||
|
refresh: true
|
||||||
|
body:
|
||||||
|
- '{"index": {"_index": ".kibana-foo", "_type": "test_type"}}'
|
||||||
|
- '{"f1": "v1_old", "f2": 0}'
|
||||||
|
- '{"index": {"_index": ".kibana-foo", "_type": "test_type"}}'
|
||||||
|
- '{"f1": "v2_old", "f2": 1}'
|
||||||
|
- '{"index": {"_index": ".kibana-foo", "_type": "test_type"}}'
|
||||||
|
- '{"f1": "v3_old", "f2": 2}'
|
||||||
|
- '{"index": {"_index": ".kibana-foo", "_type": "test_type"}}'
|
||||||
|
- '{"f1": "v4_old", "f2": 3}'
|
||||||
|
- '{"index": {"_index": ".kibana-foo", "_type": "test_type"}}'
|
||||||
|
- '{"f1": "v5_old", "f2": 4}'
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic a2liYW5hOmNoYW5nZW1l"
|
||||||
|
indices.flush:
|
||||||
|
index: .kibana-foo
|
||||||
|
|
||||||
|
- do:
|
||||||
|
headers:
|
||||||
|
Authorization: "Basic a2liYW5hOmNoYW5nZW1l"
|
||||||
|
search:
|
||||||
|
index: .kibana-foo
|
||||||
|
|
||||||
|
- match: { hits.total: 5 }
|
Loading…
Reference in New Issue