From 97823bd978b4ecd70190ddf28df656f445362b5f Mon Sep 17 00:00:00 2001 From: Petar Dzepina Date: Thu, 13 Apr 2023 23:18:57 +0200 Subject: [PATCH] InjectSecurity - inject User object in UserInfo in threadContext (#396) * Added user_info injection of User object in InjectSecurity Signed-off-by: Petar (cherry picked from commit f7639aac2a3a5692299bf225f866b89f3b0159fa) --- .../opensearch/commons/InjectSecurity.java | 39 ++++++++++++++++++- .../commons/InjectSecurityTest.java | 39 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opensearch/commons/InjectSecurity.java b/src/main/java/org/opensearch/commons/InjectSecurity.java index f2644c29..c6d581f6 100644 --- a/src/main/java/org/opensearch/commons/InjectSecurity.java +++ b/src/main/java/org/opensearch/commons/InjectSecurity.java @@ -10,12 +10,14 @@ import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_USE_INJECTED_USER_FOR_PLUGINS; import java.util.List; +import java.util.StringJoiner; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.common.Strings; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.commons.authuser.User; /** * For background jobs usage only. User or Roles injection can be done using transport layer only. @@ -91,6 +93,7 @@ public InjectSecurity(final String id, final Settings settings, final ThreadCont /** * Injects user or roles, based on opendistro_security_use_injected_user_for_plugins setting. By default injects roles. + * Expects threadContext to be stashed * @param user * @param roles */ @@ -104,7 +107,8 @@ public void inject(final String user, final List roles) { /** * Injects user. - * @param user + * Expects threadContext to be stashed + * @param user name */ public void injectUser(final String user) { if (Strings.isNullOrEmpty(user)) { @@ -115,8 +119,39 @@ public void injectUser(final String user) { threadContext.putTransient(INJECTED_USER, user); log.debug("{}, InjectSecurity - inject roles: {}", Thread.currentThread().getName(), id); } else { - log.error("{}, InjectSecurity- most likely thread context corruption : {}", Thread.currentThread().getName(), id); + log.error("{}, InjectSecurity - most likely thread context corruption : {}", Thread.currentThread().getName(), id); + } + } + + /** + * Injects user object into user info. + * Expects threadContext to be stashed. + * @param user + */ + public void injectUserInfo(final User user) { + if (user == null) { + return; + } + String userObjectAsString = threadContext.getTransient(ConfigConstants.OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT); + if (userObjectAsString != null) { + log + .error( + "{}, InjectSecurity - id: [{}] found existing user_info: {}", + Thread.currentThread().getName(), + id, + userObjectAsString + ); + return; + } + StringJoiner joiner = new StringJoiner("|"); + joiner.add(user.getName()); + joiner.add(java.lang.String.join(",", user.getBackendRoles())); + joiner.add(java.lang.String.join(",", user.getRoles())); + String requestedTenant = user.getRequestedTenant(); + if (!Strings.isNullOrEmpty(requestedTenant)) { + joiner.add(requestedTenant); } + threadContext.putTransient(ConfigConstants.OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT, joiner.toString()); } /** diff --git a/src/test/java/org/opensearch/commons/InjectSecurityTest.java b/src/test/java/org/opensearch/commons/InjectSecurityTest.java index 818aa9c7..b2dea7f3 100644 --- a/src/test/java/org/opensearch/commons/InjectSecurityTest.java +++ b/src/test/java/org/opensearch/commons/InjectSecurityTest.java @@ -12,14 +12,17 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.opensearch.commons.ConfigConstants.INJECTED_USER; import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_INJECTED_ROLES; +import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT; import static org.opensearch.commons.ConfigConstants.OPENSEARCH_SECURITY_USE_INJECTED_USER_FOR_PLUGINS; import java.util.Arrays; import java.util.HashMap; +import java.util.List; import org.junit.jupiter.api.Test; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.commons.authuser.User; public class InjectSecurityTest { @@ -85,6 +88,42 @@ public void testInjectUser() { assertNull(threadContext.getTransient(INJECTED_USER)); } + @Test + public void testInjectUserInfo() { + Settings settings = Settings.builder().build(); + Settings headerSettings = Settings.builder().put("request.headers.default", "1").build(); + ThreadContext threadContext = new ThreadContext(headerSettings); + threadContext.putHeader("name", "opendistro"); + threadContext.putTransient("ctx.name", "plugin"); + + assertEquals("1", threadContext.getHeader("default")); + assertEquals("opendistro", threadContext.getHeader("name")); + assertEquals("plugin", threadContext.getTransient("ctx.name")); + + User user = new User( + "Bob", + List.of("backendRole1", "backendRole2"), + List.of("role1", "role2"), + List.of("attr1", "attr2"), + "tenant1" + ); + try (InjectSecurity helper = new InjectSecurity("test-name", null, threadContext)) { + helper.injectUserInfo(user); + assertEquals("1", threadContext.getHeader("default")); + assertEquals("opendistro", threadContext.getHeader("name")); + assertEquals("plugin", threadContext.getTransient("ctx.name")); + assertNotNull(threadContext.getTransient(OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT)); + assertEquals( + "Bob|backendRole1,backendRole2|role1,role2|tenant1", + threadContext.getTransient(OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT) + ); + } + assertEquals("1", threadContext.getHeader("default")); + assertEquals("opendistro", threadContext.getHeader("name")); + assertEquals("plugin", threadContext.getTransient("ctx.name")); + assertNull(threadContext.getTransient(OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT)); + } + @Test public void testInjectProperty() { Settings settings = Settings.builder().put(OPENSEARCH_SECURITY_USE_INJECTED_USER_FOR_PLUGINS, false).build();