Modified the initial authentication logic
When a http request arrives, we first verify that it carries an authentication token (if it doesn't we throw an authentication exception). Beyond that, any action request that arrives, if it doesn't have an authentication token we assume system user identity. The rationale behind it is that if a request comes in via the transport, then the sending peer authenticated with a client auth cert (the cert acts as the guarantee here that the actor can be assumed as System)... otherwise, the request can come from the local node and triggered by the system (e.g. gateway recovery) The System user only has permissions to internal apis (it doesn't have full access/permission to all the apis). when a System identity is assumed, the authorization service will grant/deny the request based on whether the request is an internal api or not. Aso fixed the known actions (to be insync with 1.x branch) Closes elastic/elasticsearch#45 Original commit: elastic/x-pack-elasticsearch@be27cb0e1b
This commit is contained in:
parent
eb29414077
commit
956aeb53f4
|
@ -11,6 +11,7 @@ import org.elasticsearch.action.support.ActionFilterChain;
|
|||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.rest.*;
|
||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
||||
import org.elasticsearch.shield.authc.system.SystemRealm;
|
||||
|
@ -27,18 +28,39 @@ public class SecurityFilter extends AbstractComponent {
|
|||
private final AuthorizationService authzService;
|
||||
|
||||
@Inject
|
||||
public SecurityFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService) {
|
||||
public SecurityFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService, RestController restController) {
|
||||
super(settings);
|
||||
this.authcService = authcService;
|
||||
this.authzService = authzService;
|
||||
restController.registerFilter(new Rest(this));
|
||||
}
|
||||
|
||||
void process(String action, TransportRequest request, AuthenticationToken defaultToken) {
|
||||
AuthenticationToken token = authcService.token(action, request, defaultToken);
|
||||
void process(String action, TransportRequest request) {
|
||||
AuthenticationToken token = authcService.token(action, request, SystemRealm.TOKEN);
|
||||
User user = authcService.authenticate(action, request, token);
|
||||
authzService.authorize(user, action, request);
|
||||
}
|
||||
|
||||
public static class Rest extends RestFilter {
|
||||
|
||||
private final SecurityFilter filter;
|
||||
|
||||
public Rest(SecurityFilter filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(RestRequest request, RestChannel channel, RestFilterChain filterChain) throws Exception {
|
||||
filter.authcService.verifyToken(request);
|
||||
filterChain.continueProcessing(request, channel);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Transport extends TransportFilter.Base {
|
||||
|
||||
private final SecurityFilter filter;
|
||||
|
@ -50,7 +72,7 @@ public class SecurityFilter extends AbstractComponent {
|
|||
|
||||
@Override
|
||||
public void inboundRequest(String action, TransportRequest request) {
|
||||
filter.process(action, request, SystemRealm.TOKEN);
|
||||
filter.process(action, request);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +88,7 @@ public class SecurityFilter extends AbstractComponent {
|
|||
@Override
|
||||
public void process(String action, ActionRequest request, ActionListener listener, ActionFilterChain chain) {
|
||||
try {
|
||||
filter.process(action, request, null);
|
||||
filter.process(action, request);
|
||||
} catch (Throwable t) {
|
||||
listener.onFailure(t);
|
||||
return;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.authc;
|
||||
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
|
||||
|
@ -13,6 +14,12 @@ import org.elasticsearch.transport.TransportMessage;
|
|||
*/
|
||||
public interface AuthenticationService {
|
||||
|
||||
/**
|
||||
* Inspects the given rest request and verifies it carries an authentication token, if it doesn't
|
||||
* an {@link AuthenticationException} is thrown
|
||||
*/
|
||||
void verifyToken(RestRequest request) throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* Extracts the authenticate token from the given message. If no recognized auth token is associated
|
||||
* with the message, an AuthenticationException is thrown.
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.common.component.AbstractComponent;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.internal.Nullable;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
|
@ -33,6 +34,16 @@ public class InternalAuthenticationService extends AbstractComponent implements
|
|||
this.auditTrail = auditTrail;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verifyToken(RestRequest request) throws AuthenticationException {
|
||||
for (Realm realm : realms) {
|
||||
if (realm.hasToken(request)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new AuthenticationException("Missing authentication token");
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationToken token(String action, TransportMessage<?> message) {
|
||||
return token(action, message, null);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
package org.elasticsearch.shield.authc;
|
||||
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
|
||||
|
@ -20,6 +21,8 @@ public interface Realm<T extends AuthenticationToken> {
|
|||
*/
|
||||
String type();
|
||||
|
||||
boolean hasToken(RestRequest request);
|
||||
|
||||
/**
|
||||
* Attempts to extract a authentication token from the request. If an appropriate token is found
|
||||
* {@link #authenticate(AuthenticationToken)} will be called for an authentication attempt. If no
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.component.AbstractComponent;
|
|||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.inject.name.Named;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
|
@ -40,6 +41,11 @@ public class ESUsersRealm extends AbstractComponent implements Realm<UsernamePas
|
|||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasToken(RestRequest request) {
|
||||
return UsernamePasswordToken.hasToken(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsernamePasswordToken token(TransportMessage<?> message) {
|
||||
return UsernamePasswordToken.extractToken(message, null);
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.elasticsearch.common.cache.CacheBuilder;
|
|||
import org.elasticsearch.common.component.AbstractComponent;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.authc.AuthenticationException;
|
||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
||||
|
@ -42,6 +43,11 @@ public abstract class CachingUsernamePasswordRealm extends AbstractComponent imp
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasToken(RestRequest request) {
|
||||
return UsernamePasswordToken.hasToken(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UsernamePasswordToken token(TransportMessage<?> message) {
|
||||
return UsernamePasswordToken.extractToken(message, null);
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.shield.authc.support;
|
|||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.elasticsearch.common.base.Charsets;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.authc.AuthenticationException;
|
||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
|
@ -43,6 +44,11 @@ public class UsernamePasswordToken implements AuthenticationToken {
|
|||
return password;
|
||||
}
|
||||
|
||||
public static boolean hasToken(RestRequest request) {
|
||||
String header = request.header(BASIC_AUTH_HEADER);
|
||||
return header != null && BASIC_AUTH_PATTERN.matcher(header).matches();
|
||||
}
|
||||
|
||||
public static UsernamePasswordToken extractToken(TransportMessage<?> message, UsernamePasswordToken defaultToken) {
|
||||
UsernamePasswordToken token = (UsernamePasswordToken) message.context().get(TOKEN_KEY);
|
||||
if (token != null) {
|
||||
|
@ -65,6 +71,9 @@ public class UsernamePasswordToken implements AuthenticationToken {
|
|||
|
||||
String userpasswd = new String(Base64.decodeBase64(matcher.group(1)), Charsets.UTF_8);
|
||||
int i = userpasswd.indexOf(':');
|
||||
if (i < 0) {
|
||||
throw new AuthenticationException("Invalid basic authentication header value");
|
||||
}
|
||||
token = new UsernamePasswordToken(userpasswd.substring(0, i), userpasswd.substring(i+1).toCharArray());
|
||||
message.context().put(TOKEN_KEY, token);
|
||||
return token;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.shield.authc.system;
|
||||
|
||||
import org.elasticsearch.common.inject.AbstractModule;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
||||
import org.elasticsearch.shield.authc.Realm;
|
||||
|
@ -33,6 +34,11 @@ public class SystemRealm implements Realm<AuthenticationToken> {
|
|||
return "system";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasToken(RestRequest request) {
|
||||
return false; // system calls never come from rest interface
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthenticationToken token(TransportMessage<?> message) {
|
||||
// as far as this realm is concerned, there's never a system token
|
||||
|
|
|
@ -81,8 +81,8 @@ public abstract class Privilege<P extends Privilege<P>> {
|
|||
public static final Index DATA_ACCESS = new Index("data_access","indices:data/.*");
|
||||
public static final Index CRUD = new Index("crud", "indices:data/write/.*", "indices:data/read/.*");
|
||||
public static final Index READ = new Index("read", "indices:data/read/.*");
|
||||
public static final Index SEARCH = new Index("search", SearchAction.NAME + ".*", GetAction.NAME + ".*");
|
||||
public static final Index GET = new Index("get", GetAction.NAME + ".*");
|
||||
public static final Index SEARCH = new Index("search", SearchAction.NAME + ".*", GetAction.NAME + ".*");
|
||||
public static final Index GET = new Index("get", GetAction.NAME + ".*");
|
||||
public static final Index INDEX = new Index("index", "indices:data/write/index.*", "indices:data/write/update");
|
||||
public static final Index DELETE = new Index("delete", "indices:data/write/delete.*");
|
||||
public static final Index WRITE = new Index("write", "indices:data/write/.*");
|
||||
|
|
|
@ -9,6 +9,10 @@ import org.elasticsearch.action.ActionListener;
|
|||
import org.elasticsearch.action.ActionRequest;
|
||||
import org.elasticsearch.action.support.ActionFilterChain;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.rest.RestChannel;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestFilterChain;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.authc.AuthenticationException;
|
||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
||||
|
@ -36,34 +40,24 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
private SecurityFilter filter;
|
||||
private AuthenticationService authcService;
|
||||
private AuthorizationService authzService;
|
||||
private RestController restController;
|
||||
|
||||
@Before
|
||||
public void init() throws Exception {
|
||||
authcService = mock(AuthenticationService.class);
|
||||
authzService = mock(AuthorizationService.class);
|
||||
filter = new SecurityFilter(ImmutableSettings.EMPTY, authcService, authzService);
|
||||
restController = mock(RestController.class);
|
||||
filter = new SecurityFilter(ImmutableSettings.EMPTY, authcService, authzService, restController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcess_WithoutDefaultToken() throws Exception {
|
||||
public void testProcess() throws Exception {
|
||||
TransportRequest request = new InternalRequest();
|
||||
AuthenticationToken token = mock(AuthenticationToken.class);
|
||||
User user = new User.Simple("_username", "r1");
|
||||
when(authcService.token("_action", request, null)).thenReturn(token);
|
||||
when(authcService.token("_action", request, SystemRealm.TOKEN)).thenReturn(token);
|
||||
when(authcService.authenticate("_action", request, token)).thenReturn(user);
|
||||
filter.process("_action", request, null);
|
||||
verify(authzService).authorize(user, "_action", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcess_WithDefaultToken() throws Exception {
|
||||
TransportRequest request = new InternalRequest();
|
||||
AuthenticationToken token = mock(AuthenticationToken.class);
|
||||
AuthenticationToken defaultToken = mock(AuthenticationToken.class);
|
||||
User user = new User.Simple("_username", "r1");
|
||||
when(authcService.token("_action", request, defaultToken)).thenReturn(token);
|
||||
when(authcService.authenticate("_action", request, token)).thenReturn(user);
|
||||
filter.process("_action", request, defaultToken);
|
||||
filter.process("_action", request);
|
||||
verify(authzService).authorize(user, "_action", request);
|
||||
}
|
||||
|
||||
|
@ -73,9 +67,9 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
thrown.expectMessage("failed authc");
|
||||
TransportRequest request = new InternalRequest();
|
||||
AuthenticationToken token = mock(AuthenticationToken.class);
|
||||
when(authcService.token("_action", request, null)).thenReturn(token);
|
||||
when(authcService.token("_action", request, SystemRealm.TOKEN)).thenReturn(token);
|
||||
when(authcService.authenticate("_action", request, token)).thenThrow(new AuthenticationException("failed authc"));
|
||||
filter.process("_action", request, null);
|
||||
filter.process("_action", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -83,8 +77,8 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
thrown.expect(AuthenticationException.class);
|
||||
thrown.expectMessage("failed authc");
|
||||
TransportRequest request = new InternalRequest();
|
||||
when(authcService.token("_action", request, null)).thenThrow(new AuthenticationException("failed authc"));
|
||||
filter.process("_action", request, null);
|
||||
when(authcService.token("_action", request, SystemRealm.TOKEN)).thenThrow(new AuthenticationException("failed authc"));
|
||||
filter.process("_action", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -94,10 +88,10 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
TransportRequest request = new InternalRequest();
|
||||
AuthenticationToken token = mock(AuthenticationToken.class);
|
||||
User user = new User.Simple("_username", "r1");
|
||||
when(authcService.token("_action", request, null)).thenReturn(token);
|
||||
when(authcService.token("_action", request, SystemRealm.TOKEN)).thenReturn(token);
|
||||
when(authcService.authenticate("_action", request, token)).thenReturn(user);
|
||||
doThrow(new AuthorizationException("failed authz")).when(authzService).authorize(user, "_action", request);
|
||||
filter.process("_action", request, null);
|
||||
filter.process("_action", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -106,7 +100,7 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
SecurityFilter.Transport transport = new SecurityFilter.Transport(filter);
|
||||
InternalRequest request = new InternalRequest();
|
||||
transport.inboundRequest("_action", request);
|
||||
verify(filter).process("_action", request, SystemRealm.TOKEN);
|
||||
verify(filter).process("_action", request);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -116,7 +110,7 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
filter = mock(SecurityFilter.class);
|
||||
SecurityFilter.Transport transport = new SecurityFilter.Transport(filter);
|
||||
InternalRequest request = new InternalRequest();
|
||||
doThrow(new RuntimeException("process-error")).when(filter).process("_action", request, SystemRealm.TOKEN);
|
||||
doThrow(new RuntimeException("process-error")).when(filter).process("_action", request);
|
||||
transport.inboundRequest("_action", request);
|
||||
}
|
||||
|
||||
|
@ -128,7 +122,7 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
ActionListener listener = mock(ActionListener.class);
|
||||
ActionFilterChain chain = mock(ActionFilterChain.class);
|
||||
action.process("_action", request, listener, chain);
|
||||
verify(filter).process("_action", request, null);
|
||||
verify(filter).process("_action", request);
|
||||
verify(chain).continueProcessing("_action", request, listener);
|
||||
}
|
||||
|
||||
|
@ -140,12 +134,35 @@ public class SecurityFilterTests extends ElasticsearchTestCase {
|
|||
ActionListener listener = mock(ActionListener.class);
|
||||
ActionFilterChain chain = mock(ActionFilterChain.class);
|
||||
RuntimeException exception = new RuntimeException("process-error");
|
||||
doThrow(exception).when(filter).process("_action", request, null);
|
||||
doThrow(exception).when(filter).process("_action", request);
|
||||
action.process("_action", request, listener, chain);
|
||||
verify(listener).onFailure(exception);
|
||||
verifyNoMoreInteractions(chain);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRest_WithToken() throws Exception {
|
||||
SecurityFilter.Rest rest = new SecurityFilter.Rest(filter);
|
||||
RestRequest request = mock(RestRequest.class);
|
||||
RestChannel channel = mock(RestChannel.class);
|
||||
RestFilterChain chain = mock(RestFilterChain.class);
|
||||
rest.process(request, channel, chain);
|
||||
verify(authcService).verifyToken(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRest_WithoutToken() throws Exception {
|
||||
AuthenticationException exception = new AuthenticationException("no token");
|
||||
thrown.expect(AuthenticationException.class);
|
||||
thrown.expectMessage("no token");
|
||||
SecurityFilter.Rest rest = new SecurityFilter.Rest(filter);
|
||||
RestRequest request = mock(RestRequest.class);
|
||||
RestChannel channel = mock(RestChannel.class);
|
||||
RestFilterChain chain = mock(RestFilterChain.class);
|
||||
doThrow(exception).when(authcService).verifyToken(request);
|
||||
rest.process(request, channel, chain);
|
||||
}
|
||||
|
||||
private static class InternalRequest extends TransportRequest {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,16 @@
|
|||
package org.elasticsearch.shield.authc;
|
||||
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.elasticsearch.shield.audit.AuditTrail;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.transport.TransportMessage;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.elasticsearch.shield.test.ShieldAssertions.assertContainsWWWAuthenticateHeader;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
@ -24,8 +27,12 @@ import static org.mockito.Mockito.*;
|
|||
*/
|
||||
public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
InternalAuthenticationService service;
|
||||
TransportMessage message;
|
||||
RestRequest restRequest;
|
||||
Realm firstRealm;
|
||||
Realm secondRealm;
|
||||
AuditTrail auditTrail;
|
||||
|
@ -35,6 +42,7 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
|
|||
public void init() throws Exception {
|
||||
token = mock(AuthenticationToken.class);
|
||||
message = new InternalMessage();
|
||||
restRequest = mock(RestRequest.class);
|
||||
firstRealm = mock(Realm.class);
|
||||
when(firstRealm.type()).thenReturn("first");
|
||||
secondRealm = mock(Realm.class);
|
||||
|
@ -153,6 +161,22 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
|
|||
assertThat(message.context().get(InternalAuthenticationService.USER_CTX_KEY), is((Object) user));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyToken_Exists() throws Exception {
|
||||
when(firstRealm.hasToken(restRequest)).thenReturn(false);
|
||||
when(secondRealm.hasToken(restRequest)).thenReturn(true);
|
||||
service.verifyToken(restRequest);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVerifyToken_Missing() throws Exception {
|
||||
thrown.expect(AuthenticationException.class);
|
||||
thrown.expectMessage("Missing authentication token");
|
||||
when(firstRealm.hasToken(restRequest)).thenReturn(false);
|
||||
when(secondRealm.hasToken(restRequest)).thenReturn(false);
|
||||
service.verifyToken(restRequest);
|
||||
}
|
||||
|
||||
private static class InternalMessage extends TransportMessage<InternalMessage> {
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.elasticsearch.shield.authc.support;
|
||||
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.User;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -25,7 +26,12 @@ public class CachingUsernamePasswordRealmTests {
|
|||
return new User.Simple(token.principal(), "testRole1", "testRole2");
|
||||
}
|
||||
|
||||
@Override public String type() { return "test"; };
|
||||
@Override public String type() { return "test"; }
|
||||
|
||||
@Override
|
||||
public boolean hasToken(RestRequest request) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ package org.elasticsearch.shield.authc.support;
|
|||
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.shield.authc.AuthenticationException;
|
||||
import org.elasticsearch.test.ElasticsearchTestCase;
|
||||
import org.elasticsearch.transport.TransportRequest;
|
||||
|
@ -14,6 +15,8 @@ import org.junit.Test;
|
|||
|
||||
import static org.elasticsearch.shield.test.ShieldAssertions.assertContainsWWWAuthenticateHeader;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -52,6 +55,21 @@ public class UsernamePasswordTokenTests extends ElasticsearchTestCase {
|
|||
assertThat(token, is(token2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractToken_Invalid() throws Exception {
|
||||
String[] invalidValues = { "Basic", "Basic ", "Basic f" };
|
||||
for (String value : invalidValues) {
|
||||
TransportRequest request = new TransportRequest() {};
|
||||
request.putHeader(UsernamePasswordToken.BASIC_AUTH_HEADER, value);
|
||||
try {
|
||||
UsernamePasswordToken.extractToken(request, null);
|
||||
fail("Expected an authentication exception for invalid basic auth token [" + value + "]");
|
||||
} catch (AuthenticationException ae) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThatAuthorizationExceptionContainsResponseHeaders() {
|
||||
TransportRequest request = new TransportRequest() {};
|
||||
|
@ -64,4 +82,27 @@ public class UsernamePasswordTokenTests extends ElasticsearchTestCase {
|
|||
assertContainsWWWAuthenticateHeader(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasToken() throws Exception {
|
||||
RestRequest request = mock(RestRequest.class);
|
||||
when(request.header(UsernamePasswordToken.BASIC_AUTH_HEADER)).thenReturn("Basic foobar");
|
||||
assertThat(UsernamePasswordToken.hasToken(request), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasToken_Missing() throws Exception {
|
||||
RestRequest request = mock(RestRequest.class);
|
||||
when(request.header(UsernamePasswordToken.BASIC_AUTH_HEADER)).thenReturn(null);
|
||||
assertThat(UsernamePasswordToken.hasToken(request), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasToken_WithInvalidToken() throws Exception {
|
||||
RestRequest request = mock(RestRequest.class);
|
||||
when(request.header(UsernamePasswordToken.BASIC_AUTH_HEADER)).thenReturn("invalid");
|
||||
assertThat(UsernamePasswordToken.hasToken(request), is(false));
|
||||
when(request.header(UsernamePasswordToken.BASIC_AUTH_HEADER)).thenReturn("Basic");
|
||||
assertThat(UsernamePasswordToken.hasToken(request), is(false));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,9 +41,9 @@ import static org.hamcrest.Matchers.is;
|
|||
@ClusterScope(scope = Scope.SUITE, numDataNodes = 1, numClientNodes = 0)
|
||||
public abstract class ShieldIntegrationTest extends ElasticsearchIntegrationTest {
|
||||
|
||||
private static final String DEFAULT_USER_NAME = "test_user";
|
||||
private static final String DEFAULT_PASSWORD = "changeme";
|
||||
private static final String DEFAULT_ROLE = "user";
|
||||
protected static final String DEFAULT_USER_NAME = "test_user";
|
||||
protected static final String DEFAULT_PASSWORD = "changeme";
|
||||
protected static final String DEFAULT_ROLE = "user";
|
||||
|
||||
public static final String CONFIG_IPFILTER_ALLOW_ALL = "allow: all\n";
|
||||
public static final String CONFIG_STANDARD_USER = DEFAULT_USER_NAME + ":{plain}" + DEFAULT_PASSWORD + "\n";
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.elasticsearch.http.HttpServerTransport;
|
|||
import org.elasticsearch.node.Node;
|
||||
import org.elasticsearch.node.NodeBuilder;
|
||||
import org.elasticsearch.plugins.PluginsService;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.shield.test.ShieldIntegrationTest;
|
||||
import org.elasticsearch.shield.transport.netty.NettySecuredTransport;
|
||||
import org.elasticsearch.transport.Transport;
|
||||
|
@ -164,6 +165,7 @@ public class SslIntegrationTests extends ShieldIntegrationTest {
|
|||
String url = String.format(Locale.ROOT, "https://%s:%s/", InetAddresses.toUriString(inetSocketTransportAddress.address().getAddress()), inetSocketTransportAddress.address().getPort());
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setRequestProperty("Authorization", UsernamePasswordToken.basicAuthHeaderValue(DEFAULT_USER_NAME, DEFAULT_PASSWORD.toCharArray()));
|
||||
connection.connect();
|
||||
|
||||
assertThat(connection.getResponseCode(), is(200));
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.elasticsearch.common.settings.Settings;
|
|||
import org.elasticsearch.common.transport.InetSocketTransportAddress;
|
||||
import org.elasticsearch.common.transport.TransportAddress;
|
||||
import org.elasticsearch.http.HttpServerTransport;
|
||||
import org.elasticsearch.shield.authc.support.UsernamePasswordToken;
|
||||
import org.elasticsearch.shield.test.ShieldIntegrationTest;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -127,6 +128,7 @@ public class SslRequireAuthTests extends ShieldIntegrationTest {
|
|||
String url = String.format(Locale.ROOT, "https://%s:%s/", InetAddresses.toUriString(inetSocketTransportAddress.address().getAddress()), inetSocketTransportAddress.address().getPort());
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setRequestProperty("Authorization", UsernamePasswordToken.basicAuthHeaderValue(DEFAULT_USER_NAME, DEFAULT_PASSWORD.toCharArray()));
|
||||
connection.connect();
|
||||
|
||||
assertThat(connection.getResponseCode(), is(200));
|
||||
|
|
|
@ -36,9 +36,9 @@ indices:admin/optimize
|
|||
indices:admin/refresh
|
||||
indices:admin/settings/update
|
||||
indices:admin/shards/search_shards
|
||||
cluster:admin/template/delete
|
||||
cluster:admin/template/get
|
||||
cluster:admin/template/put
|
||||
indices:admin/template/delete
|
||||
indices:admin/template/get
|
||||
indices:admin/template/put
|
||||
indices:admin/types/exists
|
||||
indices:admin/validate/query
|
||||
indices:admin/warmers/delete
|
||||
|
|
Loading…
Reference in New Issue