Report anonymous roles in response to "GET _security/_authenticate" API call when: * Anonymous role is enabled * User is not the anonymous user * Credentials is not an API Key
This commit is contained in:
parent
d1031fd928
commit
f0615113b6
|
@ -132,7 +132,8 @@ public class EnableSecurityOnBasicLicenseIT extends ESRestTestCase {
|
|||
final Map<String, Object> auth = getAsMap("/_security/_authenticate");
|
||||
// From file realm, configured in build.gradle
|
||||
assertThat(ObjectPath.evaluate(auth, "username"), equalTo("security_test_user"));
|
||||
assertThat(ObjectPath.evaluate(auth, "roles"), contains("security_test_role"));
|
||||
// The anonymous role is granted by anonymous access enabled in build.gradle
|
||||
assertThat(ObjectPath.evaluate(auth, "roles"), contains("security_test_role", "anonymous"));
|
||||
}
|
||||
|
||||
private void checkAllowedWrite(String indexName) throws IOException {
|
||||
|
|
|
@ -448,6 +448,7 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
|
|||
final NativeRoleMappingStore nativeRoleMappingStore = new NativeRoleMappingStore(settings, client, securityIndex.get(),
|
||||
scriptService);
|
||||
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
||||
components.add(anonymousUser);
|
||||
final ReservedRealm reservedRealm = new ReservedRealm(environment, settings, nativeUsersStore,
|
||||
anonymousUser, securityIndex.get(), threadPool);
|
||||
final SecurityExtension.SecurityComponents extensionComponents = new ExtensionComponents(environment, client, clusterService,
|
||||
|
|
|
@ -17,18 +17,24 @@ import org.elasticsearch.xpack.core.security.action.user.AuthenticateAction;
|
|||
import org.elasticsearch.xpack.core.security.action.user.AuthenticateRequest;
|
||||
import org.elasticsearch.xpack.core.security.action.user.AuthenticateResponse;
|
||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
|
||||
import org.elasticsearch.xpack.core.security.user.SystemUser;
|
||||
import org.elasticsearch.xpack.core.security.user.User;
|
||||
import org.elasticsearch.xpack.core.security.user.XPackUser;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class TransportAuthenticateAction extends HandledTransportAction<AuthenticateRequest, AuthenticateResponse> {
|
||||
|
||||
private final SecurityContext securityContext;
|
||||
private final AnonymousUser anonymousUser;
|
||||
|
||||
@Inject
|
||||
public TransportAuthenticateAction(TransportService transportService, ActionFilters actionFilters, SecurityContext securityContext) {
|
||||
public TransportAuthenticateAction(TransportService transportService, ActionFilters actionFilters, SecurityContext securityContext,
|
||||
AnonymousUser anonymousUser) {
|
||||
super(AuthenticateAction.NAME, transportService, actionFilters, AuthenticateRequest::new);
|
||||
this.securityContext = securityContext;
|
||||
this.anonymousUser = anonymousUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,7 +49,32 @@ public class TransportAuthenticateAction extends HandledTransportAction<Authenti
|
|||
} else if (SystemUser.is(runAsUser) || XPackUser.is(runAsUser)) {
|
||||
listener.onFailure(new IllegalArgumentException("user [" + runAsUser.principal() + "] is internal"));
|
||||
} else {
|
||||
listener.onResponse(new AuthenticateResponse(authentication));
|
||||
final User user = authentication.getUser();
|
||||
final boolean shouldAddAnonymousRoleNames = anonymousUser.enabled() && false == anonymousUser.equals(user)
|
||||
&& authentication.getAuthenticationType() != Authentication.AuthenticationType.API_KEY;
|
||||
if (shouldAddAnonymousRoleNames) {
|
||||
final String[] allRoleNames = Stream.concat(
|
||||
Stream.of(user.roles()), Stream.of(anonymousUser.roles())).toArray(String[]::new);
|
||||
listener.onResponse(new AuthenticateResponse(
|
||||
new Authentication(
|
||||
new User(new User(
|
||||
user.principal(),
|
||||
allRoleNames,
|
||||
user.fullName(),
|
||||
user.email(),
|
||||
user.metadata(),
|
||||
user.enabled()
|
||||
), user.authenticatedUser()),
|
||||
authentication.getAuthenticatedBy(),
|
||||
authentication.getLookedUpBy(),
|
||||
authentication.getVersion(),
|
||||
authentication.getAuthenticationType(),
|
||||
authentication.getMetadata()
|
||||
)
|
||||
));
|
||||
} else {
|
||||
listener.onResponse(new AuthenticateResponse(authentication));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.elasticsearch.xpack.core.security.SecurityContext;
|
|||
import org.elasticsearch.xpack.core.security.action.user.AuthenticateRequest;
|
||||
import org.elasticsearch.xpack.core.security.action.user.AuthenticateResponse;
|
||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
|
||||
import org.elasticsearch.xpack.core.security.user.ElasticUser;
|
||||
import org.elasticsearch.xpack.core.security.user.KibanaUser;
|
||||
import org.elasticsearch.xpack.core.security.user.SystemUser;
|
||||
|
@ -44,7 +45,7 @@ public class TransportAuthenticateActionTests extends ESTestCase {
|
|||
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
|
||||
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
|
||||
TransportAuthenticateAction action = new TransportAuthenticateAction(transportService,
|
||||
mock(ActionFilters.class), securityContext);
|
||||
mock(ActionFilters.class), securityContext, prepareAnonymousUser());
|
||||
|
||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
||||
final AtomicReference<AuthenticateResponse> responseRef = new AtomicReference<>();
|
||||
|
@ -70,7 +71,7 @@ public class TransportAuthenticateActionTests extends ESTestCase {
|
|||
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
|
||||
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
|
||||
TransportAuthenticateAction action = new TransportAuthenticateAction(transportService,
|
||||
mock(ActionFilters.class), securityContext);
|
||||
mock(ActionFilters.class), securityContext, prepareAnonymousUser());
|
||||
|
||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
||||
final AtomicReference<AuthenticateResponse> responseRef = new AtomicReference<>();
|
||||
|
@ -98,10 +99,12 @@ public class TransportAuthenticateActionTests extends ESTestCase {
|
|||
SecurityContext securityContext = mock(SecurityContext.class);
|
||||
when(securityContext.getAuthentication()).thenReturn(authentication);
|
||||
when(securityContext.getUser()).thenReturn(user);
|
||||
|
||||
final AnonymousUser anonymousUser = prepareAnonymousUser();
|
||||
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
|
||||
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
|
||||
TransportAuthenticateAction action = new TransportAuthenticateAction(transportService,
|
||||
mock(ActionFilters.class), securityContext);
|
||||
mock(ActionFilters.class), securityContext, anonymousUser);
|
||||
|
||||
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
|
||||
final AtomicReference<AuthenticateResponse> responseRef = new AtomicReference<>();
|
||||
|
@ -118,7 +121,35 @@ public class TransportAuthenticateActionTests extends ESTestCase {
|
|||
});
|
||||
|
||||
assertThat(responseRef.get(), notNullValue());
|
||||
assertThat(responseRef.get().authentication(), sameInstance(authentication));
|
||||
if (anonymousUser.enabled()) {
|
||||
final Authentication auth = responseRef.get().authentication();
|
||||
final User authUser = auth.getUser();
|
||||
org.elasticsearch.common.collect.List.of(authUser.roles()).containsAll(
|
||||
org.elasticsearch.common.collect.List.of(authentication.getUser().roles()));
|
||||
org.elasticsearch.common.collect.List.of(authUser.roles()).containsAll(
|
||||
org.elasticsearch.common.collect.List.of(anonymousUser.roles()));
|
||||
assertThat(authUser.authenticatedUser(), sameInstance(user.authenticatedUser()));
|
||||
assertThat(auth.getAuthenticatedBy(), sameInstance(auth.getAuthenticatedBy()));
|
||||
assertThat(auth.getLookedUpBy(), sameInstance(auth.getLookedUpBy()));
|
||||
assertThat(auth.getVersion(), sameInstance(auth.getVersion()));
|
||||
assertThat(auth.getAuthenticationType(), sameInstance(auth.getAuthenticationType()));
|
||||
assertThat(auth.getMetadata(), sameInstance(auth.getMetadata()));
|
||||
} else {
|
||||
assertThat(responseRef.get().authentication(), sameInstance(authentication));
|
||||
}
|
||||
assertThat(throwableRef.get(), nullValue());
|
||||
}
|
||||
|
||||
private AnonymousUser prepareAnonymousUser() {
|
||||
final AnonymousUser anonymousUser = mock(AnonymousUser.class);
|
||||
if (randomBoolean()) {
|
||||
when(anonymousUser.enabled()).thenReturn(true);
|
||||
when(anonymousUser.roles()).thenReturn(
|
||||
randomList(1, 4, () -> randomAlphaOfLengthBetween(4, 12)).toArray(new String[0]));
|
||||
} else {
|
||||
when(anonymousUser.enabled()).thenReturn(false);
|
||||
}
|
||||
return anonymousUser;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -67,8 +67,13 @@ public class RestAuthenticateActionTests extends SecurityIntegTestCase {
|
|||
assertThat(objectPath.evaluate("lookup_realm.type").toString(), equalTo("file"));
|
||||
assertThat(objectPath.evaluate("authentication_type").toString(), equalTo("realm"));
|
||||
List<String> roles = objectPath.evaluate("roles");
|
||||
assertThat(roles.size(), is(1));
|
||||
assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE));
|
||||
if (anonymousEnabled) {
|
||||
assertThat(roles.size(), is(3));
|
||||
assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE, SecuritySettingsSource.TEST_ROLE, "foo"));
|
||||
} else {
|
||||
assertThat(roles.size(), is(1));
|
||||
assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE));
|
||||
}
|
||||
}
|
||||
|
||||
public void testAuthenticateApiWithoutAuthentication() throws Exception {
|
||||
|
|
Loading…
Reference in New Issue