Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/134835.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 134835
summary: Add new `/_security/stats` endpoint
area: Authorization
type: enhancement
issues: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"security.get_stats": {
"documentation": {
"url": "https://www.elastic.co/docs/api/doc/elasticsearch/operation/operation-security-get-stats",
"description": "Get security statistics for all nodes"
},
"stability": "stable",
"visibility": "public",
"headers": {
"accept": [
"application/json"
],
"content_type": [
"application/json"
]
},
"url": {
"paths": [
{
"path": "/_security/stats",
"methods": [
"GET"
]
}
]
},
"params": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
9168000
2 changes: 1 addition & 1 deletion server/src/main/resources/transport/upper_bounds/9.2.csv
Original file line number Diff line number Diff line change
@@ -1 +1 @@
index_request_include_tsid,9167000
security_stats_endpoint,9168000
1 change: 1 addition & 0 deletions x-pack/plugin/core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@
exports org.elasticsearch.xpack.core.security.action.token;
exports org.elasticsearch.xpack.core.security.action.user;
exports org.elasticsearch.xpack.core.security.action.settings;
exports org.elasticsearch.xpack.core.security.action.stats;
exports org.elasticsearch.xpack.core.security.action;
exports org.elasticsearch.xpack.core.security.authc.esnative;
exports org.elasticsearch.xpack.core.security.authc.file;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.core.security.action.stats;

import org.elasticsearch.action.ActionType;

public class GetSecurityStatsAction extends ActionType<GetSecurityStatsNodesResponse> {

public static final GetSecurityStatsAction INSTANCE = new GetSecurityStatsAction();
public static final String NAME = "cluster:monitor/xpack/security/stats";

private GetSecurityStatsAction() {
super(NAME);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.core.security.action.stats;

import org.elasticsearch.TransportVersion;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.transport.AbstractTransportRequest;

import java.io.IOException;

public class GetSecurityStatsNodeRequest extends AbstractTransportRequest {

private static final TransportVersion SECURITY_STATS_ENDPOINT = TransportVersion.fromName("security_stats_endpoint");

public GetSecurityStatsNodeRequest() {}

public GetSecurityStatsNodeRequest(final StreamInput in) throws IOException {
super(in);
}

@Override
public void writeTo(final StreamOutput out) throws IOException {
if (out.getTransportVersion().supports(SECURITY_STATS_ENDPOINT) == false) { // shouldn't happen, blocked at RestAction
throw new UnsupportedOperationException("node doesn't support security stats endpoint");
}
super.writeTo(out);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.core.security.action.stats;

import org.elasticsearch.action.support.nodes.BaseNodeResponse;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;

public class GetSecurityStatsNodeResponse extends BaseNodeResponse implements ToXContentObject {

public GetSecurityStatsNodeResponse(final StreamInput in) throws IOException {
super(in);
}

public GetSecurityStatsNodeResponse(final DiscoveryNode node) {
super(node);
}

@Override
public void writeTo(final StreamOutput out) throws IOException {
super.writeTo(out);
}

@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.core.security.action.stats;

import org.elasticsearch.action.support.nodes.BaseNodesRequest;

public class GetSecurityStatsNodesRequest extends BaseNodesRequest {
public GetSecurityStatsNodesRequest() {
super((String[]) null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

package org.elasticsearch.xpack.core.security.action.stats;

import org.elasticsearch.action.FailedNodeException;
import org.elasticsearch.action.support.nodes.BaseNodesResponse;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xcontent.ToXContentObject;
import org.elasticsearch.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.List;

public class GetSecurityStatsNodesResponse extends BaseNodesResponse<GetSecurityStatsNodeResponse> implements ToXContentObject {

public GetSecurityStatsNodesResponse(
final ClusterName clusterName,
final List<GetSecurityStatsNodeResponse> nodes,
final List<FailedNodeException> failures
) {
super(clusterName, nodes, failures);
}

@Override
protected List<GetSecurityStatsNodeResponse> readNodesFrom(final StreamInput in) throws IOException {
return in.readCollectionAsList(GetSecurityStatsNodeResponse::new);
}

@Override
protected void writeNodesTo(final StreamOutput out, final List<GetSecurityStatsNodeResponse> nodes) throws IOException {
out.writeCollection(nodes);
}

@Override
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
builder.startObject();
builder.startObject("nodes");
for (GetSecurityStatsNodeResponse response : getNodes()) {
builder.startObject(response.getNode().getId());
response.toXContent(builder, params);
builder.endObject();
}
builder.endObject();
builder.endObject();
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ public class Constants {
"cluster:monitor/xpack/rollup/get/caps",
"cluster:monitor/xpack/searchable_snapshots/stats",
"cluster:monitor/xpack/security/saml/metadata",
"cluster:monitor/xpack/security/stats",
"cluster:monitor/xpack/spatial/stats",
"cluster:monitor/xpack/sql/async/status", // org.elasticsearch.xpack.core.sql.SqlAsyncActionNames.SQL_ASYNC_GET_STATUS_ACTION_NAME
"cluster:monitor/xpack/sql/stats/dist",
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugin/security/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
exports org.elasticsearch.xpack.security.action.token to org.elasticsearch.server;
exports org.elasticsearch.xpack.security.action.user to org.elasticsearch.server;
exports org.elasticsearch.xpack.security.action.settings to org.elasticsearch.server;
exports org.elasticsearch.xpack.security.action.stats to org.elasticsearch.server;
exports org.elasticsearch.xpack.security.operator to org.elasticsearch.internal.operator, org.elasticsearch.internal.security;
exports org.elasticsearch.xpack.security.authz to org.elasticsearch.internal.security;
exports org.elasticsearch.xpack.security.authc to org.elasticsearch.xcontent, org.elasticsearch.internal.security;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
import org.elasticsearch.xpack.core.security.action.service.GetServiceAccountNodesCredentialsAction;
import org.elasticsearch.xpack.core.security.action.settings.GetSecuritySettingsAction;
import org.elasticsearch.xpack.core.security.action.settings.UpdateSecuritySettingsAction;
import org.elasticsearch.xpack.core.security.action.stats.GetSecurityStatsAction;
import org.elasticsearch.xpack.core.security.action.token.CreateTokenAction;
import org.elasticsearch.xpack.core.security.action.token.InvalidateTokenAction;
import org.elasticsearch.xpack.core.security.action.token.RefreshTokenAction;
Expand Down Expand Up @@ -281,6 +282,7 @@
import org.elasticsearch.xpack.security.action.settings.TransportGetSecuritySettingsAction;
import org.elasticsearch.xpack.security.action.settings.TransportReloadRemoteClusterCredentialsAction;
import org.elasticsearch.xpack.security.action.settings.TransportUpdateSecuritySettingsAction;
import org.elasticsearch.xpack.security.action.stats.TransportSecurityStatsAction;
import org.elasticsearch.xpack.security.action.token.TransportCreateTokenAction;
import org.elasticsearch.xpack.security.action.token.TransportInvalidateTokenAction;
import org.elasticsearch.xpack.security.action.token.TransportRefreshTokenAction;
Expand Down Expand Up @@ -402,6 +404,7 @@
import org.elasticsearch.xpack.security.rest.action.service.RestGetServiceAccountCredentialsAction;
import org.elasticsearch.xpack.security.rest.action.settings.RestGetSecuritySettingsAction;
import org.elasticsearch.xpack.security.rest.action.settings.RestUpdateSecuritySettingsAction;
import org.elasticsearch.xpack.security.rest.action.stats.RestSecurityStatsAction;
import org.elasticsearch.xpack.security.rest.action.user.RestChangePasswordAction;
import org.elasticsearch.xpack.security.rest.action.user.RestDeleteUserAction;
import org.elasticsearch.xpack.security.rest.action.user.RestGetUserPrivilegesAction;
Expand Down Expand Up @@ -1731,6 +1734,7 @@ public List<ActionHandler> getActions() {
new ActionHandler(UpdateSecuritySettingsAction.INSTANCE, TransportUpdateSecuritySettingsAction.class),
new ActionHandler(ActionTypes.RELOAD_REMOTE_CLUSTER_CREDENTIALS_ACTION, TransportReloadRemoteClusterCredentialsAction.class),
new ActionHandler(UpdateIndexMigrationVersionAction.INSTANCE, UpdateIndexMigrationVersionAction.TransportAction.class),
new ActionHandler(GetSecurityStatsAction.INSTANCE, TransportSecurityStatsAction.class),
usageAction,
infoAction
).filter(Objects::nonNull).toList();
Expand Down Expand Up @@ -1823,7 +1827,8 @@ public List<RestHandler> getRestHandlers(
new RestEnableProfileAction(settings, getLicenseState()),
new RestDisableProfileAction(settings, getLicenseState()),
new RestGetSecuritySettingsAction(settings, getLicenseState()),
new RestUpdateSecuritySettingsAction(settings, getLicenseState())
new RestUpdateSecuritySettingsAction(settings, getLicenseState()),
new RestSecurityStatsAction(settings, getLicenseState(), clusterSupportsFeature)
).filter(Objects::nonNull).toList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@

public class SecurityFeatures implements FeatureSpecification {

public static final NodeFeature SECURITY_STATS_ENDPOINT = new NodeFeature("security_stats_endpoint");

@Override
public Set<NodeFeature> getFeatures() {
return Set.of(QUERYABLE_BUILT_IN_ROLES_FEATURE);
return Set.of(QUERYABLE_BUILT_IN_ROLES_FEATURE, SECURITY_STATS_ENDPOINT);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.security.action.stats;

import org.elasticsearch.action.FailedNodeException;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.nodes.TransportNodesAction;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.injection.guice.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.action.stats.GetSecurityStatsAction;
import org.elasticsearch.xpack.core.security.action.stats.GetSecurityStatsNodeRequest;
import org.elasticsearch.xpack.core.security.action.stats.GetSecurityStatsNodeResponse;
import org.elasticsearch.xpack.core.security.action.stats.GetSecurityStatsNodesRequest;
import org.elasticsearch.xpack.core.security.action.stats.GetSecurityStatsNodesResponse;

import java.io.IOException;
import java.util.List;

public class TransportSecurityStatsAction extends TransportNodesAction<
GetSecurityStatsNodesRequest,
GetSecurityStatsNodesResponse,
GetSecurityStatsNodeRequest,
GetSecurityStatsNodeResponse,
Void> {

@Inject
public TransportSecurityStatsAction(
ThreadPool threadPool,
ClusterService clusterService,
TransportService transportService,
ActionFilters actionFilters
) {
super(
GetSecurityStatsAction.INSTANCE.name(),
clusterService,
transportService,
actionFilters,
GetSecurityStatsNodeRequest::new,
threadPool.executor(ThreadPool.Names.MANAGEMENT)
);
}

@Override
protected GetSecurityStatsNodesResponse newResponse(
final GetSecurityStatsNodesRequest request,
final List<GetSecurityStatsNodeResponse> responses,
final List<FailedNodeException> failures
) {
return new GetSecurityStatsNodesResponse(clusterService.getClusterName(), responses, failures);
}

@Override
protected GetSecurityStatsNodeRequest newNodeRequest(final GetSecurityStatsNodesRequest request) {
return new GetSecurityStatsNodeRequest();
}

@Override
protected GetSecurityStatsNodeResponse newNodeResponse(final StreamInput in, final DiscoveryNode node) throws IOException {
return new GetSecurityStatsNodeResponse(in);
}

@Override
protected GetSecurityStatsNodeResponse nodeOperation(final GetSecurityStatsNodeRequest request, final Task task) {
return new GetSecurityStatsNodeResponse(clusterService.localNode());
}
}
Loading