Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add default request labeling rules in security plugin #4403

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,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;
Expand Down Expand Up @@ -1212,6 +1213,7 @@ public Collection<Object> createComponents(
if (!SSLConfig.isSslOnlyMode() && !isDisabled(settings) && allowDefaultInit && useClusterState) {
clusterService.addListener(cr);
}
components.add(new DefaultUserInfoLabelingRule());
return components;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all these fields needed? From the linked issue, it seems like only tenant information is needed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @DarshitChanpura ! The tenancy we want to obtain is different from the current tenancy concept (a dashboard concept ?). We want to provide the ability for users to identify "who send what" queries. A request can be sent from a human user (identified by username, roles etc), it can also be sent by some application used by multiple users (identified by username, remote address etc), Thus we want to populate all information that can correctly identify a "source of a request".


/**
* @param threadContext
* @param searchRequest
* @return Map of User related info and the corresponding values
*/
@Override
public Map<String, String> 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<String, String> getUserInfoFromThreadContext(ThreadContext threadContext) {
Map<String, String> userInfoMap = new HashMap<>();
if (threadContext == null) {
return userInfoMap;
}
final Pair<User, TransportAddress> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -401,6 +402,7 @@ public Collection<Object> createComponents(
}

components.add(principalExtractor);
components.add(new DefaultUserInfoLabelingRule());

return components;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String, String> actualUserInfoMap = defaultUserInfoLabelingRule.evaluate(threadContext, searchRequest);
assertEquals(expectedUserInfoMap, actualUserInfoMap);
}

public void testGetPartialInfoFromThreadContext() {
threadContext.putTransient("_opendistro_security_remote_address", "127.0.0.1");
Map<String, String> expectedUserInfoMap = new HashMap<>();
expectedUserInfoMap.put(DefaultUserInfoLabelingRule.REMOTE_ADDRESS, "127.0.0.1");
Map<String, String> actualUserInfoMap = defaultUserInfoLabelingRule.evaluate(threadContext, searchRequest);
assertEquals(expectedUserInfoMap, actualUserInfoMap);
}

public void testGetUserInfoFromThreadContext_EmptyUserInfo() {
ansjcy marked this conversation as resolved.
Show resolved Hide resolved
Map<String, String> actualUserInfoMap = defaultUserInfoLabelingRule.evaluate(threadContext, searchRequest);
assertTrue(actualUserInfoMap.isEmpty());
}

public void testGetUserInfoFromThreadContext_NullThreadContext() {
Map<String, String> userInfoMap = defaultUserInfoLabelingRule.evaluate(null, searchRequest);
assertTrue(userInfoMap.isEmpty());
}
}
Loading