From 082a05746ba979a53d746009b9d2a3fb1c3a429d Mon Sep 17 00:00:00 2001 From: Chenyang Ji Date: Tue, 4 Jun 2024 22:00:03 -0700 Subject: [PATCH] Add default request labeling rules in security plugin Signed-off-by: Chenyang Ji --- .../security/OpenSearchSecurityPlugin.java | 2 + .../labels/DefaultUserInfoLabelingRule.java | 59 ++++++++++++++++++ .../ssl/OpenSearchSecuritySSLPlugin.java | 2 + .../DefaultUserInfoLabelingRuleTests.java | 62 +++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 src/main/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRule.java create mode 100644 src/test/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRuleTests.java diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 36ef7709e8..95678c8a37 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -163,6 +163,7 @@ import org.opensearch.security.http.NonSslHttpServerTransport; import org.opensearch.security.http.XFFResolver; import org.opensearch.security.identity.SecurityTokenManager; +import org.opensearch.security.identity.labels.DefaultUserInfoLabelingRule; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.privileges.PrivilegesInterceptor; import org.opensearch.security.privileges.RestLayerPrivilegesEvaluator; @@ -1208,6 +1209,7 @@ public Collection createComponents( if (!SSLConfig.isSslOnlyMode() && !isDisabled(settings) && allowDefaultInit && useClusterState) { clusterService.addListener(cr); } + components.add(new DefaultUserInfoLabelingRule()); return components; } diff --git a/src/main/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRule.java b/src/main/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRule.java new file mode 100644 index 0000000000..ec5a51ec5c --- /dev/null +++ b/src/main/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRule.java @@ -0,0 +1,59 @@ +package org.opensearch.security.identity.labels; + +import org.apache.commons.lang3.tuple.Pair; +import org.opensearch.action.search.SearchRequest; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.search.labels.rules.Rule; +import org.opensearch.security.dlic.rest.support.Utils; +import org.opensearch.security.user.User; + +import java.util.HashMap; +import java.util.Map; + +/** + * Rules to get user info labels for RuleBasedLabelingService in OpenSearch + */ +public class DefaultUserInfoLabelingRule implements Rule { + public static final String REMOTE_ADDRESS = "remote_address"; + public static final String USER_NAME = "user_name"; + public static final String USER_SECURITY_ROLES = "user_backend_roles"; + public static final String USER_ROLES = "user_roles"; + public static final String USER_TENANT = "user_tenant"; + + /** + * @param threadContext + * @param searchRequest + * @return Map of User related info and the corresponding values + */ + @Override + public Map evaluate(ThreadContext threadContext, SearchRequest searchRequest) { + return getUserInfoFromThreadContext(threadContext); + } + + /** + * Get User info, specifically injected by the security plugin, from the thread context + * + * @param threadContext context of the thread + * @return Map of User related info and the corresponding values + */ + private Map getUserInfoFromThreadContext(ThreadContext threadContext) { + Map userInfoMap = new HashMap<>(); + if (threadContext == null) { + return userInfoMap; + } + final Pair userAndRemoteAddress = Utils.userAndRemoteAddressFrom(threadContext); + TransportAddress remoteAddress = userAndRemoteAddress.getRight(); + if (remoteAddress != null) { + userInfoMap.put(REMOTE_ADDRESS, remoteAddress.toString()); + } + User user = userAndRemoteAddress.getLeft(); + if (user != null) { + userInfoMap.put(USER_NAME, user.getName()); + userInfoMap.put(USER_ROLES, String.join(",", user.getRoles())); + userInfoMap.put(USER_SECURITY_ROLES, String.join(",", user.getSecurityRoles())); + userInfoMap.put(USER_TENANT, user.getRequestedTenant()); + } + return userInfoMap; + } +} diff --git a/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java b/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java index e6a1b47888..4b6b19426d 100644 --- a/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java +++ b/src/main/java/org/opensearch/security/ssl/OpenSearchSecuritySSLPlugin.java @@ -75,6 +75,7 @@ import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.NonValidatingObjectMapper; import org.opensearch.security.filter.SecurityRestFilter; +import org.opensearch.security.identity.labels.DefaultUserInfoLabelingRule; import org.opensearch.security.ssl.http.netty.ValidatingDispatcher; import org.opensearch.security.ssl.rest.SecuritySSLInfoAction; import org.opensearch.security.ssl.transport.DefaultPrincipalExtractor; @@ -401,6 +402,7 @@ public Collection createComponents( } components.add(principalExtractor); + components.add(new DefaultUserInfoLabelingRule()); return components; } diff --git a/src/test/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRuleTests.java b/src/test/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRuleTests.java new file mode 100644 index 0000000000..09b5abc630 --- /dev/null +++ b/src/test/java/org/opensearch/security/identity/labels/DefaultUserInfoLabelingRuleTests.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.security.identity.labels; + +import org.opensearch.action.search.SearchRequest; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.test.OpenSearchTestCase; +import org.junit.Before; + +import java.util.HashMap; +import java.util.Map; + +public class DefaultUserInfoLabelingRuleTests extends OpenSearchTestCase { + private DefaultUserInfoLabelingRule defaultUserInfoLabelingRule; + private ThreadContext threadContext; + private SearchRequest searchRequest; + + @Before + public void setUpVariables() { + defaultUserInfoLabelingRule = new DefaultUserInfoLabelingRule(); + threadContext = new ThreadContext(Settings.EMPTY); + searchRequest = new SearchRequest(); + } + + public void testGetUserInfoFromThreadContext() { + threadContext.putTransient("_opendistro_security_user_info", "user1|role1,role2|group1,group2|tenant1"); + threadContext.putTransient("_opendistro_security_remote_address", "127.0.0.1"); + Map expectedUserInfoMap = new HashMap<>(); + expectedUserInfoMap.put(DefaultUserInfoLabelingRule.REMOTE_ADDRESS, "127.0.0.1"); + expectedUserInfoMap.put(DefaultUserInfoLabelingRule.USER_NAME, "user1"); + expectedUserInfoMap.put(DefaultUserInfoLabelingRule.USER_SECURITY_ROLES, "role1,role2"); + expectedUserInfoMap.put(DefaultUserInfoLabelingRule.USER_ROLES, "group1,group2"); + expectedUserInfoMap.put(DefaultUserInfoLabelingRule.USER_TENANT, "tenant1"); + Map actualUserInfoMap = defaultUserInfoLabelingRule.evaluate(threadContext, searchRequest); + assertEquals(expectedUserInfoMap, actualUserInfoMap); + } + + public void testGetPartialInfoFromThreadContext() { + threadContext.putTransient("_opendistro_security_remote_address", "127.0.0.1"); + Map expectedUserInfoMap = new HashMap<>(); + expectedUserInfoMap.put(DefaultUserInfoLabelingRule.REMOTE_ADDRESS, "127.0.0.1"); + Map actualUserInfoMap = defaultUserInfoLabelingRule.evaluate(threadContext, searchRequest); + assertEquals(expectedUserInfoMap, actualUserInfoMap); + } + + public void testGetUserInfoFromThreadContext_EmptyUserInfo() { + Map actualUserInfoMap = defaultUserInfoLabelingRule.evaluate(threadContext, searchRequest); + assertTrue(actualUserInfoMap.isEmpty()); + } + + public void testGetUserInfoFromThreadContext_NullThreadContext() { + Map userInfoMap = defaultUserInfoLabelingRule.evaluate(null, searchRequest); + assertTrue(userInfoMap.isEmpty()); + } +}