diff --git a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java index 20092e26b4..a03b87c20e 100644 --- a/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java +++ b/src/main/java/org/opensearch/security/dlic/rest/api/AuditApiAction.java @@ -267,12 +267,24 @@ private void auditApiRequestHandlers(RequestHandler.RequestHandlersBuilder reque .onChangeRequest(RestRequest.Method.PATCH, request -> withEnabledAuditApi(request).map(this::processPatchRequest)) .onChangeRequest( RestRequest.Method.PUT, - request -> withEnabledAuditApi(request).map(ignore -> processPutRequest("config", request)) + request -> withCreateOrUpdateAuditApi(request).map(ignore -> processPutRequest("config", request)) ) .override(RestRequest.Method.POST, methodNotImplementedHandler) .override(RestRequest.Method.DELETE, methodNotImplementedHandler); } + /** + * Allows PUT to bootstrap the audit config when the document does not exist, + * or to update it through normal validation when it does. + */ + ValidationResult withCreateOrUpdateAuditApi(final RestRequest request) { + if (!securityApiDependencies.configurationRepository().isAuditHotReloadingEnabled()) { + // Document does not exist — allow PUT to bootstrap the audit config + return ValidationResult.success(request); + } + return withEnabledAuditApi(request); + } + ValidationResult withEnabledAuditApi(final RestRequest request) { if (!securityApiDependencies.configurationRepository().isAuditHotReloadingEnabled()) { return ValidationResult.error(RestStatus.NOT_IMPLEMENTED, methodNotImplementedMessage(request.method())); diff --git a/src/test/java/org/opensearch/security/dlic/rest/api/AuditApiActionValidationTest.java b/src/test/java/org/opensearch/security/dlic/rest/api/AuditApiActionValidationTest.java index 7bf40994bd..7897406054 100644 --- a/src/test/java/org/opensearch/security/dlic/rest/api/AuditApiActionValidationTest.java +++ b/src/test/java/org/opensearch/security/dlic/rest/api/AuditApiActionValidationTest.java @@ -17,6 +17,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.core.rest.RestStatus; +import org.opensearch.rest.RestRequest; import org.opensearch.security.auditlog.config.AuditConfig; import org.opensearch.security.securityconf.impl.CType; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; @@ -42,6 +43,26 @@ public void disabledAuditApi() { } } + @Test + public void putAllowedWhenAuditConfigDocDoesNotExist() { + final var auditApiAction = new AuditApiAction(clusterService, threadPool, securityApiDependencies); + when(configurationRepository.isAuditHotReloadingEnabled()).thenReturn(false); + + // PUT is allowed when the audit config document does not exist (bootstrap case) + final var result = auditApiAction.withCreateOrUpdateAuditApi(FakeRestRequest.builder().withMethod(RestRequest.Method.PUT).build()); + assertTrue(result.isValid()); + } + + @Test + public void putAllowedWhenAuditConfigDocExists() { + final var auditApiAction = new AuditApiAction(clusterService, threadPool, securityApiDependencies); + when(configurationRepository.isAuditHotReloadingEnabled()).thenReturn(true); + + // PUT is also allowed when the audit config document exists (update case) + final var result = auditApiAction.withCreateOrUpdateAuditApi(FakeRestRequest.builder().withMethod(RestRequest.Method.PUT).build()); + assertTrue(result.isValid()); + } + @Test public void enabledAuditApi() { final var auditApiAction = new AuditApiAction(clusterService, threadPool, securityApiDependencies);