Add anonymous user support
When a request (rest/transport) that arrives to elasticsearch, and that has no authentication token associated with it, the request is then considered to be sent by an anonymous user. By default, we disallow anonymous requests and fail it by returning an authentication error. Anonymous access can be enabled by configuring the `shield.authc.anonymous.roles` setting in `elasticsearch.yml` file. When set, an anonymous request will be associated with an `anonymous` user that holds the configured roles. From there on, authorization will continue as usual, and will try to authorize the request based on these roles. Closes elastic/elasticsearch#376 Original commit: elastic/x-pack-elasticsearch@028b3a380b
This commit is contained in:
parent
af86fcaa52
commit
458daa2323
|
@ -6,6 +6,7 @@
|
||||||
package org.elasticsearch.shield.authc;
|
package org.elasticsearch.shield.authc;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamInput;
|
import org.elasticsearch.common.io.stream.BytesStreamInput;
|
||||||
|
@ -27,6 +28,8 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class InternalAuthenticationService extends AbstractComponent implements AuthenticationService {
|
public class InternalAuthenticationService extends AbstractComponent implements AuthenticationService {
|
||||||
|
|
||||||
|
static final String ANONYMOUS_USERNAME = "_es_anonymous_user";
|
||||||
|
|
||||||
static final String TOKEN_KEY = "_shield_token";
|
static final String TOKEN_KEY = "_shield_token";
|
||||||
static final String USER_KEY = "_shield_user";
|
static final String USER_KEY = "_shield_user";
|
||||||
|
|
||||||
|
@ -35,6 +38,9 @@ public class InternalAuthenticationService extends AbstractComponent implements
|
||||||
private final SignatureService signatureService;
|
private final SignatureService signatureService;
|
||||||
private final boolean signUserHeader;
|
private final boolean signUserHeader;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final User anonymouseUser;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public InternalAuthenticationService(Settings settings, Realms realms, AuditTrail auditTrail, SignatureService signatureService) {
|
public InternalAuthenticationService(Settings settings, Realms realms, AuditTrail auditTrail, SignatureService signatureService) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
@ -42,12 +48,16 @@ public class InternalAuthenticationService extends AbstractComponent implements
|
||||||
this.auditTrail = auditTrail;
|
this.auditTrail = auditTrail;
|
||||||
this.signatureService = signatureService;
|
this.signatureService = signatureService;
|
||||||
this.signUserHeader = componentSettings.getAsBoolean("sign_user_header", true);
|
this.signUserHeader = componentSettings.getAsBoolean("sign_user_header", true);
|
||||||
|
anonymouseUser = resolveAnonymouseUser(componentSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public User authenticate(RestRequest request) throws AuthenticationException {
|
public User authenticate(RestRequest request) throws AuthenticationException {
|
||||||
AuthenticationToken token = token(request);
|
AuthenticationToken token = token(request);
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
|
if (anonymouseUser != null) {
|
||||||
|
return anonymouseUser;
|
||||||
|
}
|
||||||
auditTrail.anonymousAccessDenied(request);
|
auditTrail.anonymousAccessDenied(request);
|
||||||
throw new AuthenticationException("missing authentication token for REST request [" + request.uri() + "]");
|
throw new AuthenticationException("missing authentication token for REST request [" + request.uri() + "]");
|
||||||
}
|
}
|
||||||
|
@ -122,6 +132,15 @@ public class InternalAuthenticationService extends AbstractComponent implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static User resolveAnonymouseUser(Settings settings) {
|
||||||
|
String[] roles = settings.getAsArray("anonymous.roles", null);
|
||||||
|
if (roles == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String username = settings.get("anonymous.username", ANONYMOUS_USERNAME);
|
||||||
|
return new User.Simple(username, roles);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticates the user associated with the given request by delegating the authentication to
|
* Authenticates the user associated with the given request by delegating the authentication to
|
||||||
* the configured realms. Each realm that supports the given token will be asked to perform authentication,
|
* the configured realms. Each realm that supports the given token will be asked to perform authentication,
|
||||||
|
@ -145,11 +164,14 @@ public class InternalAuthenticationService extends AbstractComponent implements
|
||||||
AuthenticationToken token = token(action, message);
|
AuthenticationToken token = token(action, message);
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
if (fallbackUser == null) {
|
if (fallbackUser != null) {
|
||||||
auditTrail.anonymousAccessDenied(action, message);
|
return fallbackUser;
|
||||||
throw new AuthenticationException("missing authentication token for action [" + action + "]");
|
|
||||||
}
|
}
|
||||||
return fallbackUser;
|
if (anonymouseUser != null) {
|
||||||
|
return anonymouseUser;
|
||||||
|
}
|
||||||
|
auditTrail.anonymousAccessDenied(action, message);
|
||||||
|
throw new AuthenticationException("missing authentication token for action [" + action + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -401,6 +401,84 @@ public class InternalAuthenticationServiceTests extends ElasticsearchTestCase {
|
||||||
assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user"));
|
assertThat(message.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResolveAnonymousUser() throws Exception {
|
||||||
|
Settings settings = ImmutableSettings.builder()
|
||||||
|
.put("anonymous.username", "anonym1")
|
||||||
|
.putArray("anonymous.roles", "r1", "r2", "r3")
|
||||||
|
.build();
|
||||||
|
User user = InternalAuthenticationService.resolveAnonymouseUser(settings);
|
||||||
|
assertThat(user, notNullValue());
|
||||||
|
assertThat(user.principal(), equalTo("anonym1"));
|
||||||
|
assertThat(user.roles(), arrayContainingInAnyOrder("r1", "r2", "r3"));
|
||||||
|
|
||||||
|
settings = ImmutableSettings.builder()
|
||||||
|
.putArray("anonymous.roles", "r1", "r2", "r3")
|
||||||
|
.build();
|
||||||
|
user = InternalAuthenticationService.resolveAnonymouseUser(settings);
|
||||||
|
assertThat(user, notNullValue());
|
||||||
|
assertThat(user.principal(), equalTo(InternalAuthenticationService.ANONYMOUS_USERNAME));
|
||||||
|
assertThat(user.roles(), arrayContainingInAnyOrder("r1", "r2", "r3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testResolveAnonymousUser_NoSettings() throws Exception {
|
||||||
|
Settings settings = randomBoolean() ?
|
||||||
|
ImmutableSettings.EMPTY :
|
||||||
|
ImmutableSettings.builder().put("anonymous.username", "user1").build();
|
||||||
|
User user = InternalAuthenticationService.resolveAnonymouseUser(settings);
|
||||||
|
assertThat(user, nullValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnonymousUser_Rest() throws Exception {
|
||||||
|
String username = randomBoolean() ? InternalAuthenticationService.ANONYMOUS_USERNAME : "user1";
|
||||||
|
ImmutableSettings.Builder builder = ImmutableSettings.builder()
|
||||||
|
.putArray("shield.authc.anonymous.roles", "r1", "r2", "r3");
|
||||||
|
if (username != InternalAuthenticationService.ANONYMOUS_USERNAME) {
|
||||||
|
builder.put("shield.authc.anonymous.username", username);
|
||||||
|
}
|
||||||
|
service = new InternalAuthenticationService(builder.build(), realms, auditTrail, signatureService);
|
||||||
|
|
||||||
|
RestRequest request = new InternalRestRequest();
|
||||||
|
|
||||||
|
User user = service.authenticate(request);
|
||||||
|
assertThat(user, notNullValue());
|
||||||
|
assertThat(user.principal(), equalTo(username));
|
||||||
|
assertThat(user.roles(), arrayContainingInAnyOrder("r1", "r2", "r3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnonymousUser_Transport_NoDefaultUser() throws Exception {
|
||||||
|
Settings settings = ImmutableSettings.builder()
|
||||||
|
.putArray("shield.authc.anonymous.roles", "r1", "r2", "r3")
|
||||||
|
.build();
|
||||||
|
service = new InternalAuthenticationService(settings, realms, auditTrail, signatureService);
|
||||||
|
|
||||||
|
InternalMessage message = new InternalMessage();
|
||||||
|
|
||||||
|
User user = service.authenticate("_action", message, null);
|
||||||
|
assertThat(user, notNullValue());
|
||||||
|
assertThat(user.principal(), equalTo(InternalAuthenticationService.ANONYMOUS_USERNAME));
|
||||||
|
assertThat(user.roles(), arrayContainingInAnyOrder("r1", "r2", "r3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnonymousUser_Transport_WithDefaultUser() throws Exception {
|
||||||
|
Settings settings = ImmutableSettings.builder()
|
||||||
|
.putArray("shield.authc.anonymous.roles", "r1", "r2", "r3")
|
||||||
|
.build();
|
||||||
|
service = new InternalAuthenticationService(settings, realms, auditTrail, signatureService);
|
||||||
|
|
||||||
|
InternalMessage message = new InternalMessage();
|
||||||
|
|
||||||
|
User user = service.authenticate("_action", message, User.SYSTEM);
|
||||||
|
assertThat(user, notNullValue());
|
||||||
|
assertThat(user, sameInstance(User.SYSTEM));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static class InternalMessage extends TransportMessage<InternalMessage> {
|
private static class InternalMessage extends TransportMessage<InternalMessage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue