Skip to content

Commit bb10c8a

Browse files
committed
Add opaque_id to index audit logging (#32260)
Logs opaque_id if it is available with all audit log messages using index-based audit log. Closes #31521
1 parent 765fcc3 commit bb10c8a

File tree

3 files changed

+62
-22
lines changed

3 files changed

+62
-22
lines changed

x-pack/plugin/core/src/main/resources/security_audit_log.json

+3
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@
8080
},
8181
"rule": {
8282
"type": "keyword"
83+
},
84+
"opaque_id": {
85+
"type": "keyword"
8386
}
8487
}
8588
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/audit/index/IndexAuditTrail.java

+7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.elasticsearch.node.Node;
4848
import org.elasticsearch.plugins.Plugin;
4949
import org.elasticsearch.rest.RestRequest;
50+
import org.elasticsearch.tasks.Task;
5051
import org.elasticsearch.threadpool.ThreadPool;
5152
import org.elasticsearch.transport.TransportMessage;
5253
import org.elasticsearch.xpack.core.XPackClientPlugin;
@@ -885,6 +886,12 @@ private XContentBuilder common(String layer, String type, XContentBuilder builde
885886
builder.field(Field.NODE_HOST_ADDRESS, nodeHostAddress);
886887
builder.field(Field.LAYER, layer);
887888
builder.field(Field.TYPE, type);
889+
890+
String opaqueId = threadPool.getThreadContext().getHeader(Task.X_OPAQUE_ID);
891+
if (opaqueId != null) {
892+
builder.field("opaque_id", opaqueId);
893+
}
894+
888895
return builder;
889896
}
890897

x-pack/qa/audit-tests/src/test/java/org/elasticsearch/xpack/security/audit/IndexAuditIT.java

+52-22
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,19 @@
1111
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
1212
import org.elasticsearch.action.search.SearchResponse;
1313
import org.elasticsearch.client.Client;
14+
import org.elasticsearch.client.Request;
15+
import org.elasticsearch.client.RequestOptions;
1416
import org.elasticsearch.client.Response;
1517
import org.elasticsearch.cluster.ClusterState;
1618
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
1719
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
1820
import org.elasticsearch.common.network.NetworkModule;
1921
import org.elasticsearch.common.settings.SecureString;
2022
import org.elasticsearch.common.settings.Settings;
23+
import org.elasticsearch.index.query.QueryBuilder;
2124
import org.elasticsearch.index.query.QueryBuilders;
2225
import org.elasticsearch.plugins.Plugin;
26+
import org.elasticsearch.tasks.Task;
2327
import org.elasticsearch.test.ESIntegTestCase;
2428
import org.elasticsearch.test.TestCluster;
2529
import org.elasticsearch.xpack.core.XPackClientPlugin;
@@ -112,8 +116,53 @@ public void testIndexAuditTrailWorking() throws Exception {
112116
UsernamePasswordToken.basicAuthHeaderValue(USER, new SecureString(PASS.toCharArray()))));
113117
assertThat(response.getStatusLine().getStatusCode(), is(200));
114118
final AtomicReference<ClusterState> lastClusterState = new AtomicReference<>();
119+
final boolean found = awaitSecurityAuditIndex(lastClusterState, QueryBuilders.matchQuery("principal", USER));
120+
121+
assertTrue("Did not find security audit index. Current cluster state:\n" + lastClusterState.get().toString(), found);
122+
123+
SearchResponse searchResponse = client().prepareSearch(".security_audit_log*").setQuery(
124+
QueryBuilders.matchQuery("principal", USER)).get();
125+
assertThat(searchResponse.getHits().getHits().length, greaterThan(0));
126+
assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("principal"), is(USER));
127+
}
128+
129+
public void testAuditTrailTemplateIsRecreatedAfterDelete() throws Exception {
130+
// this is already "tested" by the test framework since we wipe the templates before and after,
131+
// but lets be explicit about the behavior
132+
awaitIndexTemplateCreation();
133+
134+
// delete the template
135+
DeleteIndexTemplateResponse deleteResponse = client().admin().indices()
136+
.prepareDeleteTemplate(IndexAuditTrail.INDEX_TEMPLATE_NAME).execute().actionGet();
137+
assertThat(deleteResponse.isAcknowledged(), is(true));
138+
awaitIndexTemplateCreation();
139+
}
140+
141+
public void testOpaqueIdWorking() throws Exception {
142+
Request request = new Request("GET", "/");
143+
RequestOptions.Builder options = request.getOptions().toBuilder();
144+
options.addHeader(Task.X_OPAQUE_ID, "foo");
145+
options.addHeader(UsernamePasswordToken.BASIC_AUTH_HEADER,
146+
UsernamePasswordToken.basicAuthHeaderValue(USER, new SecureString(PASS.toCharArray())));
147+
request.setOptions(options);
148+
Response response = getRestClient().performRequest(request);
149+
assertThat(response.getStatusLine().getStatusCode(), is(200));
150+
final AtomicReference<ClusterState> lastClusterState = new AtomicReference<>();
151+
final boolean found = awaitSecurityAuditIndex(lastClusterState, QueryBuilders.matchQuery("opaque_id", "foo"));
152+
153+
assertTrue("Did not find security audit index. Current cluster state:\n" + lastClusterState.get().toString(), found);
154+
155+
SearchResponse searchResponse = client().prepareSearch(".security_audit_log*").setQuery(
156+
QueryBuilders.matchQuery("opaque_id", "foo")).get();
157+
assertThat(searchResponse.getHits().getHits().length, greaterThan(0));
158+
159+
assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("opaque_id"), is("foo"));
160+
}
161+
162+
private boolean awaitSecurityAuditIndex(AtomicReference<ClusterState> lastClusterState,
163+
QueryBuilder query) throws InterruptedException {
115164
final AtomicBoolean indexExists = new AtomicBoolean(false);
116-
final boolean found = awaitBusy(() -> {
165+
return awaitBusy(() -> {
117166
if (indexExists.get() == false) {
118167
ClusterState state = client().admin().cluster().prepareState().get().getState();
119168
lastClusterState.set(state);
@@ -138,28 +187,9 @@ public void testIndexAuditTrailWorking() throws Exception {
138187
logger.info("refreshing audit indices");
139188
client().admin().indices().prepareRefresh(".security_audit_log*").get();
140189
logger.info("refreshed audit indices");
141-
return client().prepareSearch(".security_audit_log*").setQuery(QueryBuilders.matchQuery("principal", USER))
142-
.get().getHits().getTotalHits() > 0;
190+
return client().prepareSearch(".security_audit_log*").setQuery(query)
191+
.get().getHits().getTotalHits() > 0;
143192
}, 60L, TimeUnit.SECONDS);
144-
145-
assertTrue("Did not find security audit index. Current cluster state:\n" + lastClusterState.get().toString(), found);
146-
147-
SearchResponse searchResponse = client().prepareSearch(".security_audit_log*").setQuery(
148-
QueryBuilders.matchQuery("principal", USER)).get();
149-
assertThat(searchResponse.getHits().getHits().length, greaterThan(0));
150-
assertThat(searchResponse.getHits().getAt(0).getSourceAsMap().get("principal"), is(USER));
151-
}
152-
153-
public void testAuditTrailTemplateIsRecreatedAfterDelete() throws Exception {
154-
// this is already "tested" by the test framework since we wipe the templates before and after,
155-
// but lets be explicit about the behavior
156-
awaitIndexTemplateCreation();
157-
158-
// delete the template
159-
DeleteIndexTemplateResponse deleteResponse = client().admin().indices()
160-
.prepareDeleteTemplate(IndexAuditTrail.INDEX_TEMPLATE_NAME).execute().actionGet();
161-
assertThat(deleteResponse.isAcknowledged(), is(true));
162-
awaitIndexTemplateCreation();
163193
}
164194

165195
private void awaitIndexTemplateCreation() throws InterruptedException {

0 commit comments

Comments
 (0)