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:
uboness 2015-02-02 23:35:03 +01:00
parent af86fcaa52
commit 458daa2323
2 changed files with 104 additions and 4 deletions

View File

@ -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 {

View File

@ -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> {
} }