in src/main/java/org/opensearch/security/filter/SecurityFilter.java [162:378]
private <Request extends ActionRequest, Response extends ActionResponse> void apply0(Task task, final String action, Request request,
ActionListener<Response> listener, ActionFilterChain<Request, Response> chain) {
try {
if(threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN) == null) {
threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN, Origin.LOCAL.toString());
}
final ComplianceConfig complianceConfig = auditLog.getComplianceConfig();
if (complianceConfig != null && complianceConfig.isEnabled()) {
attachSourceFieldContext(request);
}
final Set<String> injectedRoles = rolesInjector.injectUserAndRoles(request, action, task, threadContext);
boolean enforcePrivilegesEvaluation = false;
User user = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER);
if(user == null && (user = backendRegistry.authenticate(request, null, task, action)) != null) {
threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, user);
enforcePrivilegesEvaluation = true;
}
final boolean userIsAdmin = isUserAdmin(user, adminDns);
final boolean interClusterRequest = HeaderHelper.isInterClusterRequest(threadContext);
final boolean trustedClusterRequest = HeaderHelper.isTrustedClusterRequest(threadContext);
final boolean confRequest = "true".equals(HeaderHelper.getSafeFromHeader(threadContext, ConfigConstants.OPENDISTRO_SECURITY_CONF_REQUEST_HEADER));
final boolean passThroughRequest = action.startsWith("indices:admin/seq_no")
|| action.equals(WhoAmIAction.NAME);
final boolean internalRequest =
(interClusterRequest || HeaderHelper.isDirectRequest(threadContext))
&& action.startsWith("internal:")
&& !action.startsWith("internal:transport/proxy");
if (user != null) {
org.apache.logging.log4j.ThreadContext.put("user", user.getName());
}
if (isActionTraceEnabled()) {
String count = "";
if(request instanceof BulkRequest) {
count = ""+((BulkRequest) request).requests().size();
}
if(request instanceof MultiGetRequest) {
count = ""+((MultiGetRequest) request).getItems().size();
}
if(request instanceof MultiSearchRequest) {
count = ""+((MultiSearchRequest) request).requests().size();
}
traceAction("Node "+cs.localNode().getName()+" -> "+action+" ("+count+"): userIsAdmin="+userIsAdmin+"/conRequest="+confRequest+"/internalRequest="+internalRequest
+"origin="+threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN)+"/directRequest="+HeaderHelper.isDirectRequest(threadContext)+"/remoteAddress="+request.remoteAddress());
threadContext.putHeader("_opendistro_security_trace"+System.currentTimeMillis()+"#"+UUID.randomUUID().toString(), Thread.currentThread().getName()+" FILTER -> "+"Node "+cs.localNode().getName()+" -> "+action+" userIsAdmin="+userIsAdmin+"/conRequest="+confRequest+"/internalRequest="+internalRequest
+"origin="+threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN)+"/directRequest="+HeaderHelper.isDirectRequest(threadContext)+"/remoteAddress="+request.remoteAddress()+" "+threadContext.getHeaders().entrySet().stream().filter(p->!p.getKey().startsWith("_opendistro_security_trace")).collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())));
}
if(userIsAdmin
|| confRequest
|| internalRequest
|| passThroughRequest){
if(userIsAdmin && !confRequest && !internalRequest && !passThroughRequest) {
auditLog.logGrantedPrivileges(action, request, task);
auditLog.logIndexEvent(action, request, task);
}
chain.proceed(task, action, request, listener);
return;
}
if(immutableIndicesMatcher != WildcardMatcher.NONE) {
boolean isImmutable = false;
if(request instanceof BulkShardRequest) {
for(BulkItemRequest bsr: ((BulkShardRequest) request).items()) {
isImmutable = checkImmutableIndices(bsr.request(), listener);
if(isImmutable) {
break;
}
}
} else {
isImmutable = checkImmutableIndices(request, listener);
}
if(isImmutable) {
return;
}
}
if(Origin.LOCAL.toString().equals(threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN))
&& (interClusterRequest || HeaderHelper.isDirectRequest(threadContext))
&& (injectedRoles == null)
&& !enforcePrivilegesEvaluation
) {
chain.proceed(task, action, request, listener);
return;
}
if(user == null) {
if(action.startsWith("cluster:monitor/state")) {
chain.proceed(task, action, request, listener);
return;
}
boolean skipSecurityIfDualMode = threadContext.getTransient(ConfigConstants.SECURITY_SSL_DUAL_MODE_SKIP_SECURITY) == Boolean.TRUE;
if((interClusterRequest || trustedClusterRequest || request.remoteAddress() == null) && !compatConfig.transportInterClusterAuthEnabled()) {
chain.proceed(task, action, request, listener);
return;
} else if((interClusterRequest || trustedClusterRequest || request.remoteAddress() == null || skipSecurityIfDualMode) && compatConfig.transportInterClusterPassiveAuthEnabled()) {
log.info("Transport auth in passive mode and no user found. Injecting default user");
user = User.DEFAULT_TRANSPORT_USER;
threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, user);
} else {
log.error("No user found for "+ action+" from "+request.remoteAddress()+" "+threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN)+" via "+threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_CHANNEL_TYPE)+" "+threadContext.getHeaders());
listener.onFailure(new OpenSearchSecurityException("No user found for "+action, RestStatus.INTERNAL_SERVER_ERROR));
return;
}
}
final PrivilegesEvaluator eval = evalp;
if (!eval.isInitialized()) {
log.error("OpenSearch Security not initialized for {}", action);
listener.onFailure(new OpenSearchSecurityException("OpenSearch Security not initialized for "
+ action, RestStatus.SERVICE_UNAVAILABLE));
return;
}
if (log.isTraceEnabled()) {
log.trace("Evaluate permissions for user: {}", user.getName());
}
final PrivilegesEvaluatorResponse pres = eval.evaluate(user, action, request, task, injectedRoles);
if (log.isDebugEnabled()) {
log.debug(pres.toString());
}
if (pres.isAllowed()) {
auditLog.logGrantedPrivileges(action, request, task);
auditLog.logIndexEvent(action, request, task);
if(!dlsFlsValve.invoke(action, request, listener, pres.getAllowedFlsFields(), pres.getMaskedFields(), pres.getQueries())) {
return;
}
final CreateIndexRequestBuilder createIndexRequestBuilder = pres.getCreateIndexRequestBuilder();
if (createIndexRequestBuilder == null) {
chain.proceed(task, action, request, listener);
} else {
CreateIndexRequest createIndexRequest = createIndexRequestBuilder.request();
log.info("Request {} requires new tenant index {} with aliases {}",
request.getClass().getSimpleName(), createIndexRequest.index(), alias2Name(createIndexRequest.aliases()));
createIndexRequestBuilder.execute(new ActionListener<CreateIndexResponse>() {
@Override
public void onResponse(CreateIndexResponse createIndexResponse) {
if (createIndexResponse.isAcknowledged()) {
log.debug("Request to create index {} with aliases {} acknowledged, proceeding with {}",
createIndexRequest.index(), alias2Name(createIndexRequest.aliases()), request.getClass().getSimpleName());
chain.proceed(task, action, request, listener);
} else {
String message = LoggerMessageFormat.format("Request to create index {} with aliases {} was not acknowledged, failing {}",
createIndexRequest.index(), alias2Name(createIndexRequest.aliases()), request.getClass().getSimpleName());
log.error(message);
listener.onFailure(new OpenSearchException(message));
}
}
@Override
public void onFailure(Exception e) {
Throwable cause = ExceptionsHelper.unwrapCause(e);
if (cause instanceof ResourceAlreadyExistsException) {
log.warn("Request to create index {} with aliases {} failed as the resource already exists, proceeding with {}",
createIndexRequest.index(), alias2Name(createIndexRequest.aliases()), request.getClass().getSimpleName(), e);
chain.proceed(task, action, request, listener);
} else {
log.error("Request to create index {} with aliases {} failed, failing {}",
createIndexRequest.index(), alias2Name(createIndexRequest.aliases()), request.getClass().getSimpleName(), e);
listener.onFailure(e);
}
}
});
}
} else {
auditLog.logMissingPrivileges(action, request, task);
String err;
if(!pres.getMissingSecurityRoles().isEmpty()) {
err = String.format("No mapping for %s on roles %s", user, pres.getMissingSecurityRoles());
} else {
err = (injectedRoles != null) ?
String.format("no permissions for %s and associated roles %s", pres.getMissingPrivileges(), pres.getResolvedSecurityRoles()) :
String.format("no permissions for %s and %s", pres.getMissingPrivileges(), user);
}
log.debug(err);
listener.onFailure(new OpenSearchSecurityException(err, RestStatus.FORBIDDEN));
}
} catch (OpenSearchException e) {
if (task != null) {
log.debug("Failed to apply filter. Task id: {} ({}). Action: {}", task.getId(), task.getDescription(), action, e);
} else {
log.debug("Failed to apply filter. Action: {}", action, e);
}
listener.onFailure(e);
} catch (Throwable e) {
log.error("Unexpected exception "+e, e);
listener.onFailure(new OpenSearchSecurityException("Unexpected exception " + action, RestStatus.INTERNAL_SERVER_ERROR));
}
}