From 8a96cab07cdffd754fb43a91dc1e17033703be77 Mon Sep 17 00:00:00 2001 From: Sam <128482925+samuelcostae@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:43:07 +0100 Subject: [PATCH] Feature/extensions bwc setting (#3180) ### Description This Draft PR includes the new setting bwcPluginMode (backward compatible plugin mode for extensions ) ### Issues Resolved #2616 Is this a backport? If so, please add backport PR # and/or commits # ### Testing [Please provide details of testing done: unit testing, integration testing and manual testing] ### Check List - [ ] New functionality includes testing - [ ] New functionality has been documented - [x] Commits are signed per the DCO using --signoff By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin). --------- Signed-off-by: Sam --- .../security/OpenSearchSecurityPlugin.java | 28 +++++++++++- .../security/authtoken/jwt/JwtVendor.java | 13 +++++- .../security/support/ConfigConstants.java | 5 +++ .../security/authtoken/jwt/JwtVendorTest.java | 45 ++++++++++++++++++- 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 180fab8f68..b40b0186ac 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -106,6 +106,7 @@ import org.opensearch.indices.IndicesService; import org.opensearch.indices.SystemIndexDescriptor; import org.opensearch.plugins.ClusterPlugin; +import org.opensearch.plugins.ExtensionAwarePlugin; import org.opensearch.plugins.MapperPlugin; import org.opensearch.repositories.RepositoriesService; import org.opensearch.rest.RestController; @@ -194,7 +195,15 @@ import org.opensearch.watcher.ResourceWatcherService; // CS-ENFORCE-SINGLE -public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin implements ClusterPlugin, MapperPlugin { +public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin + implements + ClusterPlugin, + MapperPlugin, + // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings + ExtensionAwarePlugin +// CS-ENFORCE-SINGLE + +{ private static final String KEYWORD = ".keyword"; private static final Logger actionTrace = LogManager.getLogger("opendistro_security_action_trace"); @@ -1110,6 +1119,23 @@ public Settings additionalSettings() { } return builder.build(); } + // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings + + @Override + public List> getExtensionSettings() { + List> extensionSettings = new ArrayList>(); + + extensionSettings.add( + Setting.boolSetting( + ConfigConstants.EXTENSIONS_BWC_PLUGIN_MODE, + ConfigConstants.EXTENSIONS_BWC_PLUGIN_MODE_DEFAULT, + Property.ExtensionScope, + Property.Final + ) + ); + return extensionSettings; + } + // CS-ENFORCE-SINGLE: @Override public List> getSettings() { diff --git a/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java b/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java index 21d7caad57..4372e2dfee 100644 --- a/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java +++ b/src/main/java/org/opensearch/security/authtoken/jwt/JwtVendor.java @@ -30,6 +30,7 @@ import org.apache.logging.log4j.Logger; import org.opensearch.common.settings.Settings; +import org.opensearch.security.support.ConfigConstants; public class JwtVendor { private static final Logger logger = LogManager.getLogger(JwtVendor.class); @@ -40,6 +41,7 @@ public class JwtVendor { private final JsonWebKey signingKey; private final JoseJwtProducer jwtProducer; private final LongSupplier timeProvider; + private final Boolean bwcModeEnabled; public JwtVendor(final Settings settings, final Optional timeProvider) { JoseJwtProducer jwtProducer = new JoseJwtProducer(); @@ -59,6 +61,12 @@ public JwtVendor(final Settings settings, final Optional timeProvi } else { this.timeProvider = () -> System.currentTimeMillis() / 1000; } + // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings + this.bwcModeEnabled = settings.getAsBoolean( + ConfigConstants.EXTENSIONS_BWC_PLUGIN_MODE, + ConfigConstants.EXTENSIONS_BWC_PLUGIN_MODE_DEFAULT + ); + // CS-ENFORCE-SINGLE } /* @@ -142,7 +150,10 @@ public String createJwt( throw new Exception("Roles cannot be null"); } - /* TODO: If the backendRoles is not null and the BWC Mode is on, put them into the "dbr" claim */ + if (bwcModeEnabled && backendRoles != null) { + String listOfBackendRoles = String.join(",", backendRoles); + jwtClaims.setProperty("dbr", listOfBackendRoles); + } String encodedJwt = jwtProducer.processJwt(jwt); diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index ee04ff62f3..3b0b6a1091 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -320,6 +320,11 @@ public enum RolesMappingResolution { public static final String TENANCY_GLOBAL_TENANT_NAME = "global"; public static final String TENANCY_GLOBAL_TENANT_DEFAULT_NAME = ""; + // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings + public static final String EXTENSIONS_BWC_PLUGIN_MODE = "bwcPluginMode"; + public static final boolean EXTENSIONS_BWC_PLUGIN_MODE_DEFAULT = false; + // CS-ENFORCE-SINGLE + public static Set getSettingAsSet( final Settings settings, final String key, diff --git a/src/test/java/org/opensearch/security/authtoken/jwt/JwtVendorTest.java b/src/test/java/org/opensearch/security/authtoken/jwt/JwtVendorTest.java index 60ed365285..3477432462 100644 --- a/src/test/java/org/opensearch/security/authtoken/jwt/JwtVendorTest.java +++ b/src/test/java/org/opensearch/security/authtoken/jwt/JwtVendorTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.opensearch.common.settings.Settings; +import org.opensearch.security.support.ConfigConstants; public class JwtVendorTest { @@ -48,7 +49,7 @@ public void testCreateJwtWithRoles() throws Exception { String subject = "admin"; String audience = "audience_0"; List roles = List.of("IT", "HR"); - List backendRoles = List.of("Sales"); + List backendRoles = List.of("Sales", "Support"); String expectedRoles = "IT,HR"; Integer expirySeconds = 300; LongSupplier currentTime = () -> (int) 100; @@ -71,6 +72,48 @@ public void testCreateJwtWithRoles() throws Exception { Assert.assertEquals(expectedExp, jwt.getClaim("exp")); Assert.assertNotEquals(expectedRoles, jwt.getClaim("er")); Assert.assertEquals(expectedRoles, EncryptionDecryptionUtil.decrypt(claimsEncryptionKey, jwt.getClaim("er").toString())); + Assert.assertNull(jwt.getClaim("dbr")); + } + + @Test + public void testCreateJwtWithBackwardsCompatibilityMode() throws Exception { + String issuer = "cluster_0"; + String subject = "admin"; + String audience = "audience_0"; + List roles = List.of("IT", "HR"); + List backendRoles = List.of("Sales", "Support"); + String expectedRoles = "IT,HR"; + String expectedBackendRoles = "Sales,Support"; + + Integer expirySeconds = 300; + LongSupplier currentTime = () -> (int) 100; + String claimsEncryptionKey = RandomStringUtils.randomAlphanumeric(16); + Settings settings = Settings.builder() + .put("signing_key", "abc123") + .put("encryption_key", claimsEncryptionKey) + // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings + .put(ConfigConstants.EXTENSIONS_BWC_PLUGIN_MODE, true) + // CS-ENFORCE-SINGLE + .build(); + Long expectedExp = currentTime.getAsLong() + expirySeconds; + + JwtVendor jwtVendor = new JwtVendor(settings, Optional.of(currentTime)); + String encodedJwt = jwtVendor.createJwt(issuer, subject, audience, expirySeconds, roles, backendRoles); + + JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(encodedJwt); + JwtToken jwt = jwtConsumer.getJwtToken(); + + Assert.assertEquals("obo", jwt.getClaim("typ")); + Assert.assertEquals("cluster_0", jwt.getClaim("iss")); + Assert.assertEquals("admin", jwt.getClaim("sub")); + Assert.assertEquals("audience_0", jwt.getClaim("aud")); + Assert.assertNotNull(jwt.getClaim("iat")); + Assert.assertNotNull(jwt.getClaim("exp")); + Assert.assertEquals(expectedExp, jwt.getClaim("exp")); + Assert.assertNotEquals(expectedRoles, jwt.getClaim("er")); + Assert.assertEquals(expectedRoles, EncryptionDecryptionUtil.decrypt(claimsEncryptionKey, jwt.getClaim("er").toString())); + Assert.assertNotNull(jwt.getClaim("dbr")); + Assert.assertEquals(expectedBackendRoles, jwt.getClaim("dbr")); } @Test(expected = Exception.class)