Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* Provide SecureHttpTransportParameters to complement SecureTransportParameters counterpart ([#5432](https://github.com/opensearch-project/security/pull/5432))
* Use isClusterPerm instead of requestedResolved.isLocalAll() to determine if action is a cluster action ([#5445](https://github.com/opensearch-project/security/pull/5445))
* Fix config update with deprecated config types failing in mixed clusters ([#5456](https://github.com/opensearch-project/security/pull/5456))
* Add serialized user custom attributes to the the thread context ([#5491](https://github.com/opensearch-project/security/pull/5491))

### Refactoring

Expand Down
3 changes: 2 additions & 1 deletion sample-resource-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ ext {
licenseFile = rootProject.file('LICENSE.txt')
noticeFile = rootProject.file('NOTICE.txt')

common_utils_version = System.getProperty("common_utils.version", "3.1.0.0")
common_utils_version = System.getProperty("common_utils.version", "3.2.0.0-SNAPSHOT")
}

repositories {
Expand Down Expand Up @@ -71,6 +71,7 @@ dependencies {
integrationTestImplementation rootProject.sourceSets.integrationTest.output
integrationTestImplementation rootProject.sourceSets.main.output
integrationTestImplementation "org.opensearch.client:opensearch-rest-high-level-client:${opensearch_version}"
integrationTestImplementation 'org.ldaptive:ldaptive:1.2.3'

// To be removed once integration test framework supports extended plugins
integrationTestImplementation project(path: ":${rootProject.name}-spi", configuration: 'shadow')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,16 @@ public class SampleResourcePluginTests {
.anonymousAuth(true)
.authc(AUTHC_HTTPBASIC_INTERNAL)
.users(USER_ADMIN, SHARED_WITH_USER)
.nodeSettings(Map.of(SECURITY_SYSTEM_INDICES_ENABLED_KEY, true, OPENSEARCH_RESOURCE_SHARING_ENABLED, true))
.nodeSettings(
Map.of(
SECURITY_SYSTEM_INDICES_ENABLED_KEY,
true,
OPENSEARCH_RESOURCE_SHARING_ENABLED,
true,
"plugins.security.user_attribute_serialization.enabled",
true
)
)
.build();

@After
Expand All @@ -97,6 +106,34 @@ public void testPluginInstalledCorrectly() {
}
}

@Test
public void testUserSerializationAndDeserialization() throws Exception {
String resourceId;
// create sample resource
try (TestRestClient client = cluster.getRestClient(USER_ADMIN)) {
String sampleResource = """
{"name":"sample"}
""";

TestRestClient.HttpResponse response = client.putJson(SAMPLE_RESOURCE_CREATE_ENDPOINT, sampleResource);
response.assertStatusCode(HttpStatus.SC_OK);

resourceId = response.getTextFromJsonBody("/message").split(":")[1].trim();

Awaitility.await()
.alias("Wait until resource data is populated")
.until(() -> client.get(SAMPLE_RESOURCE_GET_ENDPOINT + "/" + resourceId).getStatusCode(), equalTo(200));
}

try (TestRestClient client = cluster.getRestClient(cluster.getAdminCertificate())) {
Awaitility.await()
.alias("Wait until resource-sharing data is populated")
.until(() -> client.get(RESOURCE_SHARING_INDEX + "/_search").bodyAsJsonNode().get("hits").get("hits").size(), equalTo(1));
HttpResponse sharingEntry = client.get(RESOURCE_SHARING_INDEX + "/_doc/" + resourceId);
System.out.println("sharingEntry: " + sharingEntry.getBody());
}
}

@Test
public void testCreateUpdateDeleteSampleResource() throws Exception {
String resourceId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ protected void doExecute(Task task, CreateResourceRequest request, ActionListene
ThreadContext threadContext = transportService.getThreadPool().getThreadContext();
String userStr = threadContext.getTransient(ConfigConstants.OPENSEARCH_SECURITY_USER_INFO_THREAD_CONTEXT);
User user = User.parse(userStr);
System.out.println("Parsed User: " + user);
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
createResource(request, user, listener);
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,9 +449,8 @@ public int hashCode() {

public static final class User implements UserCredentialsHolder, ToXContentObject {

public final static TestSecurityConfig.User USER_ADMIN = new User("admin").roles(
new Role("allaccess").indexPermissions("*").on("*").clusterPermissions("*")
);
public final static TestSecurityConfig.User USER_ADMIN = new User("admin").attr("attr1", "val1")
.roles(new Role("allaccess").indexPermissions("*").on("*").clusterPermissions("*"));

String name;
private String password;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2222,6 +2222,15 @@ public List<Setting<?>> getSettings() {
Property.Final
)
);

settings.add(
Setting.boolSetting(
ConfigConstants.USER_ATTRIBUTE_SERIALIZATION_ENABLED,
ConfigConstants.USER_ATTRIBUTE_SERIALIZATION_ENABLED_DEFAULT,
Property.NodeScope,
Property.Filtered
)
);
}

return settings;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7;
import org.opensearch.security.securityconf.impl.v7.RoleV7;
import org.opensearch.security.securityconf.impl.v7.TenantV7;
import org.opensearch.security.support.Base64Helper;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.support.WildcardMatcher;
import org.opensearch.security.user.User;
Expand All @@ -113,6 +114,8 @@

import static org.opensearch.security.OpenSearchSecurityPlugin.traceAction;
import static org.opensearch.security.support.ConfigConstants.OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT;
import static org.opensearch.security.support.ConfigConstants.USER_ATTRIBUTE_SERIALIZATION_ENABLED;
import static org.opensearch.security.support.ConfigConstants.USER_ATTRIBUTE_SERIALIZATION_ENABLED_DEFAULT;
import static org.opensearch.security.support.SecurityUtils.escapePipe;

public class PrivilegesEvaluator {
Expand Down Expand Up @@ -278,6 +281,10 @@ public boolean isInitialized() {
return configModel != null && dcm != null && actionPrivileges.get() != null;
}

private boolean isUserAttributeSerializationEnabled() {
return this.settings.getAsBoolean(USER_ATTRIBUTE_SERIALIZATION_ENABLED, USER_ATTRIBUTE_SERIALIZATION_ENABLED_DEFAULT);
}

private void setUserInfoInThreadContext(User user, Set<String> mappedRoles) {
if (threadContext.getTransient(OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT) == null) {
StringJoiner joiner = new StringJoiner("|");
Expand All @@ -289,7 +296,14 @@ private void setUserInfoInThreadContext(User user, Set<String> mappedRoles) {
String requestedTenant = user.getRequestedTenant();
if (!Strings.isNullOrEmpty(requestedTenant)) {
joiner.add(escapePipe(requestedTenant));
} else {
joiner.add("null");
}

if (this.isUserAttributeSerializationEnabled()) {
joiner.add(Base64Helper.serializeObject(user.getCustomAttributesMap()));
}

threadContext.putTransient(OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT, joiner.toString());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,9 @@ public enum RolesMappingResolution {
public static final String SECURITY_CONFIG_VERSION_RETENTION_COUNT = SECURITY_SETTINGS_PREFIX + "config_version.retention_count";
public static final int SECURITY_CONFIG_VERSION_RETENTION_COUNT_DEFAULT = 10;

public static final String USER_ATTRIBUTE_SERIALIZATION_ENABLED = SECURITY_SETTINGS_PREFIX + "user_attribute_serialization.enabled";
public static final boolean USER_ATTRIBUTE_SERIALIZATION_ENABLED_DEFAULT = false;

// On-behalf-of endpoints settings
// CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings
public static final String EXTENSIONS_BWC_PLUGIN_MODE = "bwcPluginMode";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import com.amazon.dlic.auth.ldap.LdapUser;
Expand Down Expand Up @@ -58,10 +59,15 @@ public final class SafeSerializationUtils {
Number.class,
Collection.class,
Map.class,
Enum.class
Enum.class,
ImmutableMap.class
);

private static final Set<String> SAFE_CLASS_NAMES = Collections.singleton("org.ldaptive.LdapAttribute$LdapAttributeValues");
private static final Set<String> SAFE_CLASS_NAMES = Set.of(
"org.ldaptive.LdapAttribute$LdapAttributeValues",
"com.google.common.collect.ImmutableBiMap$SerializedForm",
"com.google.common.collect.ImmutableMap$SerializedForm"
);
static final Map<Class<?>, Boolean> safeClassCache = new ConcurrentHashMap<>();

static boolean isSafeClass(Class<?> cls) {
Expand Down
Loading