From e2370ca5f76873c6295ceebac48cccf1abb973de Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Thu, 14 Sep 2023 12:07:35 -0400 Subject: [PATCH] No tests but everything else Signed-off-by: Stephen Crawford --- .../auth/http/jwt/HTTPJwtAuthenticator.java | 88 +++---------------- .../security/OpenSearchSecurityPlugin.java | 12 ++- .../security/auth/BackendRegistry.java | 6 ++ .../internal/NoOpAuthenticationBackend.java | 5 +- .../configuration/ClusterInfoHolder.java | 9 ++ .../security/securityconf/ConfigModelV7.java | 52 +++++++++++ .../securityconf/DynamicConfigFactory.java | 4 +- .../securityconf/DynamicConfigModel.java | 3 + .../securityconf/DynamicConfigModelV6.java | 5 ++ .../securityconf/DynamicConfigModelV7.java | 30 ++++++- .../securityconf/impl/v6/ConfigV6.java | 47 ++++++++++ .../securityconf/impl/v7/ConfigV7.java | 3 + .../security/ssl/util/ExceptionUtils.java | 12 +++ .../security/support/ConfigConstants.java | 6 ++ .../security/user/AuthCredentials.java | 22 +++++ 15 files changed, 224 insertions(+), 80 deletions(-) diff --git a/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java index 38bc006e92..03e385d5c0 100644 --- a/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/jwt/HTTPJwtAuthenticator.java @@ -13,13 +13,7 @@ import java.nio.file.Path; import java.security.AccessController; -import java.security.Key; -import java.security.KeyFactory; -import java.security.NoSuchAlgorithmException; import java.security.PrivilegedAction; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; import java.util.Collection; import java.util.Map.Entry; import java.util.regex.Pattern; @@ -27,10 +21,8 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtParser; import io.jsonwebtoken.JwtParserBuilder; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.WeakKeyException; -import org.apache.http.HttpHeaders; +import org.apache.hc.core5.http.HttpHeaders; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -44,6 +36,7 @@ import org.opensearch.core.rest.RestStatus; import org.opensearch.security.auth.HTTPAuthenticator; import org.opensearch.security.user.AuthCredentials; +import org.opensearch.security.util.KeyUtils; public class HTTPJwtAuthenticator implements HTTPAuthenticator { @@ -64,45 +57,7 @@ public class HTTPJwtAuthenticator implements HTTPAuthenticator { public HTTPJwtAuthenticator(final Settings settings, final Path configPath) { super(); - final JwtParserBuilder _jwtParserBuilder = Jwts.parserBuilder(); - - try { - String signingKey = settings.get("signing_key"); - - if (signingKey == null || signingKey.length() == 0) { - log.error("signingKey must not be null or empty. JWT authentication will not work"); - } else { - - signingKey = signingKey.replace("-----BEGIN PUBLIC KEY-----\n", ""); - signingKey = signingKey.replace("-----END PUBLIC KEY-----", ""); - - byte[] decoded = Decoders.BASE64.decode(signingKey); - Key key = null; - - try { - key = getPublicKey(decoded, "RSA"); - } catch (Exception e) { - log.debug("No public RSA key, try other algos ({})", e.toString()); - } - - try { - key = getPublicKey(decoded, "EC"); - } catch (Exception e) { - log.debug("No public ECDSA key, try other algos ({})", e.toString()); - } - - if (key != null) { - _jwtParserBuilder.setSigningKey(key); - } else { - _jwtParserBuilder.setSigningKey(decoded); - } - - } - } catch (Throwable e) { - log.error("Error creating JWT authenticator. JWT authentication will not work", e); - throw new RuntimeException(e); - } - + String signingKey = settings.get("signing_key"); jwtUrlParameter = settings.get("jwt_url_parameter"); jwtHeaderName = settings.get("jwt_header", HttpHeaders.AUTHORIZATION); isDefaultAuthHeader = HttpHeaders.AUTHORIZATION.equalsIgnoreCase(jwtHeaderName); @@ -111,28 +66,20 @@ public HTTPJwtAuthenticator(final Settings settings, final Path configPath) { requireAudience = settings.get("required_audience"); requireIssuer = settings.get("required_issuer"); - if (requireAudience != null) { - _jwtParserBuilder.requireAudience(requireAudience); - } - - if (requireIssuer != null) { - _jwtParserBuilder.requireIssuer(requireIssuer); - } - - final SecurityManager sm = System.getSecurityManager(); - - if (sm != null) { - sm.checkPermission(new SpecialPermission()); - } + JwtParserBuilder jwtParserBuilder = KeyUtils.createJwtParserBuilderFromSigningKey(signingKey, log); + if (jwtParserBuilder == null) { + jwtParser = null; + } else { + if (requireAudience != null) { + jwtParserBuilder = jwtParserBuilder.require("aud", requireAudience); + } - JwtParser parser = AccessController.doPrivileged(new PrivilegedAction() { - @Override - public JwtParser run() { - return _jwtParserBuilder.build(); + if (requireIssuer != null) { + jwtParserBuilder = jwtParserBuilder.require("iss", requireIssuer); } - }); - jwtParser = parser; + jwtParser = jwtParserBuilder.build(); + } } @Override @@ -296,11 +243,4 @@ protected String[] extractRoles(final Claims claims, final RestRequest request) return roles; } - private static PublicKey getPublicKey(final byte[] keyBytes, final String algo) throws NoSuchAlgorithmException, - InvalidKeySpecException { - X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes); - KeyFactory kf = KeyFactory.getInstance(algo); - return kf.generatePublic(spec); - } - } diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 54f47841ff..ef01662618 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -105,6 +105,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; @@ -117,6 +118,7 @@ import org.opensearch.search.query.QuerySearchResult; import org.opensearch.security.action.configupdate.ConfigUpdateAction; import org.opensearch.security.action.configupdate.TransportConfigUpdateAction; +import org.opensearch.security.action.onbehalf.CreateOnBehalfOfTokenAction; import org.opensearch.security.action.whoami.TransportWhoAmIAction; import org.opensearch.security.action.whoami.WhoAmIAction; import org.opensearch.security.auditlog.AuditLog; @@ -190,7 +192,7 @@ import org.opensearch.transport.TransportService; import org.opensearch.watcher.ResourceWatcherService; -public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin implements ClusterPlugin, MapperPlugin { +public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin implements ClusterPlugin, MapperPlugin, ExtensionAwarePlugin { private static final String KEYWORD = ".keyword"; private static final Logger actionTrace = LogManager.getLogger("opendistro_security_action_trace"); @@ -210,6 +212,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin private volatile ClusterService cs; private volatile AtomicReference localNode = new AtomicReference<>(); private volatile AuditLog auditLog; + private volatile DynamicConfigFactory dcf; private volatile BackendRegistry backendRegistry; private volatile SslExceptionHandler sslExceptionHandler; private volatile Client localClient; @@ -537,6 +540,9 @@ public List getRestHandlers( principalExtractor ) ); + CreateOnBehalfOfTokenAction cobot = new CreateOnBehalfOfTokenAction(settings, threadPool, Objects.requireNonNull(cs)); + dcf.registerDCFListener(cobot); + handlers.add(cobot); handlers.addAll( SecurityRestApiActions.getHandler( settings, @@ -948,7 +954,7 @@ public Collection createComponents( // Register opensearch dynamic settings transportPassiveAuthSetting.registerClusterSettingsChangeListener(clusterService.getClusterSettings()); - final ClusterInfoHolder cih = new ClusterInfoHolder(); + final ClusterInfoHolder cih = new ClusterInfoHolder(this.cs.getClusterName().value()); this.cs.addListener(cih); this.salt = Salt.from(settings); @@ -1036,7 +1042,7 @@ public Collection createComponents( compatConfig ); - final DynamicConfigFactory dcf = new DynamicConfigFactory(cr, settings, configPath, localClient, threadPool, cih); + dcf = new DynamicConfigFactory(cr, settings, configPath, localClient, threadPool, cih); dcf.registerDCFListener(backendRegistry); dcf.registerDCFListener(compatConfig); dcf.registerDCFListener(irr); diff --git a/src/main/java/org/opensearch/security/auth/BackendRegistry.java b/src/main/java/org/opensearch/security/auth/BackendRegistry.java index 9721664c70..b8fd8524a5 100644 --- a/src/main/java/org/opensearch/security/auth/BackendRegistry.java +++ b/src/main/java/org/opensearch/security/auth/BackendRegistry.java @@ -59,6 +59,7 @@ import org.opensearch.security.auth.blocking.ClientBlockRegistry; import org.opensearch.security.auth.internal.NoOpAuthenticationBackend; import org.opensearch.security.configuration.AdminDNs; +import org.opensearch.security.http.OnBehalfOfAuthenticator; import org.opensearch.security.http.XFFResolver; import org.opensearch.security.securityconf.DynamicConfigModel; import org.opensearch.security.ssl.util.Utils; @@ -605,7 +606,12 @@ private User impersonate(final RestRequest request, final User originalUser) thr final boolean isDebugEnabled = log.isDebugEnabled(); // loop over all http/rest auth domains for (final AuthDomain authDomain : restAuthDomains) { + final AuthenticationBackend authenticationBackend = authDomain.getBackend(); + // Skip over the OnBehalfOfAuthenticator since it is not compatible for user impersonation + if (authDomain.getHttpAuthenticator() instanceof OnBehalfOfAuthenticator) { + continue; + } final User impersonatedUser = checkExistsAndAuthz( restImpersonationCache, new User(impersonatedUserHeader), diff --git a/src/main/java/org/opensearch/security/auth/internal/NoOpAuthenticationBackend.java b/src/main/java/org/opensearch/security/auth/internal/NoOpAuthenticationBackend.java index 1f149aabcf..807312b3c8 100644 --- a/src/main/java/org/opensearch/security/auth/internal/NoOpAuthenticationBackend.java +++ b/src/main/java/org/opensearch/security/auth/internal/NoOpAuthenticationBackend.java @@ -46,9 +46,12 @@ public String getType() { @Override public User authenticate(final AuthCredentials credentials) { - return new User(credentials.getUsername(), credentials.getBackendRoles(), credentials); + User user = new User(credentials.getUsername(), credentials.getBackendRoles(), credentials); + user.addSecurityRoles(credentials.getSecurityRoles()); + return user; } + @Override public boolean exists(User user) { return true; diff --git a/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java b/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java index 78448fb732..3065ab75a2 100644 --- a/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java +++ b/src/main/java/org/opensearch/security/configuration/ClusterInfoHolder.java @@ -49,6 +49,11 @@ public class ClusterInfoHolder implements ClusterStateListener { private volatile DiscoveryNodes nodes = null; private volatile Boolean isLocalNodeElectedClusterManager = null; private volatile boolean initialized; + private final String clusterName; + + public ClusterInfoHolder(String clusterName) { + this.clusterName = clusterName; + } @Override public void clusterChanged(ClusterChangedEvent event) { @@ -80,6 +85,10 @@ public void clusterChanged(ClusterChangedEvent event) { isLocalNodeElectedClusterManager = event.localNodeClusterManager() ? Boolean.TRUE : Boolean.FALSE; } + public String getClusterName() { + return this.clusterName; + } + public Boolean getHas6xNodes() { return has6xNodes; } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index fe6bb9ff4f..ed947781bf 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -17,6 +17,8 @@ package org.opensearch.security.securityconf; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -37,6 +39,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonProperty; + import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableSet; @@ -57,6 +61,7 @@ import org.opensearch.core.common.transport.TransportAddress; import org.opensearch.common.util.set.Sets; import org.opensearch.core.xcontent.NamedXContentRegistry; +import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration; import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7; @@ -563,6 +568,53 @@ private boolean containsDlsFlsConfig() { } } + public static class OnBehalfOfSettings { + @JsonProperty("enabled") + private Boolean oboEnabled = Boolean.TRUE; + @JsonProperty("signing_key") + private String signingKey; + @JsonProperty("encryption_key") + private String encryptionKey; + + @JsonIgnore + public String configAsJson() { + try { + return DefaultObjectMapper.writeValueAsString(this, false); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public Boolean getOboEnabled() { + return oboEnabled; + } + + public void setOboEnabled(Boolean oboEnabled) { + this.oboEnabled = oboEnabled; + } + + public String getSigningKey() { + return signingKey; + } + + public void setSigningKey(String signingKey) { + this.signingKey = signingKey; + } + + public String getEncryptionKey() { + return encryptionKey; + } + + public void setEncryptionKey(String encryptionKey) { + this.encryptionKey = encryptionKey; + } + + @Override + public String toString() { + return "OnBehalfOfSettings [ enabled=" + oboEnabled + ", signing_key=" + signingKey + ", encryption_key=" + encryptionKey + "]"; + } + } + public static class SecurityRole { private final String name; private final Set ipatterns; diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java index 106bd7956b..dd9ca12253 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigFactory.java @@ -127,6 +127,7 @@ public final static SecurityDynamicConfiguration addStatics(SecurityDynamicCo private final Settings opensearchSettings; private final Path configPath; private final InternalAuthenticationBackend iab = new InternalAuthenticationBackend(); + private final ClusterInfoHolder cih; SecurityDynamicConfiguration config; @@ -142,6 +143,7 @@ public DynamicConfigFactory( this.cr = cr; this.opensearchSettings = opensearchSettings; this.configPath = configPath; + this.cih = cih; if (opensearchSettings.getAsBoolean(ConfigConstants.SECURITY_UNSUPPORTED_LOAD_STATIC_RESOURCES, true)) { try { @@ -278,7 +280,7 @@ public void onChange(Map> typeToConfig) { ); // rebuild v7 Models - dcm = new DynamicConfigModelV7(getConfigV7(config), opensearchSettings, configPath, iab); + dcm = new DynamicConfigModelV7(getConfigV7(config), opensearchSettings, configPath, iab, cih); ium = new InternalUsersModelV7( (SecurityDynamicConfiguration) internalusers, (SecurityDynamicConfiguration) roles, diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java index 08976f2013..e3d10878da 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModel.java @@ -38,6 +38,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.opensearch.common.settings.Settings; import org.opensearch.security.auth.AuthDomain; import org.opensearch.security.auth.AuthFailureListener; import org.opensearch.security.auth.AuthorizationBackend; @@ -104,6 +105,8 @@ public abstract class DynamicConfigModel { public abstract Multimap> getAuthBackendClientBlockRegistries(); + public abstract Settings getDynamicOnBehalfOfSettings(); + protected final Map authImplMap = new HashMap<>(); public DynamicConfigModel() { diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java index 994989416b..78344d0451 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV6.java @@ -207,6 +207,11 @@ public Multimap> getAuthBackendClientBlockRe return Multimaps.unmodifiableMultimap(authBackendClientBlockRegistries); } + @Override + public Settings getDynamicOnBehalfOfSettings() { + return Settings.EMPTY; + } + private void buildAAA() { final SortedSet restAuthDomains0 = new TreeSet<>(); diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java index f6bbcc2161..1e9080b8d4 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java @@ -60,6 +60,9 @@ import org.opensearch.security.securityconf.impl.v7.ConfigV7.Authz; import org.opensearch.security.securityconf.impl.v7.ConfigV7.AuthzDomain; import org.opensearch.security.support.ReflectionHelper; +import org.opensearch.security.auth.internal.NoOpAuthenticationBackend; +import org.opensearch.security.configuration.ClusterInfoHolder; +import org.opensearch.security.http.OnBehalfOfAuthenticator; public class DynamicConfigModelV7 extends DynamicConfigModel { @@ -72,18 +75,20 @@ public class DynamicConfigModelV7 extends DynamicConfigModel { private Set transportAuthorizers; private List destroyableComponents; private final InternalAuthenticationBackend iab; + private final ClusterInfoHolder cih; private List ipAuthFailureListeners; private Multimap authBackendFailureListeners; private List> ipClientBlockRegistries; private Multimap> authBackendClientBlockRegistries; - public DynamicConfigModelV7(ConfigV7 config, Settings opensearchSettings, Path configPath, InternalAuthenticationBackend iab) { + public DynamicConfigModelV7(ConfigV7 config, Settings opensearchSettings, Path configPath, InternalAuthenticationBackend iab, ClusterInfoHolder cih) { super(); this.config = config; this.opensearchSettings = opensearchSettings; this.configPath = configPath; this.iab = iab; + this.cih = cih; buildAAA(); } @@ -207,6 +212,13 @@ public Multimap> getAuthBackendClientBlockRe return Multimaps.unmodifiableMultimap(authBackendClientBlockRegistries); } + @Override + public Settings getDynamicOnBehalfOfSettings() { + return Settings.builder() + .put(Settings.builder().loadFromSource(config.dynamic.on_behalf_of.configAsJson(), XContentType.JSON).build()) + .build(); + } + private void buildAAA() { final SortedSet restAuthDomains0 = new TreeSet<>(); @@ -355,6 +367,22 @@ private void buildAAA() { } } + /* + * If the OnBehalfOf (OBO) authentication is configured: + * Add the OBO authbackend in to the auth domains + * Challenge: false - no need to iterate through the auth domains again when OBO authentication failed + * order: -1 - prioritize the OBO authentication when it gets enabled + */ + Settings oboSettings = getDynamicOnBehalfOfSettings(); + if (oboSettings.get("signing_key") != null && oboSettings.get("encryption_key") != null) { + final AuthDomain _ad = new AuthDomain( + new NoOpAuthenticationBackend(Settings.EMPTY, null), + new OnBehalfOfAuthenticator(getDynamicOnBehalfOfSettings(), this.cih.getClusterName()), + false, + -1 ); + restAuthDomains0.add(_ad); + } + List originalDestroyableComponents = destroyableComponents; restAuthDomains = Collections.unmodifiableSortedSet(restAuthDomains0); diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java b/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java index c85e69fb0d..20e58a5bb7 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v6/ConfigV6.java @@ -37,6 +37,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.annotation.JsonProperty; import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auth.internal.InternalAuthenticationBackend; @@ -356,4 +357,50 @@ public String toString() { } + public static class OnBehalfOfSettings { + @JsonProperty("enabled") + private Boolean oboEnabled = Boolean.TRUE; + @JsonProperty("signing_key") + private String signingKey; + @JsonProperty("encryption_key") + private String encryptionKey; + + @JsonIgnore + public String configAsJson() { + try { + return DefaultObjectMapper.writeValueAsString(this, false); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public Boolean getOboEnabled() { + return oboEnabled; + } + + public void setOboEnabled(Boolean oboEnabled) { + this.oboEnabled = oboEnabled; + } + + public String getSigningKey() { + return signingKey; + } + + public void setSigningKey(String signingKey) { + this.signingKey = signingKey; + } + public String getEncryptionKey() { + return encryptionKey; + } + + + public void setEncryptionKey(String encryptionKey) { + this.encryptionKey = encryptionKey; + } + + @Override + public String toString() { + return "OnBehalfOfSettings [ enabled=" + oboEnabled + ", signing_key=" + signingKey + ", encryption_key=" + encryptionKey + "]"; + } + } } diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java index 87de6a31b0..6f95d81d47 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/ConfigV7.java @@ -41,6 +41,7 @@ import org.opensearch.security.DefaultObjectMapper; import org.opensearch.security.auth.internal.InternalAuthenticationBackend; +import org.opensearch.security.securityconf.ConfigModelV7; import org.opensearch.security.securityconf.impl.v6.ConfigV6; public class ConfigV7 { @@ -65,6 +66,7 @@ public ConfigV7(ConfigV6 c6) { dynamic.hosts_resolver_mode = c6.dynamic.hosts_resolver_mode; dynamic.transport_userrname_attribute = c6.dynamic.transport_userrname_attribute; + dynamic.kibana = new Kibana(); dynamic.kibana.index = c6.dynamic.kibana.index; @@ -133,6 +135,7 @@ public static class Dynamic { public String hosts_resolver_mode = "ip-only"; public String transport_userrname_attribute; public boolean do_not_fail_on_forbidden_empty; + public ConfigModelV7.OnBehalfOfSettings on_behalf_of = new ConfigModelV7.OnBehalfOfSettings(); @Override public String toString() { diff --git a/src/main/java/org/opensearch/security/ssl/util/ExceptionUtils.java b/src/main/java/org/opensearch/security/ssl/util/ExceptionUtils.java index 9d6d3dade8..ed8321323c 100644 --- a/src/main/java/org/opensearch/security/ssl/util/ExceptionUtils.java +++ b/src/main/java/org/opensearch/security/ssl/util/ExceptionUtils.java @@ -67,4 +67,16 @@ public static OpenSearchException createBadHeaderException() { public static OpenSearchException createTransportClientNoLongerSupportedException() { return new OpenSearchException("Transport client authentication no longer supported."); } + + public static OpenSearchException invalidUsageOfOBOTokenException() { + return new OpenSearchException("On-Behalf-Of Token is not allowed to be used for accessing this endpoint."); + } + + public static OpenSearchException createJwkCreationException() { + return new OpenSearchException("An error occurred during the creation of Jwk."); + } + + public static OpenSearchException createJwkCreationException(Throwable cause) { + return new OpenSearchException("An error occurred during the creation of Jwk: {}", cause, cause.getMessage()); + } } diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index e5cc566133..1475ceb17c 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -321,6 +321,12 @@ public enum RolesMappingResolution { public static final String TENANCY_GLOBAL_TENANT_NAME = "global"; public static final String TENANCY_GLOBAL_TENANT_DEFAULT_NAME = ""; + // On-behalf-of endpoints settings + // 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/main/java/org/opensearch/security/user/AuthCredentials.java b/src/main/java/org/opensearch/security/user/AuthCredentials.java index cab3eab6fd..d25f287603 100644 --- a/src/main/java/org/opensearch/security/user/AuthCredentials.java +++ b/src/main/java/org/opensearch/security/user/AuthCredentials.java @@ -32,6 +32,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -48,6 +49,7 @@ public final class AuthCredentials { private final String username; private byte[] password; private Object nativeCredentials; + private final Set securityRoles = new HashSet(); private final Set backendRoles = new HashSet(); private boolean complete; private final byte[] internalPasswordHash; @@ -94,6 +96,18 @@ public AuthCredentials(final String username, String... backendRoles) { this(username, null, null, backendRoles); } + /** + * Create new credentials with a username, a initial optional set of roles and empty password/native credentials + * @param username The username, must not be null or empty + * @param securityRoles The internal roles the user has been mapped to + * @param backendRoles set of roles this user is a member of + * @throws IllegalArgumentException if username is null or empty + */ + public AuthCredentials(final String username, List securityRoles, String... backendRoles) { + this(username, null, null, backendRoles); + this.securityRoles.addAll(securityRoles); + } + private AuthCredentials(final String username, byte[] password, Object nativeCredentials, String... backendRoles) { super(); @@ -195,6 +209,14 @@ public String toString() { + "]"; } + /** + * + * @return Defensive copy of the security roles this user is member of. + */ + public Set getSecurityRoles() { + return Set.copyOf(securityRoles); + } + /** * * @return Defensive copy of the roles this user is member of.