security: extend usage stats

This commit extends the usage stats to include the usage of ssl, ip filtering, auditing,
system key, field and document level security, and the number of roles.

See elastic/elasticsearch#2210

Original commit: elastic/x-pack-elasticsearch@e44c5748ba
This commit is contained in:
jaymode 2016-06-01 07:04:46 -04:00
parent e8c1e7f9d8
commit e861608c59
17 changed files with 443 additions and 32 deletions

View File

@ -141,8 +141,12 @@ public class Security implements ActionPlugin {
}
modules.add(new AuthenticationModule(settings));
modules.add(new AuthorizationModule(settings));
if (enabled == false) {
modules.add(new SecurityModule(settings, securityLicenseState));
modules.add(new CryptoModule(settings));
modules.add(new AuditTrailModule(settings));
modules.add(new SecurityTransportModule(settings));
return modules;
}
@ -152,7 +156,6 @@ public class Security implements ActionPlugin {
securityLicenseState = new SecurityLicenseState();
modules.add(new SecurityModule(settings, securityLicenseState));
modules.add(new CryptoModule(settings));
modules.add(new AuthorizationModule(settings));
modules.add(new AuditTrailModule(settings));
modules.add(new SecurityRestModule(settings));
modules.add(new SecurityActionModule(settings));

View File

@ -13,14 +13,21 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.XPackFeatureSet;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -30,16 +37,33 @@ import java.util.stream.Collectors;
*/
public class SecurityFeatureSet implements XPackFeatureSet {
private final Settings settings;
private final boolean enabled;
private final SecurityLicenseState licenseState;
@Nullable private final Realms realms;
@Nullable
private final Realms realms;
@Nullable
private final RolesStore rolesStore;
@Nullable
private final IPFilter ipFilter;
@Nullable
private final AuditTrailService auditTrailService;
@Nullable
private final CryptoService cryptoService;
@Inject
public SecurityFeatureSet(Settings settings, @Nullable SecurityLicenseState licenseState,
@Nullable Realms realms, NamedWriteableRegistry namedWriteableRegistry) {
@Nullable Realms realms, NamedWriteableRegistry namedWriteableRegistry, @Nullable RolesStore rolesStore,
@Nullable IPFilter ipFilter, @Nullable AuditTrailService auditTrailService,
@Nullable CryptoService cryptoService) {
this.enabled = Security.enabled(settings);
this.licenseState = licenseState;
this.realms = realms;
this.rolesStore = rolesStore;
this.settings = settings;
this.ipFilter = ipFilter;
this.auditTrailService = auditTrailService;
this.cryptoService = cryptoService;
namedWriteableRegistry.register(Usage.class, Usage.writeableName(Security.NAME), Usage::new);
}
@ -66,7 +90,12 @@ public class SecurityFeatureSet implements XPackFeatureSet {
@Override
public XPackFeatureSet.Usage usage() {
List<Map<String, Object>> enabledRealms = buildEnabledRealms(realms);
return new Usage(available(), enabled(), enabledRealms);
Map<String, Object> rolesStoreUsage = rolesStoreUsage(rolesStore);
Map<String, Object> sslUsage = sslUsage(settings);
Map<String, Object> auditUsage = auditUsage(auditTrailService);
Map<String, Object> ipFilterUsage = ipFilterUsage(ipFilter);
boolean hasSystemKey = systemKeyUsage(cryptoService);
return new Usage(available(), enabled(), enabledRealms, rolesStoreUsage, sslUsage, auditUsage, ipFilterUsage, hasSystemKey);
}
static List<Map<String, Object>> buildEnabledRealms(Realms realms) {
@ -84,26 +113,86 @@ public class SecurityFeatureSet implements XPackFeatureSet {
return enabledRealms;
}
static Map<String, Object> rolesStoreUsage(@Nullable RolesStore rolesStore) {
if (rolesStore == null) {
return Collections.emptyMap();
}
return rolesStore.usageStats();
}
static Map<String, Object> sslUsage(Settings settings) {
Map<String, Object> map = new HashMap<>(2);
map.put("http", Collections.singletonMap("enabled", SecurityNettyHttpServerTransport.SSL_SETTING.get(settings)));
map.put("transport", Collections.singletonMap("enabled", SecurityNettyTransport.SSL_SETTING.get(settings)));
return map;
}
static Map<String, Object> auditUsage(@Nullable AuditTrailService auditTrailService) {
if (auditTrailService == null) {
return Collections.emptyMap();
}
return auditTrailService.usageStats();
}
static Map<String, Object> ipFilterUsage(@Nullable IPFilter ipFilter) {
if (ipFilter == null) {
return Collections.emptyMap();
}
return ipFilter.usageStats();
}
static boolean systemKeyUsage(CryptoService cryptoService) {
// we can piggy back on the encryption enabled method as it is only enabled if there is a system key
return cryptoService.encryptionEnabled();
}
static class Usage extends XPackFeatureSet.Usage {
private static final String ENABLED_REALMS_XFIELD = "enabled_realms";
private static final String ROLES_XFIELD = "roles";
private static final String SSL_XFIELD = "ssl";
private static final String AUDIT_XFIELD = "audit";
private static final String IP_FILTER_XFIELD = "ipfilter";
private static final String SYSTEM_KEY_XFIELD = "system_key";
private List<Map<String, Object>> enabledRealms;
private Map<String, Object> rolesStoreUsage;
private Map<String, Object> sslUsage;
private Map<String, Object> auditUsage;
private Map<String, Object> ipFilterUsage;
private boolean hasSystemKey;
public Usage(StreamInput in) throws IOException {
super(in);
enabledRealms = in.readList(StreamInput::readMap);
rolesStoreUsage = in.readMap();
sslUsage = in.readMap();
auditUsage = in.readMap();
ipFilterUsage = in.readMap();
hasSystemKey = in.readBoolean();
}
public Usage(boolean available, boolean enabled, List<Map<String, Object>> enabledRealms) {
public Usage(boolean available, boolean enabled, List<Map<String, Object>> enabledRealms, Map<String, Object> rolesStoreUsage,
Map<String, Object> sslUsage, Map<String, Object> auditUsage, Map<String, Object> ipFilterUsage,
boolean hasSystemKey) {
super(Security.NAME, available, enabled);
this.enabledRealms = enabledRealms;
this.rolesStoreUsage = rolesStoreUsage;
this.sslUsage = sslUsage;
this.auditUsage = auditUsage;
this.ipFilterUsage = ipFilterUsage;
this.hasSystemKey = hasSystemKey;
}
@Override
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeList(enabledRealms.stream().map((m) -> (Writeable) o -> o.writeMap(m)).collect(Collectors.toList()));
out.writeMap(rolesStoreUsage);
out.writeMap(sslUsage);
out.writeMap(auditUsage);
out.writeMap(ipFilterUsage);
out.writeBoolean(hasSystemKey);
}
@Override
@ -111,6 +200,11 @@ public class SecurityFeatureSet implements XPackFeatureSet {
super.innerXContent(builder, params);
if (enabled) {
builder.field(ENABLED_REALMS_XFIELD, enabledRealms);
builder.field(ROLES_XFIELD, rolesStoreUsage);
builder.field(SSL_XFIELD, sslUsage);
builder.field(AUDIT_XFIELD, auditUsage);
builder.field(IP_FILTER_XFIELD, ipFilterUsage);
builder.field(SYSTEM_KEY_XFIELD, hasSystemKey);
}
}
}

View File

@ -7,6 +7,7 @@ package org.elasticsearch.xpack.security.audit;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.inject.multibindings.Multibinder;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
@ -44,18 +45,16 @@ public class AuditTrailModule extends AbstractSecurityModule.Node {
@Override
protected void configureNode() {
if (!enabled) {
bind(AuditTrail.class).toInstance(AuditTrail.NOOP);
return;
}
List<String> outputs = OUTPUTS_SETTING.get(settings);
if (outputs.isEmpty()) {
if (securityEnabled == false || enabled == false || outputs.isEmpty()) {
bind(AuditTrailService.class).toProvider(Providers.of(null));
bind(AuditTrail.class).toInstance(AuditTrail.NOOP);
return;
}
bind(AuditTrail.class).to(AuditTrailService.class).asEagerSingleton();
Multibinder<AuditTrail> binder = Multibinder.newSetBinder(binder(), AuditTrail.class);
bind(AuditTrailService.class).asEagerSingleton();
bind(AuditTrail.class).to(AuditTrailService.class);
Multibinder<AuditTrail> binder = Multibinder.newSetBinder(binder(), AuditTrail.class);
Set<String> uniqueOutputs = Sets.newHashSet(outputs);
for (String output : uniqueOutputs) {
switch (output) {

View File

@ -16,6 +16,8 @@ import org.elasticsearch.xpack.security.transport.filter.SecurityIpFilterRule;
import org.elasticsearch.transport.TransportMessage;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
@ -197,4 +199,11 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail {
}
}
}
public Map<String, Object> usageStats() {
Map<String, Object> map = new HashMap<>(2);
map.put("enabled", AuditTrailModule.ENABLED_SETTING.get(settings));
map.put("outputs", AuditTrailModule.OUTPUTS_SETTING.get(settings));
return map;
}
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.security.authz;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.FileRolesStore;
@ -24,6 +25,10 @@ public class AuthorizationModule extends AbstractSecurityModule.Node {
@Override
protected void configureNode() {
if (securityEnabled == false) {
bind(RolesStore.class).toProvider(Providers.of(null));
return;
}
// First the file and native roles stores must be bound...
bind(ReservedRolesStore.class).asEagerSingleton();

View File

@ -125,7 +125,7 @@ public interface IndicesPermission extends Permission, Iterable<IndicesPermissio
if (group.check(action, indexOrAlias)) {
granted = true;
for (String index : concreteIndices) {
if (group.getFields() != null) {
if (group.hasFields()) {
Set<String> roleFields = rolesFieldsByIndex.get(index);
if (roleFields == null) {
roleFields = new HashSet<>();
@ -133,7 +133,7 @@ public interface IndicesPermission extends Permission, Iterable<IndicesPermissio
}
roleFields.addAll(group.getFields());
}
if (group.getQuery() != null) {
if (group.hasQuery()) {
Set<BytesReference> roleQueries = roleQueriesByIndex.get(index);
if (roleQueries == null) {
roleQueries = new HashSet<>();
@ -330,5 +330,13 @@ public interface IndicesPermission extends Permission, Iterable<IndicesPermissio
assert index != null;
return actionMatcher.test(action) && indexNameMatcher.test(index);
}
public boolean hasFields() {
return fields != null;
}
public boolean hasQuery() {
return query != null;
}
}
}

View File

@ -8,6 +8,9 @@ package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.xpack.security.authz.permission.Role;
import java.util.HashMap;
import java.util.Map;
/**
* A composite roles store that combines built in roles, file-based roles, and index-based roles. Checks the built in roles first, then the
* file roles, and finally the index roles.
@ -40,4 +43,12 @@ public class CompositeRolesStore implements RolesStore {
return nativeRolesStore.role(role);
}
@Override
public Map<String, Object> usageStats() {
Map<String, Object> usage = new HashMap<>(2);
usage.put("file", fileRolesStore.usageStats());
usage.put("native", nativeRolesStore.usageStats());
return usage;
}
}

View File

@ -15,13 +15,13 @@ import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.SettingsModule;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.yaml.YamlXContent;
import org.elasticsearch.env.Environment;
import org.elasticsearch.xpack.security.Security;
import org.elasticsearch.xpack.security.authc.support.RefreshListener;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission.Group;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.support.NoOpLogger;
import org.elasticsearch.xpack.security.support.Validation;
@ -97,6 +97,28 @@ public class FileRolesStore extends AbstractLifecycleComponent implements RolesS
return permissions.get(role);
}
@Override
public Map<String, Object> usageStats() {
Map<String, Object> usageStats = new HashMap<>();
usageStats.put("size", permissions.size());
boolean dls = false;
boolean fls = false;
for (Role role : permissions.values()) {
for (Group group : role.indices()) {
fls = fls || group.hasFields();
dls = dls || group.hasQuery();
}
if (fls && dls) {
break;
}
}
usageStats.put("fls", fls);
usageStats.put("dls", dls);
return usageStats;
}
public static Path resolveFile(Settings settings, Environment env) {
String location = ROLES_FILE_SETTING.get(settings);
if (location.isEmpty()) {

View File

@ -15,6 +15,9 @@ import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.MultiSearchRequestBuilder;
import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.MultiSearchResponse.Item;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
@ -47,6 +50,7 @@ import org.elasticsearch.xpack.security.action.role.ClearRolesCacheResponse;
import org.elasticsearch.xpack.security.action.role.DeleteRoleRequest;
import org.elasticsearch.xpack.security.action.role.PutRoleRequest;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission.Group;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.support.SelfReschedulingRunnable;
@ -56,8 +60,10 @@ import org.elasticsearch.threadpool.ThreadPool.Names;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
@ -329,6 +335,84 @@ public class NativeRolesStore extends AbstractComponent implements RolesStore, C
return roleAndVersion == null ? null : roleAndVersion.getRole();
}
@Override
public Map<String, Object> usageStats() {
if (state() != State.STARTED) {
return Collections.emptyMap();
}
boolean dls = false;
boolean fls = false;
Map<String, Object> usageStats = new HashMap<>();
if (securityIndexExists == false) {
usageStats.put("size", 0L);
usageStats.put("fls", fls);
usageStats.put("dls", dls);
return usageStats;
}
long count = (long) roleCache.size();
for (RoleAndVersion rv : roleCache.values()) {
Role role = rv.getRole();
for (Group group : role.indices()) {
fls = fls || group.hasFields();
dls = dls || group.hasQuery();
}
if (fls && dls) {
break;
}
}
// slow path - query for necessary information
if (fls == false || dls == false) {
MultiSearchRequestBuilder builder = client.prepareMultiSearch()
.add(client.prepareSearch(SecurityTemplateService.SECURITY_INDEX_NAME)
.setTypes(ROLE_DOC_TYPE)
.setQuery(QueryBuilders.matchAllQuery())
.setSize(0));
if (fls == false) {
builder.add(client.prepareSearch(SecurityTemplateService.SECURITY_INDEX_NAME)
.setTypes(ROLE_DOC_TYPE)
.setQuery(QueryBuilders.existsQuery("indices.fields"))
.setSize(0)
.setTerminateAfter(1));
}
if (dls == false) {
builder.add(client.prepareSearch(SecurityTemplateService.SECURITY_INDEX_NAME)
.setTypes(ROLE_DOC_TYPE)
.setQuery(QueryBuilders.existsQuery("indices.query"))
.setSize(0)
.setTerminateAfter(1));
}
MultiSearchResponse multiSearchResponse = builder.get();
int pos = 0;
Item[] responses = multiSearchResponse.getResponses();
if (responses[pos].isFailure() == false) {
count = responses[pos].getResponse().getHits().getTotalHits();
}
if (fls == false) {
if (responses[++pos].isFailure() == false) {
fls = responses[pos].getResponse().getHits().getTotalHits() > 0L;
}
}
if (dls == false) {
if (responses[++pos].isFailure() == false) {
dls = responses[pos].getResponse().getHits().getTotalHits() > 0L;
}
}
}
usageStats.put("size", count);
usageStats.put("fls", fls);
usageStats.put("dls", dls);
return usageStats;
}
private RoleAndVersion getRoleAndVersion(final String roleId) {
RoleAndVersion roleAndVersion = null;
final AtomicReference<GetResponse> getRef = new AtomicReference<>(null);

View File

@ -19,6 +19,8 @@ import org.elasticsearch.xpack.security.user.SystemUser;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
@ -55,6 +57,11 @@ public class ReservedRolesStore implements RolesStore {
}
}
@Override
public Map<String, Object> usageStats() {
return Collections.emptyMap();
}
public RoleDescriptor roleDescriptor(String role) {
switch (role) {
case SuperuserRole.NAME:

View File

@ -7,6 +7,8 @@ package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.xpack.security.authz.permission.Role;
import java.util.Map;
/**
* An interface for looking up a role given a string role name
*/
@ -14,4 +16,5 @@ public interface RolesStore {
Role role(String role);
Map<String, Object> usageStats();
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.security.crypto;
import org.elasticsearch.common.inject.util.Providers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.support.AbstractSecurityModule;
@ -19,6 +20,10 @@ public class CryptoModule extends AbstractSecurityModule.Node {
@Override
protected void configureNode() {
if (securityEnabled == false) {
bind(CryptoService.class).toProvider(Providers.of(null));
return;
}
bind(InternalCryptoService.class).asEagerSingleton();
bind(CryptoService.class).to(InternalCryptoService.class).asEagerSingleton();
}

View File

@ -21,6 +21,11 @@ public class SecurityTransportModule extends AbstractSecurityModule {
@Override
protected void configure(boolean clientMode) {
if (securityEnabled == false) {
bind(IPFilter.class).toProvider(Providers.<IPFilter>of(null));
return;
}
if (clientMode) {
// no ip filtering on the client
bind(IPFilter.class).toProvider(Providers.<IPFilter>of(null));

View File

@ -130,6 +130,13 @@ public class IPFilter {
updateRules();
}
public Map<String, Object> usageStats() {
Map<String, Object> map = new HashMap<>(2);
map.put("http", Collections.singletonMap("enabled", isHttpFilterEnabled));
map.put("transport", Collections.singletonMap("enabled", isIpFilterEnabled));
return map;
}
private void setTransportProfiles(Settings settings) {
transportGroups = settings.getAsGroups();
updateRules();

View File

@ -5,12 +5,19 @@
*/
package org.elasticsearch.xpack.security;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.XPackFeatureSet;
import org.elasticsearch.xpack.security.authz.store.RolesStore;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.transport.filter.IPFilter;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyHttpServerTransport;
import org.elasticsearch.xpack.security.transport.netty.SecurityNettyTransport;
import org.elasticsearch.xpack.watcher.support.xcontent.XContentSource;
import org.junit.Before;
@ -21,6 +28,7 @@ import java.util.List;
import java.util.Map;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.core.Is.is;
import static org.mockito.Matchers.anyObject;
@ -34,24 +42,35 @@ import static org.mockito.Mockito.when;
*/
public class SecurityFeatureSetTests extends ESTestCase {
private Settings settings;
private SecurityLicenseState licenseState;
private Realms realms;
private NamedWriteableRegistry namedWriteableRegistry;
private IPFilter ipFilter;
private RolesStore rolesStore;
private AuditTrailService auditTrail;
private CryptoService cryptoService;
@Before
public void init() throws Exception {
settings = Settings.builder().put("path.home", createTempDir()).build();
licenseState = mock(SecurityLicenseState.class);
realms = mock(Realms.class);
namedWriteableRegistry = mock(NamedWriteableRegistry.class);
ipFilter = mock(IPFilter.class);
rolesStore = mock(RolesStore.class);
auditTrail = mock(AuditTrailService.class);
cryptoService = mock(CryptoService.class);
}
public void testWritableRegistration() throws Exception {
new SecurityFeatureSet(Settings.EMPTY, licenseState, realms, namedWriteableRegistry);
new SecurityFeatureSet(settings, licenseState, realms, namedWriteableRegistry, rolesStore, ipFilter, auditTrail, cryptoService);
verify(namedWriteableRegistry).register(eq(SecurityFeatureSet.Usage.class), eq("xpack.usage.security"), anyObject());
}
public void testAvailable() throws Exception {
SecurityFeatureSet featureSet = new SecurityFeatureSet(Settings.EMPTY, licenseState, realms, namedWriteableRegistry);
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, namedWriteableRegistry, rolesStore,
ipFilter, auditTrail, cryptoService);
boolean available = randomBoolean();
when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(available);
assertThat(featureSet.available(), is(available));
@ -60,27 +79,60 @@ public class SecurityFeatureSetTests extends ESTestCase {
public void testEnabledSetting() throws Exception {
boolean enabled = randomBoolean();
Settings settings = Settings.builder()
.put(this.settings)
.put("xpack.security.enabled", enabled)
.build();
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, namedWriteableRegistry);
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, namedWriteableRegistry, rolesStore,
ipFilter, auditTrail, cryptoService);
assertThat(featureSet.enabled(), is(enabled));
}
public void testEnabledDefault() throws Exception {
SecurityFeatureSet featureSet = new SecurityFeatureSet(Settings.EMPTY, licenseState, realms, namedWriteableRegistry);
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings, licenseState, realms, namedWriteableRegistry, rolesStore,
ipFilter, auditTrail, cryptoService);
assertThat(featureSet.enabled(), is(true));
}
public void testUsage() throws Exception {
boolean available = randomBoolean();
when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(available);
boolean authcAuthzAvailable = randomBoolean();
when(licenseState.authenticationAndAuthorizationEnabled()).thenReturn(authcAuthzAvailable);
Settings.Builder settings = Settings.builder();
Settings.Builder settings = Settings.builder().put(this.settings);
boolean enabled = randomBoolean();
settings.put("xpack.security.enabled", enabled);
final boolean httpSSLEnabled = randomBoolean();
settings.put(SecurityNettyHttpServerTransport.SSL_SETTING.getKey(), httpSSLEnabled);
final boolean transportSSLEnabled = randomBoolean();
settings.put(SecurityNettyTransport.SSL_SETTING.getKey(), transportSSLEnabled);
final boolean auditingEnabled = randomBoolean();
final String[] auditOutputs = randomFrom(new String[] {"logfile"}, new String[] {"index"}, new String[] {"logfile", "index"});
when(auditTrail.usageStats())
.thenReturn(MapBuilder.<String, Object>newMapBuilder()
.put("enabled", auditingEnabled)
.put("outputs", auditOutputs)
.map());
final boolean httpIpFilterEnabled = randomBoolean();
final boolean transportIPFilterEnabled = randomBoolean();
when(ipFilter.usageStats())
.thenReturn(MapBuilder.<String, Object>newMapBuilder()
.put("http", Collections.singletonMap("enabled", httpIpFilterEnabled))
.put("transport", Collections.singletonMap("enabled", transportIPFilterEnabled))
.map());
final boolean rolesStoreEnabled = randomBoolean();
if (rolesStoreEnabled) {
when(rolesStore.usageStats()).thenReturn(Collections.singletonMap("count", 1));
} else {
when(rolesStore.usageStats()).thenReturn(Collections.emptyMap());
}
final boolean useSystemKey = randomBoolean();
when(cryptoService.encryptionEnabled()).thenReturn(useSystemKey);
List<Realm> realmsList= new ArrayList<>();
for (int i = 0; i < 5; i++) {
@ -93,24 +145,49 @@ public class SecurityFeatureSetTests extends ESTestCase {
realmUsage.put("key3", i % 2 == 0);
when(realm.usageStats()).thenReturn(realmUsage);
}
when(realms.iterator()).thenReturn(available ? realmsList.iterator() : Collections.<Realm>emptyIterator());
when(realms.iterator()).thenReturn(authcAuthzAvailable ? realmsList.iterator() : Collections.<Realm>emptyIterator());
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState, realms, namedWriteableRegistry);
SecurityFeatureSet featureSet = new SecurityFeatureSet(settings.build(), licenseState, realms, namedWriteableRegistry, rolesStore,
ipFilter, auditTrail, cryptoService);
XPackFeatureSet.Usage usage = featureSet.usage();
assertThat(usage, is(notNullValue()));
assertThat(usage.name(), is(Security.NAME));
assertThat(usage.enabled(), is(enabled));
assertThat(usage.available(), is(available));
assertThat(usage.available(), is(authcAuthzAvailable));
XContentSource source = new XContentSource(usage);
if (enabled && available) {
for (int i = 0; i < 5; i++) {
assertThat(source.getValue("enabled_realms." + i + ".key1"), is("value" + i));
assertThat(source.getValue("enabled_realms." + i + ".key2"), is(i));
assertThat(source.getValue("enabled_realms." + i + ".key3"), is(i % 2 == 0));
if (enabled) {
if (authcAuthzAvailable) {
for (int i = 0; i < 5; i++) {
assertThat(source.getValue("enabled_realms." + i + ".key1"), is("value" + i));
assertThat(source.getValue("enabled_realms." + i + ".key2"), is(i));
assertThat(source.getValue("enabled_realms." + i + ".key3"), is(i % 2 == 0));
}
} else {
assertThat(source.getValue("enabled_realms"), is(notNullValue()));
}
} else if (enabled) {
assertThat(source.getValue("enabled_realms"), is(notNullValue()));
// check SSL
assertThat(source.getValue("ssl.http.enabled"), is(httpSSLEnabled));
assertThat(source.getValue("ssl.transport.enabled"), is(transportSSLEnabled));
// auditing
assertThat(source.getValue("audit.enabled"), is(auditingEnabled));
assertThat(source.getValue("audit.outputs"), contains(auditOutputs));
// ip filter
assertThat(source.getValue("ipfilter.http.enabled"), is(httpIpFilterEnabled));
assertThat(source.getValue("ipfilter.transport.enabled"), is(transportIPFilterEnabled));
// roles
if (rolesStoreEnabled) {
assertThat(source.getValue("roles.count"), is(1));
} else {
assertThat(((Map) source.getValue("roles")).isEmpty(), is(true));
}
// system key
assertThat(source.getValue("system_key"), is(useSystemKey));
} else {
assertThat(source.getValue("enabled_realms"), is(nullValue()));
}

View File

@ -18,6 +18,7 @@ import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.action.role.DeleteRoleResponse;
import org.elasticsearch.xpack.security.action.role.GetRolesResponse;
import org.elasticsearch.xpack.security.action.role.PutRoleResponse;
import org.elasticsearch.xpack.security.action.user.AuthenticateAction;
import org.elasticsearch.xpack.security.action.user.AuthenticateRequest;
import org.elasticsearch.xpack.security.action.user.AuthenticateResponse;
@ -29,6 +30,7 @@ import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.ElasticUser;
@ -43,6 +45,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
@ -549,4 +552,47 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
.admin().cluster().prepareHealth().get();
assertThat(response.isTimedOut(), is(false));
}
public void testRolesUsageStats() throws Exception {
NativeRolesStore rolesStore = internalCluster().getInstance(NativeRolesStore.class);
Map<String, Object> usage = rolesStore.usageStats();
assertThat(usage.get("size"), is(0L));
assertThat(usage.get("fls"), is(false));
assertThat(usage.get("dls"), is(false));
long roles = 0;
final boolean fls = randomBoolean();
final boolean dls = randomBoolean();
SecurityClient client = new SecurityClient(client());
PutRoleResponse putRoleResponse = client.preparePutRole("admin_role")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"all"}, null, null)
.get();
assertThat(putRoleResponse.isCreated(), is(true));
roles++;
if (fls) {
PutRoleResponse roleResponse = client.preparePutRole("admin_role_fls")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"all"}, new String[] { "foo" }, null)
.get();
assertThat(roleResponse.isCreated(), is(true));
roles++;
}
if (dls) {
PutRoleResponse roleResponse = client.preparePutRole("admin_role_dls")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"all"}, null, new BytesArray("{ \"match_all\": {} }"))
.get();
assertThat(roleResponse.isCreated(), is(true));
roles++;
}
client.prepareClearRolesCache().get();
usage = rolesStore.usageStats();
assertThat(usage.get("size"), is(roles));
assertThat(usage.get("fls"), is(fls));
assertThat(usage.get("dls"), is(dls));
}
}

View File

@ -46,6 +46,7 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.mockito.Mockito.mock;
/**
*
@ -364,4 +365,29 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(messages.get(2).text, containsString("role [kibana] is reserved"));
assertThat(messages.get(3).text, containsString("role [transport_client] is reserved"));
}
public void testUsageStats() throws Exception {
Path roles = getDataPath("roles.yml");
Path tmp = createTempFile();
try (OutputStream stream = Files.newOutputStream(tmp)) {
Files.copy(roles, stream);
}
final boolean flsDlsEnabled = randomBoolean();
Settings settings = Settings.builder()
.put("resource.reload.interval.high", "500ms")
.put(FileRolesStore.ROLES_FILE_SETTING.getKey(), tmp.toAbsolutePath())
.put("path.home", createTempDir())
.put(XPackPlugin.featureEnabledSetting(Security.DLS_FLS_FEATURE), flsDlsEnabled)
.build();
Environment env = new Environment(settings);
FileRolesStore store = new FileRolesStore(settings, env, mock(ResourceWatcherService.class));
store.start();
Map<String, Object> usageStats = store.usageStats();
assertThat(usageStats.get("size"), is(flsDlsEnabled ? 9 : 6));
assertThat(usageStats.get("fls"), is(flsDlsEnabled));
assertThat(usageStats.get("dls"), is(flsDlsEnabled));
}
}