From 70799dacb50a2e992fe69acb16b2138f9a753a06 Mon Sep 17 00:00:00 2001 From: Nihal Harish Date: Wed, 17 Apr 2019 09:48:19 -0700 Subject: [PATCH 1/3] updated 6.6 --- .gitignore | 2 + pom.xml | 7 +- .../auth/http/jwt/HTTPJwtAuthenticator.java | 4 +- .../kerberos/HTTPSpnegoAuthenticator.java | 14 +- .../auth/http/kerberos/util/JaasKrbUtil.java | 10 +- .../auth/http/kerberos/util/KrbConstants.java | 5 +- .../com/amazon/dlic/auth/ldap/LdapUser.java | 3 +- .../backend/LDAPAuthenticationBackend.java | 29 +- .../backend/LDAPAuthorizationBackend.java | 113 +++-- .../dlic/auth/ldap/util/ConfigConstants.java | 8 +- .../dlic/auth/ldap/util/LdapHelper.java | 33 +- .../com/amazon/dlic/auth/ldap/util/Utils.java | 58 +-- ...d.java => LDAPAuthenticationBackend2.java} | 16 +- ...nd.java => LDAPAuthorizationBackend2.java} | 78 +-- .../dlic/auth/ldap2/LDAPUserSearcher.java | 21 +- .../security/auditlog/impl/AuditLogImpl.java | 138 ++++- .../configuration/DlsFlsFilterLeafReader.java | 60 ++- .../dlic/rest/api/AbstractApiAction.java | 459 +++++++++-------- .../dlic/rest/api/ActionGroupsApiAction.java | 18 +- .../rest/api/AuthTokenProcessorAction.java | 8 +- .../dlic/rest/api/FlushCacheApiAction.java | 54 +- .../rest/api/GetConfigurationApiAction.java | 52 +- .../dlic/rest/api/InternalUsersApiAction.java | 81 +-- .../api/OpenDistroSecurityConfigAction.java | 24 +- .../rest/api/PatchableResourceApiAction.java | 137 +++-- .../dlic/rest/api/PermissionsInfoAction.java | 2 +- .../rest/api/RestApiPrivilegesEvaluator.java | 2 +- .../AbstractConfigurationValidator.java | 475 +++++++++--------- .../dlic/auth/ldap/LdapBackendTest.java | 149 +++++- .../com/amazon/dlic/auth/ldap/UtilsTest.java | 15 - .../amazon/dlic/auth/ldap/srv/LdapServer.java | 6 +- .../ldap2/LdapBackendTestClientCert2.java | 17 +- .../ldap2/LdapBackendTestNewStyleConfig2.java | 195 +++++-- .../ldap2/LdapBackendTestOldStyleConfig2.java | 192 +++++-- .../compliance/ComplianceAuditlogTest.java | 4 - .../RestApiComplianceAuditlogTest.java | 5 +- .../security/cache/CachingTest.java | 111 ++++ .../cache/DummyAuthenticationBackend.java | 66 +++ .../security/cache/DummyAuthorizer.java | 55 ++ .../cache/DummyHTTPAuthenticator.java | 59 +++ .../security/dlic/dlsfls/DateMathTest.java | 165 ++++++ .../dlsfls/DlsFlsCrossClusterSearchTest.java | 150 +++++- .../security/dlic/dlsfls/DlsTest.java | 1 + .../dlic/dlsfls/IndexPatternTest.java | 20 + .../security/dlic/rest/api/RolesApiTest.java | 8 + .../security/dlic/rest/api/UserApiTest.java | 7 +- src/test/resources/cache/action_groups.yml | 92 ++++ src/test/resources/cache/config.yml | 20 + src/test/resources/cache/internal_users.yml | 84 ++++ src/test/resources/cache/kirk-keystore.jks | Bin 0 -> 4525 bytes src/test/resources/cache/node-0-keystore.jks | Bin 0 -> 4593 bytes src/test/resources/cache/roles.yml | 331 ++++++++++++ src/test/resources/cache/roles_mapping.yml | 92 ++++ src/test/resources/cache/spock-keystore.jks | Bin 0 -> 4528 bytes src/test/resources/cache/truststore.jks | Bin 0 -> 1096 bytes src/test/resources/dlsfls/roles_983.yml | 1 + src/test/resources/dlsfls/roles_ccs2.yml | 24 + src/test/resources/ldap/base.ldif | 52 +- src/test/resources/ldap/config_ldap2.yml | 2 +- 59 files changed, 2877 insertions(+), 957 deletions(-) rename src/main/java/com/amazon/dlic/auth/ldap2/{LDAPAuthenticationBackend.java => LDAPAuthenticationBackend2.java} (95%) rename src/main/java/com/amazon/dlic/auth/ldap2/{LDAPAuthorizationBackend.java => LDAPAuthorizationBackend2.java} (88%) create mode 100644 src/test/java/com/amazon/opendistroforelasticsearch/security/cache/CachingTest.java create mode 100644 src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthenticationBackend.java create mode 100644 src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthorizer.java create mode 100644 src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyHTTPAuthenticator.java create mode 100644 src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DateMathTest.java create mode 100644 src/test/resources/cache/action_groups.yml create mode 100644 src/test/resources/cache/config.yml create mode 100644 src/test/resources/cache/internal_users.yml create mode 100644 src/test/resources/cache/kirk-keystore.jks create mode 100644 src/test/resources/cache/node-0-keystore.jks create mode 100644 src/test/resources/cache/roles.yml create mode 100644 src/test/resources/cache/roles_mapping.yml create mode 100644 src/test/resources/cache/spock-keystore.jks create mode 100644 src/test/resources/cache/truststore.jks create mode 100644 src/test/resources/dlsfls/roles_ccs2.yml diff --git a/.gitignore b/.gitignore index aa7ca99..6fd04f7 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ data/ puppet/.vagrant test.sh .vagrant/ +.idea/ +*.iml diff --git a/pom.xml b/pom.xml index 0028361..e5c008b 100755 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ opendistro_security_advanced_modules - 0.8.0.0 + 0.8.0.1 jar Open Distro Security Advanced Modules for Elasticsearch @@ -33,7 +33,7 @@ 2016 - 0.8.0.0 + 0.8.0.1 6.6.2 @@ -367,8 +367,9 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M2 + 3.0.0-M3 + -Xmx3072m 3 3 true 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 b34b010..717e933 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 @@ -152,7 +152,9 @@ private AuthCredentials extractCredentials0(final RestRequest request) { if((index = jwtToken.toLowerCase().indexOf(BEARER)) > -1) { //detect Bearer jwtToken = jwtToken.substring(index+BEARER.length()); } else { - log.warn("No Bearer scheme found in header"); + if(log.isDebugEnabled()) { + log.debug("No Bearer scheme found in header"); + } } try { diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java index 1b57711..d13ffb5 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/HTTPSpnegoAuthenticator.java @@ -25,6 +25,10 @@ import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.Base64; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import javax.security.auth.Subject; import javax.security.auth.login.LoginException; @@ -48,6 +52,7 @@ import org.ietf.jgss.GSSException; import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; import com.amazon.dlic.auth.http.kerberos.util.JaasKrbUtil; import com.amazon.dlic.auth.http.kerberos.util.KrbConstants; @@ -58,11 +63,12 @@ public class HTTPSpnegoAuthenticator implements HTTPAuthenticator { private static final String EMPTY_STRING = ""; + private static final Oid[] KRB_OIDS = new Oid[] {KrbConstants.SPNEGO, KrbConstants.KRB5MECH}; protected final Logger log = LogManager.getLogger(this.getClass()); private boolean stripRealmFromPrincipalName; - private String acceptorPrincipal; + private Set acceptorPrincipal; private Path acceptorKeyTabPath; public HTTPSpnegoAuthenticator(final Settings settings, final Path configPath) { @@ -123,10 +129,10 @@ public Void run() { } stripRealmFromPrincipalName = settings.getAsBoolean("strip_realm_from_principal", true); - acceptorPrincipal = settings.get("opendistro_security.kerberos.acceptor_principal"); + acceptorPrincipal = new HashSet<>(settings.getAsList("opendistro_security.kerberos.acceptor_principal", Collections.emptyList())); final String _acceptorKeyTabPath = settings.get("opendistro_security.kerberos.acceptor_keytab_filepath"); - if(acceptorPrincipal == null || acceptorPrincipal.length() == 0) { + if(acceptorPrincipal == null || acceptorPrincipal.size() == 0) { log.error("acceptor_principal must not be null or empty. Kerberos authentication will not work"); acceptorPrincipal = null; } @@ -207,7 +213,7 @@ private AuthCredentials extractCredentials0(final RestRequest request) { final PrivilegedExceptionAction action = new PrivilegedExceptionAction() { @Override public GSSCredential run() throws GSSException { - return manager.createCredential(null, credentialLifetime, KrbConstants.SPNEGO, GSSCredential.ACCEPT_ONLY); + return manager.createCredential(null, credentialLifetime, KRB_OIDS, GSSCredential.ACCEPT_ONLY); } }; gssContext = manager.createContext(Subject.doAs(subject, action)); diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java index fe81c1f..0c97f89 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/JaasKrbUtil.java @@ -78,13 +78,17 @@ public static Subject loginUsingTicketCache(final String principal, final Path c return loginContext.getSubject(); } - public static Subject loginUsingKeytab(final String principal, final Path keytabPath, final boolean initiator) throws LoginException { + public static Subject loginUsingKeytab(final Set principalAsStrings, final Path keytabPath, final boolean initiator) throws LoginException { final Set principals = new HashSet(); - principals.add(new KerberosPrincipal(principal)); + + for(String p: principalAsStrings) { + principals.add(new KerberosPrincipal(p)); + } + final Subject subject = new Subject(false, principals, new HashSet(), new HashSet()); - final Configuration conf = useKeytab(principal, keytabPath, initiator); + final Configuration conf = useKeytab("*", keytabPath, initiator); final String confName = "KeytabConf"; final LoginContext loginContext = new LoginContext(confName, subject, null, conf); loginContext.login(); diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java index 3aca750..13808ae 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java @@ -22,15 +22,18 @@ public final class KrbConstants { static { Oid spnegoTmp = null; + Oid krbTmp = null; try { - spnegoTmp = new Oid("1.3.6.1.5.5.2"); + krbTmp = new Oid("1.2.840.113554.1.2.2"); } catch (final GSSException e) { } SPNEGO = spnegoTmp; + KRB5MECH = krbTmp; } public static final Oid SPNEGO; + public static final Oid KRB5MECH; public static final String KRB5_CONF_PROP = "java.security.krb5.conf"; public static final String JAAS_LOGIN_CONF_PROP = "java.security.auth.login.config"; public static final String USE_SUBJECT_CREDS_ONLY_PROP = "javax.security.auth.useSubjectCredsOnly"; diff --git a/src/main/java/com/amazon/dlic/auth/ldap/LdapUser.java b/src/main/java/com/amazon/dlic/auth/ldap/LdapUser.java index 0cd2efd..7f238cf 100755 --- a/src/main/java/com/amazon/dlic/auth/ldap/LdapUser.java +++ b/src/main/java/com/amazon/dlic/auth/ldap/LdapUser.java @@ -21,6 +21,7 @@ import org.ldaptive.LdapAttribute; import org.ldaptive.LdapEntry; +import com.amazon.dlic.auth.ldap.util.Utils; import com.amazon.opendistroforelasticsearch.security.support.WildcardMatcher; import com.amazon.opendistroforelasticsearch.security.user.AuthCredentials; import com.amazon.opendistroforelasticsearch.security.user.User; @@ -43,7 +44,7 @@ public LdapUser(final String name, String originalUsername, final LdapEntry user if (customAttrMaxValueLen > 0) { for (LdapAttribute attr : userEntry.getAttributes()) { if (attr != null && !attr.isBinary() && !attr.getName().toLowerCase().contains("password")) { - final String val = attr.getStringValue(); + final String val = Utils.getSingleStringValue(attr); // only consider attributes which are not binary and where its value is not // longer than customAttrMaxValueLen characters if (val != null && val.length() > 0 && val.length() <= customAttrMaxValueLen) { diff --git a/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthenticationBackend.java b/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthenticationBackend.java index b7a57f9..d059e43 100755 --- a/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthenticationBackend.java +++ b/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthenticationBackend.java @@ -41,6 +41,7 @@ import org.ldaptive.LdapEntry; import org.ldaptive.LdapException; import org.ldaptive.Response; +import org.ldaptive.SearchFilter; import org.ldaptive.SearchScope; import com.amazon.dlic.auth.ldap.LdapUser; @@ -53,14 +54,10 @@ public class LDAPAuthenticationBackend implements AuthenticationBackend { - static final String ZERO_PLACEHOLDER = "{0}"; + static final int ZERO_PLACEHOLDER = 0; static final String DEFAULT_USERBASE = ""; static final String DEFAULT_USERSEARCH_PATTERN = "(sAMAccountName={0})"; - static { - Utils.init(); - } - protected static final Logger log = LogManager.getLogger(LDAPAuthenticationBackend.class); private final Settings settings; @@ -77,7 +74,7 @@ public LDAPAuthenticationBackend(final Settings settings, final Path configPath) public User authenticate(final AuthCredentials credentials) throws ElasticsearchSecurityException { Connection ldapConnection = null; - final String user = Utils.escapeStringRfc2254(credentials.getUsername()); + final String user =credentials.getUsername(); byte[] password = credentials.getPassword(); try { @@ -129,7 +126,7 @@ public Response run() throws LdapException { String username = dn; if (usernameAttribute != null && entry.getAttribute(usernameAttribute) != null) { - username = entry.getAttribute(usernameAttribute).getStringValue(); + username = Utils.getSingleStringValue(entry.getAttribute(usernameAttribute)); } if (log.isDebugEnabled()) { @@ -225,15 +222,18 @@ static LdapEntry exists(final String user, Connection ldapConnection, Settings s private static LdapEntry existsSearchingUntilFirstHit(final String user, Connection ldapConnection, List> userBaseSettings) throws Exception { - final String username = Utils.escapeStringRfc2254(user); + final String username = user; for (Map.Entry entry : userBaseSettings) { Settings baseSettings = entry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN)); + f.setParameter(ZERO_PLACEHOLDER, username); + List result = LdapHelper.search(ldapConnection, baseSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_USERBASE), - baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN) - .replace(ZERO_PLACEHOLDER, username), + f, SearchScope.SUBTREE); if (log.isDebugEnabled()) { @@ -250,16 +250,19 @@ private static LdapEntry existsSearchingUntilFirstHit(final String user, Connect private static LdapEntry existsSearchingAllBases(final String user, Connection ldapConnection, List> userBaseSettings) throws Exception { - final String username = Utils.escapeStringRfc2254(user); + final String username = user; Set result = new HashSet<>(); for (Map.Entry entry : userBaseSettings) { Settings baseSettings = entry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN)); + f.setParameter(ZERO_PLACEHOLDER, username); + List foundEntries = LdapHelper.search(ldapConnection, baseSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_USERBASE), - baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN) - .replace(ZERO_PLACEHOLDER, username), + f, SearchScope.SUBTREE); if (log.isDebugEnabled()) { diff --git a/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthorizationBackend.java b/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthorizationBackend.java index b3128be..d9a3ac4 100755 --- a/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthorizationBackend.java +++ b/src/main/java/com/amazon/dlic/auth/ldap/backend/LDAPAuthorizationBackend.java @@ -17,6 +17,7 @@ import java.io.FileNotFoundException; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.security.AccessController; import java.security.KeyStore; @@ -28,7 +29,6 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.time.Duration; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -38,10 +38,10 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.StringTokenizer; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; -import javax.naming.ldap.Rdn; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -58,6 +58,7 @@ import org.ldaptive.LdapEntry; import org.ldaptive.LdapException; import org.ldaptive.Response; +import org.ldaptive.SearchFilter; import org.ldaptive.SearchScope; import org.ldaptive.control.RequestControl; import org.ldaptive.provider.ProviderConnection; @@ -87,17 +88,13 @@ public class LDAPAuthorizationBackend implements AuthorizationBackend { private static final String COM_SUN_JNDI_LDAP_OBJECT_DISABLE_ENDPOINT_IDENTIFICATION = "com.sun.jndi.ldap.object.disableEndpointIdentification"; private static final List DEFAULT_TLS_PROTOCOLS = Arrays.asList("TLSv1.2", "TLSv1.1"); - static final String ONE_PLACEHOLDER = "{1}"; - static final String TWO_PLACEHOLDER = "{2}"; + static final int ONE_PLACEHOLDER = 1; + static final int TWO_PLACEHOLDER = 2; static final String DEFAULT_ROLEBASE = ""; static final String DEFAULT_ROLESEARCH = "(member={0})"; static final String DEFAULT_ROLENAME = "name"; static final String DEFAULT_USERROLENAME = "memberOf"; - static { - Utils.init(); - } - protected static final Logger log = LogManager.getLogger(LDAPAuthorizationBackend.class); private final Settings settings; private final Path configPath; @@ -247,6 +244,9 @@ private static Connection getConnection0(final Settings settings, final Path con if (connection != null && connection.isOpen()) { break; + } else { + Utils.unbindAndCloseSilently(connection); + connection = null; } } catch (final Exception e) { lastException = e; @@ -522,15 +522,24 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) LdapEntry entry = null; String dn = null; + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (2): username="+user.getName()+" -> "+Arrays.toString(user.getName().getBytes(StandardCharsets.UTF_8))); + } + if (user instanceof LdapUser) { entry = ((LdapUser) user).getUserEntry(); authenticatedUser = entry.getDn(); originalUserName = ((LdapUser) user).getOriginalUsername(); } else { - authenticatedUser = Utils.escapeStringRfc2254(user.getName()); + authenticatedUser = user.getName(); originalUserName = user.getName(); } + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (3): authenticatedUser="+authenticatedUser+" -> "+Arrays.toString(authenticatedUser.getBytes(StandardCharsets.UTF_8))); + } + + final boolean rolesearchEnabled = settings.getAsBoolean(ConfigConstants.LDAP_AUTHZ_ROLESEARCH_ENABLED, true); if (log.isDebugEnabled()) { @@ -569,6 +578,10 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) log.trace("{} is a valid DN", authenticatedUser); } + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (4): authenticatedUser="+authenticatedUser+" -> "+Arrays.toString(authenticatedUser.getBytes(StandardCharsets.UTF_8))); + } + entry = LdapHelper.lookup(connection, authenticatedUser); if (entry == null) { @@ -576,6 +589,10 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) } } else { + + if(log.isDebugEnabled()) + log.debug("DBGTRACE (5): authenticatedUser="+user.getName()+" -> "+Arrays.toString(user.getName().getBytes(StandardCharsets.UTF_8))); + entry = LDAPAuthenticationBackend.exists(user.getName(), connection, settings, userBaseSettings); if (log.isTraceEnabled()) { @@ -592,6 +609,11 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) if (log.isTraceEnabled()) { log.trace("User found with DN {}", dn); } + + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (6): dn"+dn+" -> "+Arrays.toString(dn.getBytes(StandardCharsets.UTF_8))); + } + } final Set ldapRoles = new HashSet<>(150); @@ -612,6 +634,11 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) if (entry.getAttribute(roleName) != null) { final Collection userRoles = entry.getAttribute(roleName).getStringValues(); for (final String possibleRoleDN : userRoles) { + + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (7): possibleRoleDN"+possibleRoleDN); + } + if (isValidDn(possibleRoleDN)) { LdapName ldapName = new LdapName(possibleRoleDN); ldapRoles.add(ldapName); @@ -656,21 +683,29 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) final LdapAttribute userRoleAttribute = entry.getAttribute(userRoleAttributeName); if (userRoleAttribute != null) { - userRoleAttributeValue = userRoleAttribute.getStringValue(); + userRoleAttributeValue = Utils.getSingleStringValue(userRoleAttribute); } if (rolesearchEnabled) { - String escapedDn = Utils.escapeStringRfc2254(dn); + String escapedDn = dn; + + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (8): escapedDn"+escapedDn); + } for (Map.Entry roleSearchSettingsEntry : roleBaseSettings) { Settings roleSearchSettings = roleSearchSettingsEntry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH)); + f.setParameter(LDAPAuthenticationBackend.ZERO_PLACEHOLDER, escapedDn); + f.setParameter(ONE_PLACEHOLDER, originalUserName); + f.setParameter(TWO_PLACEHOLDER, + userRoleAttributeValue == null ? TWO_PLACEHOLDER : userRoleAttributeValue); + List rolesResult = LdapHelper.search(connection, roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_ROLEBASE), - roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH) - .replace(LDAPAuthenticationBackend.ZERO_PLACEHOLDER, escapedDn) - .replace(ONE_PLACEHOLDER, originalUserName).replace(TWO_PLACEHOLDER, - userRoleAttributeValue == null ? TWO_PLACEHOLDER : userRoleAttributeValue), + f, SearchScope.SUBTREE); if (log.isTraceEnabled()) { @@ -726,7 +761,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) } for (final LdapName roleLdapName : nestedReturn) { - final String role = getRoleFromAttribute(roleLdapName, roleName); + final String role = getRoleFromEntry(connection, roleLdapName, roleName); if (!Strings.isNullOrEmpty(role)) { user.addRole(role); @@ -738,7 +773,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) } else { // DN roles, extract rolename according to config for (final LdapName roleLdapName : ldapRoles) { - final String role = getRoleFromAttribute(roleLdapName, roleName); + final String role = getRoleFromEntry(connection, roleLdapName, roleName); if (!Strings.isNullOrEmpty(role)) { user.addRole(role); @@ -798,6 +833,11 @@ protected Set resolveNestedRoles(final LdapName roleDn, final Connecti final Collection userRoles = e0.getAttribute(userRoleName).getStringValues(); for (final String possibleRoleDN : userRoles) { + + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (10): possibleRoleDN"+possibleRoleDN); + } + if (isValidDn(possibleRoleDN)) { try { LdapName ldapName = new LdapName(possibleRoleDN); @@ -819,17 +859,25 @@ protected Set resolveNestedRoles(final LdapName roleDn, final Connecti } if (rolesearchEnabled) { - String escapedDn = Utils.escapeStringRfc2254(roleDn.toString()); + String escapedDn = roleDn.toString(); + + if(log.isDebugEnabled()){ + log.debug("DBGTRACE (10): escapedDn"+escapedDn); + } + for (Map.Entry roleSearchBaseSettingsEntry : Utils .getOrderedBaseSettings(roleSearchBaseSettingsSet)) { Settings roleSearchSettings = roleSearchBaseSettingsEntry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH)); + f.setParameter(LDAPAuthenticationBackend.ZERO_PLACEHOLDER, escapedDn); + f.setParameter(ONE_PLACEHOLDER, escapedDn); + List foundEntries = LdapHelper.search(ldapConnection, roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_ROLEBASE), - roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH) - .replace(LDAPAuthenticationBackend.ZERO_PLACEHOLDER, escapedDn) - .replace(ONE_PLACEHOLDER, escapedDn), + f, SearchScope.SUBTREE); if (log.isTraceEnabled()) { @@ -898,30 +946,27 @@ private boolean isValidDn(final String dn) { return true; } - private String getRoleFromAttribute(final LdapName ldapName, final String role) { + private String getRoleFromEntry(final Connection ldapConnection, final LdapName ldapName, final String role) { if (ldapName == null || Strings.isNullOrEmpty(role)) { return null; } - if ("dn".equalsIgnoreCase(role)) { + if("dn".equalsIgnoreCase(role)) { return ldapName.toString(); } - List rdns = new ArrayList<>(ldapName.getRdns().size()); - rdns.addAll(ldapName.getRdns()); - - Collections.reverse(rdns); - - for (Rdn rdn : rdns) { - if (role.equalsIgnoreCase(rdn.getType())) { + try { + final LdapEntry roleEntry = LdapHelper.lookup(ldapConnection, ldapName.toString()); - if (rdn.getValue() == null) { - return null; + if(roleEntry != null) { + final LdapAttribute roleAttribute = roleEntry.getAttribute(role); + if(roleAttribute != null) { + return Utils.getSingleStringValue(roleAttribute); } - - return String.valueOf(rdn.getValue()); } + } catch (LdapException e) { + log.error("Unable to handle role {} because of ",ldapName, e.toString(), e); } return null; diff --git a/src/main/java/com/amazon/dlic/auth/ldap/util/ConfigConstants.java b/src/main/java/com/amazon/dlic/auth/ldap/util/ConfigConstants.java index 3567422..738eebb 100755 --- a/src/main/java/com/amazon/dlic/auth/ldap/util/ConfigConstants.java +++ b/src/main/java/com/amazon/dlic/auth/ldap/util/ConfigConstants.java @@ -18,7 +18,7 @@ public final class ConfigConstants { public static final String LDAP_AUTHC_USERBASE = "userbase"; - public static final String LDAP_AUTHC_USERNAME_ATTRIBUTE = "username_attribute"; + public static final String LDAP_AUTHC_USERNAME_ATTRIBUTE = "username_attribute";//multi-value public static final String LDAP_AUTHC_USERSEARCH = "usersearch"; public static final String LDAP_AUTHCZ_USERS = "users"; @@ -29,10 +29,10 @@ public final class ConfigConstants { public static final String LDAP_AUTHZ_RESOLVE_NESTED_ROLES = "resolve_nested_roles"; public static final String LDAP_AUTHZ_ROLEBASE = "rolebase"; - public static final String LDAP_AUTHZ_ROLENAME = "rolename"; + public static final String LDAP_AUTHZ_ROLENAME = "rolename";//multi-value public static final String LDAP_AUTHZ_ROLESEARCH = "rolesearch"; - public static final String LDAP_AUTHZ_USERROLEATTRIBUTE = "userroleattribute"; - public static final String LDAP_AUTHZ_USERROLENAME = "userrolename"; + public static final String LDAP_AUTHZ_USERROLEATTRIBUTE = "userroleattribute";//multi-value + public static final String LDAP_AUTHZ_USERROLENAME = "userrolename";//multi-value public static final String LDAP_AUTHZ_SKIP_USERS = "skip_users"; public static final String LDAP_AUTHZ_ROLESEARCH_ENABLED = "rolesearch_enabled"; public static final String LDAP_AUTHZ_NESTEDROLEFILTER = "nested_role_filter"; diff --git a/src/main/java/com/amazon/dlic/auth/ldap/util/LdapHelper.java b/src/main/java/com/amazon/dlic/auth/ldap/util/LdapHelper.java index dfb3675..43769fa 100644 --- a/src/main/java/com/amazon/dlic/auth/ldap/util/LdapHelper.java +++ b/src/main/java/com/amazon/dlic/auth/ldap/util/LdapHelper.java @@ -21,6 +21,10 @@ import java.util.ArrayList; import java.util.List; +import javax.naming.InvalidNameException; +import javax.naming.ldap.LdapName; +import javax.naming.ldap.Rdn; + import org.elasticsearch.SpecialPermission; import org.ldaptive.Connection; import org.ldaptive.DerefAliases; @@ -28,6 +32,7 @@ import org.ldaptive.LdapException; import org.ldaptive.Response; import org.ldaptive.ReturnAttributes; +import org.ldaptive.SearchFilter; import org.ldaptive.SearchOperation; import org.ldaptive.SearchRequest; import org.ldaptive.SearchResult; @@ -36,7 +41,8 @@ public class LdapHelper { - public static List search(final Connection conn, final String baseDn, final String filter, + private static SearchFilter ALL = new SearchFilter("(objectClass=*)"); + public static List search(final Connection conn, final String unescapedDn, SearchFilter filter, final SearchScope searchScope) throws LdapException { final SecurityManager sm = System.getSecurityManager(); @@ -46,6 +52,7 @@ public static List search(final Connection conn, final String baseDn, } try { + final String baseDn = escapeDn(unescapedDn); return AccessController.doPrivileged(new PrivilegedExceptionAction>() { @Override public List run() throws Exception { @@ -71,12 +78,14 @@ public List run() throws Exception { } else { throw new RuntimeException(e); } + }catch (InvalidNameException e) { + throw new RuntimeException(e); } } - public static LdapEntry lookup(final Connection conn, final String dn) throws LdapException { + public static LdapEntry lookup(final Connection conn, final String unescapedDn) throws LdapException { - final List entries = search(conn, dn, "(objectClass=*)", SearchScope.OBJECT); + final List entries = search(conn, unescapedDn, ALL, SearchScope.OBJECT); if (entries.size() == 1) { return entries.get(0); @@ -85,4 +94,22 @@ public static LdapEntry lookup(final Connection conn, final String dn) throws Ld } } + private static String escapeDn(String dn) throws InvalidNameException { + final LdapName dnName = new LdapName(dn); + final List escaped = new ArrayList<>(dnName.size()); + for(Rdn rdn: dnName.getRdns()) { + escaped.add(new Rdn(rdn.getType(), escapeForwardSlash(rdn.getValue()))); + } + return new LdapName(escaped).toString(); + } + + private static Object escapeForwardSlash(Object input) { + if(input != null && input instanceof String) { + return ((String)input).replace("/", "\\2f"); + } else { + return input; + } + + } + } diff --git a/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java b/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java index ae5a92a..9d4bd8a 100644 --- a/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java +++ b/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java @@ -24,25 +24,22 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.StringTokenizer; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.elasticsearch.SpecialPermission; import org.elasticsearch.common.settings.Settings; import org.ldaptive.Connection; +import org.ldaptive.LdapAttribute; public final class Utils { - private static final String RFC2254_ESCAPE_CHARS = "\\*()\000"; + private static final Logger log = LogManager.getLogger(Utils.class); private Utils() { } - public static void init() { - // empty init() to allow prior initialization - } - public static void unbindAndCloseSilently(final Connection connection) { if (connection == null) { return; @@ -67,42 +64,6 @@ public Object run() throws Exception { } } - /** - * RFC 2254 string escaping - */ - public static String escapeStringRfc2254(final String str) { - - if (str == null || str.length() == 0) { - return str; - } - - final StringTokenizer tok = new StringTokenizer(str, RFC2254_ESCAPE_CHARS, true); - - if (tok.countTokens() == 0) { - return str; - } - - final StringBuilder out = new StringBuilder(); - while (tok.hasMoreTokens()) { - final String s = tok.nextToken(); - - if (s.equals("*")) { - out.append("\\2a"); - } else if (s.equals("(")) { - out.append("\\28"); - } else if (s.equals(")")) { - out.append("\\29"); - } else if (s.equals("\\")) { - out.append("\\5c"); - } else if (s.equals("\000")) { - out.append("\\00"); - } else { - out.append(s); - } - } - return out.toString(); - } - public static List> getOrderedBaseSettings(Settings settings) { return getOrderedBaseSettings(settings.getAsGroups(true)); } @@ -136,4 +97,17 @@ public int compare(Map.Entry o1, Map.Entry o }); } + public static String getSingleStringValue(LdapAttribute attribute) { + if(attribute == null) { + return null; + } + + if(attribute.size() > 1) { + if(log.isDebugEnabled()) { + log.debug("Multiple values found for {} ({})", attribute.getName(), attribute); + } + } + + return attribute.getStringValue(); + } } diff --git a/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthenticationBackend.java b/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthenticationBackend2.java similarity index 95% rename from src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthenticationBackend.java rename to src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthenticationBackend2.java index 7e9d152..4155f49 100755 --- a/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthenticationBackend.java +++ b/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthenticationBackend2.java @@ -47,13 +47,9 @@ import com.amazon.opendistroforelasticsearch.security.user.AuthCredentials; import com.amazon.opendistroforelasticsearch.security.user.User; -public class LDAPAuthenticationBackend implements AuthenticationBackend, Destroyable { +public class LDAPAuthenticationBackend2 implements AuthenticationBackend, Destroyable { - static { - Utils.init(); - } - - protected static final Logger log = LogManager.getLogger(LDAPAuthenticationBackend.class); + protected static final Logger log = LogManager.getLogger(LDAPAuthenticationBackend2.class); private final Settings settings; @@ -62,7 +58,7 @@ public class LDAPAuthenticationBackend implements AuthenticationBackend, Destroy private ConnectionFactory authConnectionFactory; private LDAPUserSearcher userSearcher; - public LDAPAuthenticationBackend(final Settings settings, final Path configPath) throws SSLConfigException { + public LDAPAuthenticationBackend2(final Settings settings, final Path configPath) throws SSLConfigException { this.settings = settings; LDAPConnectionFactoryFactory ldapConnectionFactoryFactory = new LDAPConnectionFactoryFactory(settings, @@ -84,7 +80,7 @@ public LDAPAuthenticationBackend(final Settings settings, final Path configPath) public User authenticate(final AuthCredentials credentials) throws ElasticsearchSecurityException { Connection ldapConnection = null; - final String user = Utils.escapeStringRfc2254(credentials.getUsername()); + final String user = credentials.getUsername(); byte[] password = credentials.getPassword(); try { @@ -123,7 +119,7 @@ public User authenticate(final AuthCredentials credentials) throws Elasticsearch String username = dn; if (usernameAttribute != null && entry.getAttribute(usernameAttribute) != null) { - username = entry.getAttribute(usernameAttribute).getStringValue(); + username = Utils.getSingleStringValue(entry.getAttribute(usernameAttribute)); } if (log.isDebugEnabled()) { @@ -224,4 +220,4 @@ public void destroy() { } -} +} \ No newline at end of file diff --git a/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthorizationBackend.java b/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthorizationBackend2.java similarity index 88% rename from src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthorizationBackend.java rename to src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthorizationBackend2.java index 3ffc494..b64c65c 100755 --- a/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthorizationBackend.java +++ b/src/main/java/com/amazon/dlic/auth/ldap2/LDAPAuthorizationBackend2.java @@ -40,6 +40,7 @@ import org.ldaptive.LdapAttribute; import org.ldaptive.LdapEntry; import org.ldaptive.LdapException; +import org.ldaptive.SearchFilter; import org.ldaptive.SearchScope; import org.ldaptive.pool.ConnectionPool; @@ -55,28 +56,24 @@ import com.amazon.opendistroforelasticsearch.security.user.User; import com.google.common.collect.HashMultimap; -public class LDAPAuthorizationBackend implements AuthorizationBackend, Destroyable { +public class LDAPAuthorizationBackend2 implements AuthorizationBackend, Destroyable { - static final String ZERO_PLACEHOLDER = "{0}"; - static final String ONE_PLACEHOLDER = "{1}"; - static final String TWO_PLACEHOLDER = "{2}"; + static final int ZERO_PLACEHOLDER = 0; + static final int ONE_PLACEHOLDER = 1; + static final int TWO_PLACEHOLDER = 2; static final String DEFAULT_ROLEBASE = ""; static final String DEFAULT_ROLESEARCH = "(member={0})"; static final String DEFAULT_ROLENAME = "name"; static final String DEFAULT_USERROLENAME = "memberOf"; - static { - Utils.init(); - } - - protected static final Logger log = LogManager.getLogger(LDAPAuthorizationBackend.class); + protected static final Logger log = LogManager.getLogger(LDAPAuthorizationBackend2.class); private final Settings settings; private final List> roleBaseSettings; private ConnectionPool connectionPool; private ConnectionFactory connectionFactory; private LDAPUserSearcher userSearcher; - public LDAPAuthorizationBackend(final Settings settings, final Path configPath) throws SSLConfigException { + public LDAPAuthorizationBackend2(final Settings settings, final Path configPath) throws SSLConfigException { this.settings = settings; this.roleBaseSettings = getRoleSearchSettings(settings); @@ -134,7 +131,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) authenticatedUser = entry.getDn(); originalUserName = ((LdapUser) user).getOriginalUsername(); } else { - authenticatedUser = Utils.escapeStringRfc2254(user.getName()); + authenticatedUser =user.getName(); originalUserName = user.getName(); } @@ -260,21 +257,25 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) final LdapAttribute userRoleAttribute = entry.getAttribute(userRoleAttributeName); if (userRoleAttribute != null) { - userRoleAttributeValue = userRoleAttribute.getStringValue(); + userRoleAttributeValue = Utils.getSingleStringValue(userRoleAttribute); } if (rolesearchEnabled) { - String escapedDn = Utils.escapeStringRfc2254(dn); + String escapedDn = dn; for (Map.Entry roleSearchSettingsEntry : roleBaseSettings) { Settings roleSearchSettings = roleSearchSettingsEntry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH)); + f.setParameter(ZERO_PLACEHOLDER, escapedDn); + f.setParameter(ONE_PLACEHOLDER, originalUserName); + f.setParameter(TWO_PLACEHOLDER, + userRoleAttributeValue == null ? TWO_PLACEHOLDER : userRoleAttributeValue); + List rolesResult = LdapHelper.search(connection, roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_ROLEBASE), - roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH) - .replace(ZERO_PLACEHOLDER, escapedDn).replace(ONE_PLACEHOLDER, originalUserName) - .replace(TWO_PLACEHOLDER, - userRoleAttributeValue == null ? TWO_PLACEHOLDER : userRoleAttributeValue), + f, SearchScope.SUBTREE); if (log.isTraceEnabled()) { @@ -330,7 +331,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) } for (final LdapName roleLdapName : nestedReturn) { - final String role = getRoleFromAttribute(roleLdapName, roleName); + final String role = getRoleFromEntry(connection, roleLdapName, roleName); if (!Strings.isNullOrEmpty(role)) { user.addRole(role); @@ -342,7 +343,7 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) } else { // DN roles, extract rolename according to config for (final LdapName roleLdapName : ldapRoles) { - final String role = getRoleFromAttribute(roleLdapName, roleName); + final String role = getRoleFromEntry(connection, roleLdapName, roleName); if (!Strings.isNullOrEmpty(role)) { user.addRole(role); @@ -376,8 +377,8 @@ public void fillRoles(final User user, final AuthCredentials optionalAuthCreds) } protected Set resolveNestedRoles(final LdapName roleDn, final Connection ldapConnection, - String userRoleName, int depth, final boolean rolesearchEnabled, - Set> roleSearchBaseSettingsSet, final List roleFilter) + String userRoleName, int depth, final boolean rolesearchEnabled, + Set> roleSearchBaseSettingsSet, final List roleFilter) throws ElasticsearchSecurityException, LdapException { if (!roleFilter.isEmpty() && WildcardMatcher.matchAny(roleFilter, roleDn.toString())) { @@ -421,16 +422,20 @@ protected Set resolveNestedRoles(final LdapName roleDn, final Connecti } if (rolesearchEnabled) { - String escapedDn = Utils.escapeStringRfc2254(roleDn.toString()); + String escapedDn = roleDn.toString(); for (Map.Entry roleSearchBaseSettingsEntry : Utils .getOrderedBaseSettings(roleSearchBaseSettingsSet)) { Settings roleSearchSettings = roleSearchBaseSettingsEntry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH)); + f.setParameter(ZERO_PLACEHOLDER, escapedDn); + f.setParameter(ONE_PLACEHOLDER, escapedDn); + List foundEntries = LdapHelper.search(ldapConnection, roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_ROLEBASE), - roleSearchSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_ROLESEARCH) - .replace(ZERO_PLACEHOLDER, escapedDn).replace(ONE_PLACEHOLDER, escapedDn), + f, SearchScope.SUBTREE); if (log.isTraceEnabled()) { @@ -499,30 +504,27 @@ private boolean isValidDn(final String dn) { return true; } - private String getRoleFromAttribute(final LdapName ldapName, final String role) { + private String getRoleFromEntry(final Connection ldapConnection, final LdapName ldapName, final String role) { if (ldapName == null || Strings.isNullOrEmpty(role)) { return null; } - if ("dn".equalsIgnoreCase(role)) { + if("dn".equalsIgnoreCase(role)) { return ldapName.toString(); } - List rdns = new ArrayList<>(ldapName.getRdns().size()); - rdns.addAll(ldapName.getRdns()); - - Collections.reverse(rdns); - - for (Rdn rdn : rdns) { - if (role.equalsIgnoreCase(rdn.getType())) { + try { + final LdapEntry roleEntry = LdapHelper.lookup(ldapConnection, ldapName.toString()); - if (rdn.getValue() == null) { - return null; + if(roleEntry != null) { + final LdapAttribute roleAttribute = roleEntry.getAttribute(role); + if(roleAttribute != null) { + return Utils.getSingleStringValue(roleAttribute); } - - return String.valueOf(rdn.getValue()); } + } catch (LdapException e) { + log.error("Unable to handle role {} because of ",ldapName, e.toString(), e); } return null; @@ -536,4 +538,4 @@ public void destroy() { } } -} +} \ No newline at end of file diff --git a/src/main/java/com/amazon/dlic/auth/ldap2/LDAPUserSearcher.java b/src/main/java/com/amazon/dlic/auth/ldap2/LDAPUserSearcher.java index ab7f887..60c6196 100644 --- a/src/main/java/com/amazon/dlic/auth/ldap2/LDAPUserSearcher.java +++ b/src/main/java/com/amazon/dlic/auth/ldap2/LDAPUserSearcher.java @@ -28,6 +28,7 @@ import org.elasticsearch.common.settings.Settings; import org.ldaptive.Connection; import org.ldaptive.LdapEntry; +import org.ldaptive.SearchFilter; import org.ldaptive.SearchScope; import com.amazon.dlic.auth.ldap.util.ConfigConstants; @@ -37,7 +38,7 @@ public class LDAPUserSearcher { protected static final Logger log = LogManager.getLogger(LDAPUserSearcher.class); - private static final String ZERO_PLACEHOLDER = "{0}"; + private static final int ZERO_PLACEHOLDER = 0; private static final String DEFAULT_USERBASE = ""; private static final String DEFAULT_USERSEARCH_PATTERN = "(sAMAccountName={0})"; @@ -85,15 +86,18 @@ LdapEntry exists(Connection ldapConnection, String user) throws Exception { } private LdapEntry existsSearchingUntilFirstHit(Connection ldapConnection, String user) throws Exception { - final String username = Utils.escapeStringRfc2254(user); + final String username = user; for (Map.Entry entry : userBaseSettings) { Settings baseSettings = entry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN)); + f.setParameter(ZERO_PLACEHOLDER, username); + List result = LdapHelper.search(ldapConnection, baseSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_USERBASE), - baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN) - .replace(ZERO_PLACEHOLDER, username), + f, SearchScope.SUBTREE); if (log.isDebugEnabled()) { @@ -109,16 +113,19 @@ private LdapEntry existsSearchingUntilFirstHit(Connection ldapConnection, String } private LdapEntry existsSearchingAllBases(Connection ldapConnection, String user) throws Exception { - final String username = Utils.escapeStringRfc2254(user); + final String username = user; Set result = new HashSet<>(); for (Map.Entry entry : userBaseSettings) { Settings baseSettings = entry.getValue(); + SearchFilter f = new SearchFilter(); + f.setFilter(baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN)); + f.setParameter(ZERO_PLACEHOLDER, username); + List foundEntries = LdapHelper.search(ldapConnection, baseSettings.get(ConfigConstants.LDAP_AUTHCZ_BASE, DEFAULT_USERBASE), - baseSettings.get(ConfigConstants.LDAP_AUTHCZ_SEARCH, DEFAULT_USERSEARCH_PATTERN) - .replace(ZERO_PLACEHOLDER, username), + f, SearchScope.SUBTREE); if (log.isDebugEnabled()) { diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java index e013a0a..8da650e 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java @@ -19,18 +19,27 @@ import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.Map; -import org.apache.logging.log4j.LogManager; import org.elasticsearch.SpecialPermission; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.env.Environment; +import org.elasticsearch.index.engine.Engine.Delete; +import org.elasticsearch.index.engine.Engine.DeleteResult; +import org.elasticsearch.index.engine.Engine.Index; +import org.elasticsearch.index.engine.Engine.IndexResult; +import org.elasticsearch.index.get.GetResult; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.rest.RestRequest; +import org.elasticsearch.tasks.Task; import org.elasticsearch.threadpool.ThreadPool; +import org.elasticsearch.transport.TransportRequest; import com.amazon.opendistroforelasticsearch.security.auditlog.routing.AuditMessageRouter; import com.amazon.opendistroforelasticsearch.security.compliance.ComplianceConfig; -import com.amazon.opendistroforelasticsearch.security.support.ConfigConstants; public final class AuditLogImpl extends AbstractAuditLog { @@ -38,7 +47,7 @@ public final class AuditLogImpl extends AbstractAuditLog { private final boolean enabled; public AuditLogImpl(final Settings settings, final Path configPath, Client clientProvider, ThreadPool threadPool, - final IndexNameExpressionResolver resolver, final ClusterService clusterService) { + final IndexNameExpressionResolver resolver, final ClusterService clusterService) { super(settings, threadPool, resolver, clusterService); this.messageRouter = new AuditMessageRouter(settings, clientProvider, threadPool, configPath); @@ -62,7 +71,7 @@ public Object run() { public void run() { try { close(); - } catch (IOException e) { + } catch (final IOException e) { log.warn("Exception while shutting down message router", e); } } @@ -74,10 +83,10 @@ public void run() { } - @Override - public void setComplianceConfig(ComplianceConfig complianceConfig) { - messageRouter.setComplianceConfig(complianceConfig); - } + @Override + public void setComplianceConfig(ComplianceConfig complianceConfig) { + messageRouter.setComplianceConfig(complianceConfig); + } @Override public void close() throws IOException { @@ -91,4 +100,117 @@ protected void save(final AuditMessage msg) { } } + @Override + public void logFailedLogin(String effectiveUser, boolean sgadmin, String initiatingUser, TransportRequest request, Task task) { + if (enabled) { + super.logFailedLogin(effectiveUser, sgadmin, initiatingUser, request, task); + } + } + + @Override + public void logFailedLogin(String effectiveUser, boolean sgadmin, String initiatingUser, RestRequest request) { + if (enabled) { + super.logFailedLogin(effectiveUser, sgadmin, initiatingUser, request); + } + } + + @Override + public void logSucceededLogin(String effectiveUser, boolean sgadmin, String initiatingUser, TransportRequest request, String action, Task task) { + if (enabled) { + super.logSucceededLogin(effectiveUser, sgadmin, initiatingUser, request, action, task); + } + } + + @Override + public void logSucceededLogin(String effectiveUser, boolean sgadmin, String initiatingUser, RestRequest request) { + if (enabled) { + super.logSucceededLogin(effectiveUser, sgadmin, initiatingUser, request); + } + } + + @Override + public void logMissingPrivileges(String privilege, String effectiveUser, RestRequest request) { + if (enabled) { + super.logMissingPrivileges(privilege, effectiveUser, request); + } + } + + @Override + public void logMissingPrivileges(String privilege, TransportRequest request, Task task) { + if (enabled) { + super.logMissingPrivileges(privilege, request, task); + } + } + + @Override + public void logGrantedPrivileges(String privilege, TransportRequest request, Task task) { + if (enabled) { + super.logGrantedPrivileges(privilege, request, task); + } + } + + @Override + public void logBadHeaders(TransportRequest request, String action, Task task) { + if (enabled) { + super.logBadHeaders(request, action, task); + } + } + + @Override + public void logBadHeaders(RestRequest request) { + if (enabled) { + super.logBadHeaders(request); + } + } + + @Override + public void logSecurityIndexAttempt (TransportRequest request, String action, Task task) { + if (enabled) { + super.logSecurityIndexAttempt(request, action, task); + } + } + + @Override + public void logSSLException(TransportRequest request, Throwable t, String action, Task task) { + if (enabled) { + super.logSSLException(request, t, action, task); + } + } + + @Override + public void logSSLException(RestRequest request, Throwable t) { + if (enabled) { + super.logSSLException(request, t); + } + } + + @Override + public void logDocumentRead(String index, String id, ShardId shardId, Map fieldNameValues, ComplianceConfig complianceConfig) { + if (enabled) { + super.logDocumentRead(index, id, shardId, fieldNameValues, complianceConfig); + } + } + + @Override + public void logDocumentWritten(ShardId shardId, GetResult originalResult, Index currentIndex, IndexResult result, + ComplianceConfig complianceConfig) { + if (enabled) { + super.logDocumentWritten(shardId, originalResult, currentIndex, result, complianceConfig); + } + } + + @Override + public void logDocumentDeleted(ShardId shardId, Delete delete, DeleteResult result) { + if (enabled) { + super.logDocumentDeleted(shardId, delete, result); + } + } + + @Override + public void logExternalConfig(Settings settings, Environment environment) { + if (enabled) { + super.logExternalConfig(settings, environment); + } + } + } diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/configuration/DlsFlsFilterLeafReader.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/configuration/DlsFlsFilterLeafReader.java index 0c21295..092e1cf 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/configuration/DlsFlsFilterLeafReader.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/configuration/DlsFlsFilterLeafReader.java @@ -31,6 +31,7 @@ import java.util.Set; import java.util.function.Function; +import com.amazon.opendistroforelasticsearch.security.dlic.rest.support.Utils; import org.apache.lucene.index.BinaryDocValues; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.FieldInfo; @@ -106,9 +107,9 @@ class DlsFlsFilterLeafReader extends FilterLeafReader { DlsFlsFilterLeafReader(final LeafReader delegate, final Set includesExcludes, - final BitSetProducer bsp, final IndexService indexService, final ThreadContext threadContext, - final ClusterService clusterService, final ComplianceConfig complianceConfig, - final AuditLog auditlog, final Set maskedFields, final ShardId shardId) { + final BitSetProducer bsp, final IndexService indexService, final ThreadContext threadContext, + final ClusterService clusterService, final ComplianceConfig complianceConfig, + final AuditLog auditlog, final Set maskedFields, final ShardId shardId) { super(delegate); maskFields = (complianceConfig.isEnabled() && maskedFields != null && maskedFields.size() > 0); @@ -239,9 +240,9 @@ private static class DlsFlsSubReaderWrapper extends FilterDirectoryReader.SubRea private final ShardId shardId; public DlsFlsSubReaderWrapper(final Set includes, final BitSetProducer bsp, - final IndexService indexService, final ThreadContext threadContext, - final ClusterService clusterService, final ComplianceConfig complianceConfig, - final AuditLog auditlog, final Set maskedFields, ShardId shardId) { + final IndexService indexService, final ThreadContext threadContext, + final ClusterService clusterService, final ComplianceConfig complianceConfig, + final AuditLog auditlog, final Set maskedFields, ShardId shardId) { this.includes = includes; this.bsp = bsp; this.indexService = indexService; @@ -273,9 +274,9 @@ static class DlsFlsDirectoryReader extends FilterDirectoryReader { private final ShardId shardId; public DlsFlsDirectoryReader(final DirectoryReader in, final Set includes, final BitSetProducer bsp, - final IndexService indexService, final ThreadContext threadContext, - final ClusterService clusterService, final ComplianceConfig complianceConfig, - final AuditLog auditlog, final Set maskedFields, ShardId shardId) throws IOException { + final IndexService indexService, final ThreadContext threadContext, + final ClusterService clusterService, final ComplianceConfig complianceConfig, + final AuditLog auditlog, final Set maskedFields, ShardId shardId) throws IOException { super(in, new DlsFlsSubReaderWrapper(includes, bsp, indexService, threadContext, clusterService, complianceConfig, auditlog, maskedFields, shardId)); this.includes = includes; this.bsp = bsp; @@ -671,12 +672,16 @@ public BinaryDocValues getBinaryDocValues(final String field) throws IOException private BinaryDocValues wrapBinaryDocValues(final String field, final BinaryDocValues binaryDocValues) { final Map rtMask; + final String matchedPattern; - if (binaryDocValues != null && (rtMask=getRuntimeMaskedFieldInfo())!=null) { + if (binaryDocValues != null && (rtMask=getRuntimeMaskedFieldInfo())!=null + && (matchedPattern = WildcardMatcher.getFirstMatchingPattern(rtMask.keySet(), handleKeyword(field)).orElse(null)) != null) { - final Optional matchedPattern = WildcardMatcher.getFirstMatchingPattern(rtMask.keySet(), handleKeyword(field)); - assert matchedPattern.isPresent(); - final MaskedField mf = rtMask.get(matchedPattern.get()); + final MaskedField mf = rtMask.get(matchedPattern); + + if(mf == null) { + return binaryDocValues; + } return new BinaryDocValues() { @@ -724,13 +729,16 @@ public SortedDocValues getSortedDocValues(final String field) throws IOException private SortedDocValues wrapSortedDocValues(final String field, final SortedDocValues sortedDocValues) { final Map rtMask; + final String matchedPattern; - if (sortedDocValues != null && (rtMask=getRuntimeMaskedFieldInfo())!=null) { + if (sortedDocValues != null && (rtMask=getRuntimeMaskedFieldInfo())!=null + && (matchedPattern = WildcardMatcher.getFirstMatchingPattern(rtMask.keySet(), handleKeyword(field)).orElse(null)) != null) { - final Optional matchedPattern = WildcardMatcher.getFirstMatchingPattern(rtMask.keySet(), handleKeyword(field)); - assert matchedPattern.isPresent(); - final MaskedField mf = rtMask.get(matchedPattern.get()); + final MaskedField mf = rtMask.get(matchedPattern); + if(mf == null) { + return sortedDocValues; + } return new SortedDocValues() { @@ -813,12 +821,16 @@ public SortedSetDocValues getSortedSetDocValues(final String field) throws IOExc private SortedSetDocValues wrapSortedSetDocValues(final String field, final SortedSetDocValues sortedSetDocValues) { final Map rtMask; + final String matchedPattern; - if (sortedSetDocValues != null && (rtMask=getRuntimeMaskedFieldInfo())!=null) { + if (sortedSetDocValues != null && (rtMask=getRuntimeMaskedFieldInfo()) !=null + && (matchedPattern = WildcardMatcher.getFirstMatchingPattern(rtMask.keySet(), handleKeyword(field)).orElse(null)) != null) { - final Optional matchedPattern = WildcardMatcher.getFirstMatchingPattern(rtMask.keySet(), handleKeyword(field)); - assert matchedPattern.isPresent(); - final MaskedField mf = rtMask.get(matchedPattern.get()); + final MaskedField mf = rtMask.get(matchedPattern); + + if(mf == null) { + return sortedSetDocValues; + } return new SortedSetDocValues() { @@ -1005,7 +1017,7 @@ public CacheHelper getReaderCacheHelper() { @Override public boolean hasDeletions() { - return true; + return dlsEnabled?true:in.hasDeletions(); } @SuppressWarnings("unchecked") @@ -1016,7 +1028,7 @@ private Map getRuntimeMaskedFieldInfo() { } final Map> maskedFieldsMap = (Map>) HeaderHelper.deserializeSafeFromHeader(threadContext, - ConfigConstants.OPENDISTRO_SECURITY_MASKED_FIELD_HEADER); + ConfigConstants.OPENDISTRO_SECURITY_MASKED_FIELD_HEADER); final String maskedEval = OpenDistroSecurityUtils.evalMap(maskedFieldsMap, indexService.index().getName()); if(maskedEval != null) { @@ -1037,4 +1049,4 @@ private String handleKeyword(final String field) { return field; } -} +} \ No newline at end of file diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AbstractApiAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AbstractApiAction.java index 9fab3c1..183b790 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AbstractApiAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AbstractApiAction.java @@ -17,18 +17,15 @@ import java.io.IOException; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.elasticsearch.ElasticsearchException; +import org.elasticsearch.ExceptionsHelper; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.index.IndexRequest; import org.elasticsearch.action.index.IndexResponse; @@ -43,15 +40,14 @@ import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext; import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; -import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.ThreadPool; @@ -83,14 +79,13 @@ public abstract class AbstractApiAction extends BaseRestHandler { protected final Settings settings; protected AbstractApiAction(final Settings settings, final Path configPath, final RestController controller, - final Client client, final AdminDNs adminDNs, final IndexBaseConfigurationRepository cl, - final ClusterService cs, final PrincipalExtractor principalExtractor, final PrivilegesEvaluator evaluator, - ThreadPool threadPool, AuditLog auditLog) { + final Client client, final AdminDNs adminDNs, final IndexBaseConfigurationRepository cl, + final ClusterService cs, final PrincipalExtractor principalExtractor, final PrivilegesEvaluator evaluator, + ThreadPool threadPool, AuditLog auditLog) { super(settings); this.settings = settings; this.opendistrosecurityIndex = settings.get(ConfigConstants.OPENDISTRO_SECURITY_CONFIG_INDEX_NAME, ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX); - this.cl = cl; this.cs = cs; this.threadPool = threadPool; @@ -105,145 +100,169 @@ protected AbstractApiAction(final Settings settings, final Path configPath, fina protected abstract String getConfigName(); - protected Tuple handleApiRequest(final RestRequest request, final Client client) - throws Throwable { + protected void handleApiRequest(final RestChannel channel, final RestRequest request, final Client client) throws IOException { // validate additional settings, if any AbstractConfigurationValidator validator = getValidator(request, request.content()); if (!validator.validateSettings()) { request.params().clear(); - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.BAD_REQUEST, validator.errorsAsXContent())); + channel.sendResponse(new BytesRestResponse(RestStatus.BAD_REQUEST, validator.errorsAsXContent(channel))); + return; } switch (request.method()) { - case DELETE: - return handleDelete(request, client, validator.settingsBuilder()); - case POST: - return handlePost(request, client, validator.settingsBuilder()); - case PUT: - return handlePut(request, client, validator.settingsBuilder()); - case GET: - return handleGet(request, client, validator.settingsBuilder()); - default: - throw new IllegalArgumentException(request.method() + " not supported"); + case DELETE: + handleDelete(channel,request, client, validator.settingsBuilder()); break; + case POST: + handlePost(channel,request, client, validator.settingsBuilder());break; + case PUT: + handlePut(channel,request, client, validator.settingsBuilder());break; + case GET: + handleGet(channel,request, client, validator.settingsBuilder());break; + default: + throw new IllegalArgumentException(request.method() + " not supported"); } } - protected Tuple handleDelete(final RestRequest request, final Client client, - final Settings.Builder additionalSettingsBuilder) throws Throwable { + protected void handleDelete(final RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettingsBuilder) throws IOException { final String name = request.param("name"); if (name == null || name.length() == 0) { - return badRequestResponse("No " + getResourceName() + " specified"); + badRequestResponse(channel, "No " + getResourceName() + " specified."); + return; } - final Settings existingAsSettings = loadAsSettings(getConfigName(), false); + final Tuple existingAsSettings = loadAsSettings(getConfigName(), false); - if (isHidden(existingAsSettings, name)) { - return notFound(getResourceName() + " " + name + " not found."); + if (isHidden(existingAsSettings.v2(), name)) { + notFound(channel, getResourceName() + " " + name + " not found."); + return; } - if (isReadOnly(existingAsSettings, name)) { - return forbidden("Resource '"+ name +"' is read-only."); + if (isReadOnly(existingAsSettings.v2(), name)) { + forbidden(channel, "Resource '"+ name +"' is read-only."); + return; } - final Map config = Utils.convertJsonToxToStructuredMap(Settings.builder().put(existingAsSettings).build()); + final Map config = Utils.convertJsonToxToStructuredMap(Settings.builder().put(existingAsSettings.v2()).build()); boolean resourceExisted = config.containsKey(name); config.remove(name); if (resourceExisted) { - save(client, request, getConfigName(), Utils.convertStructuredMapToBytes(config)); - return successResponse("'" + name + "' deleted.", getConfigName()); + saveAnUpdateConfigs(client, request, getConfigName(), Utils.convertStructuredMapToBytes(config), new OnSucessActionListener(channel) { + + @Override + public void onResponse(IndexResponse response) { + successResponse(channel, "'" + name + "' deleted."); + } + }, existingAsSettings.v1()); + } else { - return notFound(getResourceName() + " " + name + " not found."); + notFound(channel, getResourceName() + " " + name + " not found."); } } - protected Tuple handlePut(final RestRequest request, final Client client, - final Settings.Builder additionalSettingsBuilder) throws Throwable { + protected void handlePut(final RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettingsBuilder) throws IOException { final String name = request.param("name"); if (name == null || name.length() == 0) { - return badRequestResponse("No " + getResourceName() + " specified"); + badRequestResponse(channel, "No " + getResourceName() + " specified."); + return; } - final Settings existingAsSettings = loadAsSettings(getConfigName(), false); + final Tuple existingAsSettings = loadAsSettings(getConfigName(), false); - if (isHidden(existingAsSettings, name)) { - return forbidden("Resource '"+ name +"' is not available."); + if (isHidden(existingAsSettings.v2(), name)) { + forbidden(channel, "Resource '"+ name +"' is not available."); + return; } - if (isReadOnly(existingAsSettings, name)) { - return forbidden("Resource '"+ name +"' is read-only."); + if (isReadOnly(existingAsSettings.v2(), name)) { + forbidden(channel, "Resource '"+ name +"' is read-only."); + return; } if (log.isTraceEnabled()) { log.trace(additionalSettingsBuilder.build()); } - final Map con = Utils.convertJsonToxToStructuredMap(existingAsSettings); + final Map con = Utils.convertJsonToxToStructuredMap(existingAsSettings.v2()); boolean existed = con.containsKey(name); con.put(name, Utils.convertJsonToxToStructuredMap(additionalSettingsBuilder.build())); - save(client, request, getConfigName(), Utils.convertStructuredMapToBytes(con)); - if (existed) { - return successResponse("'" + name + "' updated.", getConfigName()); - } else { - return createdResponse("'" + name + "' created.", getConfigName()); - } + saveAnUpdateConfigs(client, request, getConfigName(), Utils.convertStructuredMapToBytes(con), new OnSucessActionListener(channel) { + + @Override + public void onResponse(IndexResponse response) { + if (existed) { + successResponse(channel, "'" + name + "' updated."); + } else { + createdResponse(channel, "'" + name + "' created."); + } + + } + }, existingAsSettings.v1()); + } - protected Tuple handlePost(final RestRequest request, final Client client, - final Settings.Builder additionalSettings) throws Throwable { - return notImplemented(Method.POST); + protected void handlePost(final RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettings) throws IOException { + notImplemented(channel, Method.POST); } - protected Tuple handleGet(RestRequest request, Client client, Builder additionalSettings) - throws Throwable { + protected void handleGet(final RestChannel channel, RestRequest request, Client client, Builder additionalSettings) + throws IOException{ final String resourcename = request.param("name"); - final Settings.Builder settingsBuilder = load(getConfigName(), true); + final Tuple settingsBuilder = load(getConfigName(), true); // filter hidden resources and sensitive settings - filter(settingsBuilder); + filter(settingsBuilder.v2()); - final Settings configurationSettings = settingsBuilder.build(); + final Settings configurationSettings = settingsBuilder.v2().build(); // no specific resource requested, return complete config if (resourcename == null || resourcename.length() == 0) { - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.OK, convertToJson(configurationSettings))); + channel.sendResponse( + new BytesRestResponse(RestStatus.OK, convertToJson(channel, configurationSettings))); + return; } final Map con = - new HashMap<>(Utils.convertJsonToxToStructuredMap(Settings.builder().put(configurationSettings).build())) - .entrySet() - .stream() - .filter(f->f.getKey() != null && f.getKey().equals(resourcename)) //copy keys - .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())); + new HashMap<>(Utils.convertJsonToxToStructuredMap(Settings.builder().put(configurationSettings).build())) + .entrySet() + .stream() + .filter(f->f.getKey() != null && f.getKey().equals(resourcename)) //copy keys + .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue())); if (!con.containsKey(resourcename)) { - return notFound("Resource '" + resourcename + "' not found."); + notFound(channel, "Resource '" + resourcename + "' not found."); + return; } - return new Tuple(new String[0], + + channel.sendResponse( new BytesRestResponse(RestStatus.OK, XContentHelper.convertToJson(Utils.convertStructuredMapToBytes(con), false, false, XContentType.JSON))); + + return; } - protected final Settings.Builder load(final String config, boolean triggerComplianceWhenCached) { - return Settings.builder().put(loadAsSettings(config, triggerComplianceWhenCached)); + protected final Tuple load(final String config, boolean logComplianceEvent) { + Tuple t = loadAsSettings(config, logComplianceEvent); + return new Tuple(t.v1(), Settings.builder().put(t.v2())); } - protected final Settings loadAsSettings(final String config, boolean triggerComplianceWhenCached) { - return cl.getConfiguration(config, triggerComplianceWhenCached); + protected final Tuple loadAsSettings(final String config, boolean logComplianceEvent) { + return cl.loadConfigurations(Collections.singleton(config), logComplianceEvent).get(config); } - protected boolean ensureIndexExists(final Client client) { + protected boolean ensureIndexExists() { if (!cs.state().metaData().hasConcreteIndex(this.opendistrosecurityIndex)) { return false; } @@ -251,26 +270,41 @@ protected boolean ensureIndexExists(final Client client) { } protected void filter(Settings.Builder builder) { - Settings settings = builder.build(); - - for (Map.Entry entry : settings.getAsGroups(true).entrySet()) { - if (entry.getValue().getAsBoolean("hidden", false)) { - for (String subKey : entry.getValue().keySet()) { - builder.remove(entry.getKey() + "." + subKey); - } - } - } + Settings settings = builder.build(); + + for (String key: settings.names()) { + if (settings.getAsBoolean(key+".hidden", false)) { + for (String subKey : settings.getByPrefix(key).keySet()) { + builder.remove(key+subKey); + } + } + } } - protected void save(final Client client, final RestRequest request, final String config, - final Settings.Builder settings) throws Throwable { - save(client, request, config, toSource(settings)); + abstract class OnSucessActionListener implements ActionListener { + + private final RestChannel channel; + + public OnSucessActionListener(RestChannel channel) { + super(); + this.channel = channel; + } + + @Override + public final void onFailure(Exception e) { + internalErrorResponse(channel, "Error "+e.getMessage()); + } + } - protected void save(final Client client, final RestRequest request, final String config, - final BytesReference bytesRef) throws Throwable { - final Semaphore sem = new Semaphore(0); - final List exception = new ArrayList(1); + protected void saveAnUpdateConfigs(final RestChannel channel, final Client client, final RestRequest request, final String config, + final Settings.Builder settings, OnSucessActionListener actionListener, long version) { + saveAnUpdateConfigs(client, request, config, toSource(channel, settings), actionListener, version); + } + + + protected void saveAnUpdateConfigs(final Client client, final RestRequest request, final String config, + final BytesReference bytesRef, OnSucessActionListener actionListener, long version) { final IndexRequest ir = new IndexRequest(this.opendistrosecurityIndex); String type = "security"; @@ -281,49 +315,63 @@ protected void save(final Client client, final RestRequest request, final String id = "0"; } - client.index(ir.type(type).id(id).setRefreshPolicy(RefreshPolicy.IMMEDIATE).source(config, bytesRef), - new ActionListener() { - - @Override - public void onResponse(final IndexResponse response) { - sem.release(); - if (logger.isDebugEnabled()) { - logger.debug("{} successfully updated", config); - } - } - - @Override - public void onFailure(final Exception e) { - sem.release(); - exception.add(e); - logger.error("Cannot update {} due to", config, e); - } - }); - - if (!sem.tryAcquire(2, TimeUnit.MINUTES)) { - // timeout - logger.error("Cannot update {} due to timeout}", config); - throw new ElasticsearchException("Timeout updating " + config); + client.index(ir.type(type).id(id) + .setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .version(version) + .source(config, bytesRef), + new ConfigUpdatingActionListener(client, actionListener)); + } + + private static class ConfigUpdatingActionListener implements ActionListener{ + + private final Client client; + private final ActionListener delegate; + + public ConfigUpdatingActionListener(Client client, ActionListener delegate) { + super(); + this.client = client; + this.delegate = delegate; + } + + @Override + public void onResponse(Response response) { + + final ConfigUpdateRequest cur = new ConfigUpdateRequest(new String[] { "config", "roles", "rolesmapping", "internalusers", "actiongroups" }); + + client.execute(ConfigUpdateAction.INSTANCE, cur, new ActionListener() { + @Override + public void onResponse(final ConfigUpdateResponse ur) { + delegate.onResponse(response); + } + + @Override + public void onFailure(final Exception e) { + delegate.onFailure(e); + } + }); + } - if (exception.size() > 0) { - throw exception.get(0); + @Override + public void onFailure(Exception e) { + delegate.onFailure(e); } } @Override - protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { + protected final RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException { // consume all parameters first so we can return a correct HTTP status, // not 400 consumeParameters(request); - // TODO: - Initialize if non-existant // check if Security index has been initialized - if (!ensureIndexExists(client)) { + if (!ensureIndexExists()) { return channel -> channel.sendResponse( - new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, ErrorType.OPENDISTRO_SECURITY_NOT_INITIALIZED.getMessage())); + new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, ErrorType.OPENDISTRO_SECURITY_NOT_INITIALIZED.getMessage())); // TODO + // return + // json } // check if request is authorized @@ -332,97 +380,42 @@ protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient cli if (authError != null) { logger.error("No permission to access REST API: " + authError); final User user = (User) threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - auditLog.logMissingPrivileges(authError, user==null?null:user.getName(), request); + auditLog.logMissingPrivileges(authError, user == null ? null : user.getName(), request); // for rest request request.params().clear(); - final BytesRestResponse response = (BytesRestResponse)forbidden("No permission to access REST API: " + authError).v2(); - return channel -> channel.sendResponse(response); + return channel -> forbidden(channel, "No permission to access REST API: " + authError); } - final Semaphore sem = new Semaphore(0); - final List exception = new ArrayList(1); - final Tuple response; - final Object originalUser = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); - final Object originalRemoteAddress = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); + final Object originalRemoteAddress = threadPool.getThreadContext() + .getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); final Object originalOrigin = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN); - try (StoredContext ctx = threadPool.getThreadContext().stashContext()) { - - threadPool.getThreadContext().putHeader(ConfigConstants.OPENDISTRO_SECURITY_CONF_REQUEST_HEADER, "true"); - threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, originalUser); - threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS, originalRemoteAddress); - threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN, originalOrigin); - - response = handleApiRequest(request, client); - - // reload config - if (response.v1().length > 0) { - - final ConfigUpdateRequest cur = new ConfigUpdateRequest(response.v1()); - // cur.putInContext(ConfigConstants.OPENDISTRO_SECURITY_USER, - // new User((String) - // request.getFromContext(ConfigConstants.OPENDISTRO_SECURITY_SSL_PRINCIPAL))); - - client.execute(ConfigUpdateAction.INSTANCE, cur, new ActionListener() { - - @Override - public void onFailure(final Exception e) { - sem.release(); - logger.error("Cannot update {} due to", Arrays.toString(response.v1()), e); - exception.add(e); - } - - @Override - public void onResponse(final ConfigUpdateResponse ur) { - sem.release(); - if (!checkConfigUpdateResponse(ur)) { - logger.error("Cannot update {}", Arrays.toString(response.v1())); - exception.add( - new ElasticsearchException("Unable to update " + Arrays.toString(response.v1()))); - } else if (logger.isDebugEnabled()) { - logger.debug("Configs {} successfully updated", Arrays.toString(response.v1())); - } - } - }); - - } else { - sem.release(); - } + return channel -> { - } catch (final Throwable e) { - logger.error("Unexpected exception {}", e.toString(), e); - request.params().clear(); - return channel -> channel - .sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, e.toString())); - } + try (StoredContext ctx = threadPool.getThreadContext().stashContext()) { - try { - if (!sem.tryAcquire(2, TimeUnit.MINUTES)) { - // timeout - logger.error("Cannot update {} due to timeout", Arrays.toString(response.v1())); - throw new ElasticsearchException("Timeout updating " + Arrays.toString(response.v1())); + threadPool.getThreadContext().putHeader(ConfigConstants.OPENDISTRO_SECURITY_CONF_REQUEST_HEADER, "true"); + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, originalUser); + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS, originalRemoteAddress); + threadPool.getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_ORIGIN, originalOrigin); + + handleApiRequest(channel, request, client); } - } catch (final InterruptedException e) { - Thread.currentThread().interrupt(); - } + }; + } - if (exception.size() > 0) { - request.params().clear(); - return channel -> channel - .sendResponse(new BytesRestResponse(RestStatus.INTERNAL_SERVER_ERROR, exception.get(0).toString())); + protected static BytesReference toSource(RestChannel channel, final Settings.Builder settingsBuilder) { //not throws + try { + final XContentBuilder builder = channel.newBuilder(); + builder.startObject(); // 1 + settingsBuilder.build().toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); // 2 + return BytesReference.bytes(builder); + } catch (IOException e) { + throw ExceptionsHelper.convertToElastic(e); } - return channel -> channel.sendResponse(response.v2()); - - } - - protected static BytesReference toSource(final Settings.Builder settingsBuilder) throws IOException { - final XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); // 1 - settingsBuilder.build().toXContent(builder, ToXContent.EMPTY_PARAMS); - builder.endObject(); // 2 - return BytesReference.bytes(builder); } protected boolean checkConfigUpdateResponse(final ConfigUpdateResponse response) { @@ -452,72 +445,72 @@ protected boolean checkConfigUpdateResponse(final ConfigUpdateResponse response) return success; } - protected static XContentBuilder convertToJson(Settings settings) throws IOException { - XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.prettyPrint(); - builder.startObject(); - settings.toXContent(builder, ToXContent.EMPTY_PARAMS); - builder.endObject(); - return builder; + protected static XContentBuilder convertToJson(RestChannel channel, Settings settings) { + try { + XContentBuilder builder = channel.newBuilder(); + builder.startObject(); + settings.toXContent(builder, ToXContent.EMPTY_PARAMS); + builder.endObject(); + return builder; + } catch (IOException e) { + throw ExceptionsHelper.convertToElastic(e); + } } - protected Tuple response(RestStatus status, String statusString, String message, - String... configs) { + protected void response(RestChannel channel, RestStatus status, String statusString, String message) { try { - final XContentBuilder builder = XContentFactory.jsonBuilder(); + final XContentBuilder builder = channel.newBuilder(); builder.startObject(); builder.field("status", statusString); builder.field("message", message); builder.endObject(); - String[] configsToUpdate = configs == null ? new String[0] : configs; - return new Tuple(configsToUpdate, new BytesRestResponse(status, builder)); - } catch (IOException ex) { - logger.error("Cannot build response", ex); - return null; + channel.sendResponse(new BytesRestResponse(status, builder)); + } catch (IOException e) { + throw ExceptionsHelper.convertToElastic(e); } } - protected Tuple successResponse(String message, String... configs) { - return response(RestStatus.OK, RestStatus.OK.name(), message, configs); + protected void successResponse(RestChannel channel, String message) { + response(channel, RestStatus.OK, RestStatus.OK.name(), message); } - protected Tuple createdResponse(String message, String... configs) { - return response(RestStatus.CREATED, RestStatus.CREATED.name(), message, configs); + protected void createdResponse(RestChannel channel, String message) { + response(channel, RestStatus.CREATED, RestStatus.CREATED.name(), message); } - protected Tuple badRequestResponse(String message) { - return response(RestStatus.BAD_REQUEST, RestStatus.BAD_REQUEST.name(), message); + protected void badRequestResponse(RestChannel channel, String message) { + response(channel, RestStatus.BAD_REQUEST, RestStatus.BAD_REQUEST.name(), message); } - protected Tuple notFound(String message) { - return response(RestStatus.NOT_FOUND, RestStatus.NOT_FOUND.name(), message); + protected void notFound(RestChannel channel, String message) { + response(channel, RestStatus.NOT_FOUND, RestStatus.NOT_FOUND.name(), message); } - protected Tuple forbidden(String message) { - return response(RestStatus.FORBIDDEN, RestStatus.FORBIDDEN.name(), message); + protected void forbidden(RestChannel channel, String message) { + response(channel, RestStatus.FORBIDDEN, RestStatus.FORBIDDEN.name(), message); } - protected Tuple internalErrorResponse(String message) { - return response(RestStatus.INTERNAL_SERVER_ERROR, RestStatus.INTERNAL_SERVER_ERROR.name(), message); + protected void internalErrorResponse(RestChannel channel, String message) { + response(channel, RestStatus.INTERNAL_SERVER_ERROR, RestStatus.INTERNAL_SERVER_ERROR.name(), message); } - protected Tuple unprocessable(String message) { - return response(RestStatus.UNPROCESSABLE_ENTITY, RestStatus.UNPROCESSABLE_ENTITY.name(), message); + protected void unprocessable(RestChannel channel, String message) { + response(channel, RestStatus.UNPROCESSABLE_ENTITY, RestStatus.UNPROCESSABLE_ENTITY.name(), message); } - protected Tuple notImplemented(Method method) { - return response(RestStatus.NOT_IMPLEMENTED, RestStatus.NOT_IMPLEMENTED.name(), + protected void notImplemented(RestChannel channel, Method method) { + response(channel, RestStatus.NOT_IMPLEMENTED, RestStatus.NOT_IMPLEMENTED.name(), "Method " + method.name() + " not supported for this action."); } protected boolean isReadOnly(Settings settings, String resourceName) { - return settings.getAsBoolean(resourceName+ "." + ConfigConstants.CONFIGKEY_READONLY, Boolean.FALSE); + return settings.getAsBoolean(resourceName+ "." + ConfigConstants.CONFIGKEY_READONLY, Boolean.FALSE); } - protected boolean isHidden(Settings settings, String resourceName) { - return settings.getAsBoolean(resourceName+ "." + ConfigConstants.CONFIGKEY_HIDDEN, Boolean.FALSE); - } + protected boolean isHidden(Settings settings, String resourceName) { + return settings.getAsBoolean(resourceName+ "." + ConfigConstants.CONFIGKEY_HIDDEN, Boolean.FALSE); + } /** * Consume all defined parameters for the request. Before we handle the @@ -539,4 +532,4 @@ public String getName() { protected abstract Endpoint getEndpoint(); -} +} \ No newline at end of file diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/ActionGroupsApiAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/ActionGroupsApiAction.java index e164f90..7c5dc32 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/ActionGroupsApiAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/ActionGroupsApiAction.java @@ -38,10 +38,15 @@ public class ActionGroupsApiAction extends PatchableResourceApiAction { + @Override + protected Endpoint getEndpoint() { + return Endpoint.ACTIONGROUPS; + } + @Inject public ActionGroupsApiAction(final Settings settings, final Path configPath, final RestController controller, final Client client, - final AdminDNs adminDNs, final IndexBaseConfigurationRepository cl, final ClusterService cs, - final PrincipalExtractor principalExtractor, final PrivilegesEvaluator evaluator, ThreadPool threadPool, AuditLog auditLog) { + final AdminDNs adminDNs, final IndexBaseConfigurationRepository cl, final ClusterService cs, final PrincipalExtractor principalExtractor, + final PrivilegesEvaluator evaluator, ThreadPool threadPool, AuditLog auditLog) { super(settings, configPath, controller, client, adminDNs, cl, cs, principalExtractor, evaluator, threadPool, auditLog); // legacy mapping for backwards compatibility @@ -56,16 +61,11 @@ public ActionGroupsApiAction(final Settings settings, final Path configPath, fin controller.registerHandler(Method.GET, "/_opendistro/_security/api/actiongroups/", this); controller.registerHandler(Method.DELETE, "/_opendistro/_security/api/actiongroups/{name}", this); controller.registerHandler(Method.PUT, "/_opendistro/_security/api/actiongroups/{name}", this); - controller.registerHandler(Method.PATCH, "/_opendistro/_security/api/actiongroups/", this); - controller.registerHandler(Method.PATCH, "/_opendistro/_security/api/actiongroups/{name}", this); + controller.registerHandler(Method.PATCH, "/_opendistro/_security/api/actiongroups/", this); + controller.registerHandler(Method.PATCH, "/_opendistro/_security/api/actiongroups/{name}", this); } - @Override - protected Endpoint getEndpoint() { - return Endpoint.ACTIONGROUPS; - } - @Override protected AbstractConfigurationValidator getValidator(final RestRequest request, BytesReference ref, Object... param) { return new ActionGroupValidator(request, ref, this.settings, param); diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AuthTokenProcessorAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AuthTokenProcessorAction.java index 734ece0..1cc9946 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AuthTokenProcessorAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/AuthTokenProcessorAction.java @@ -24,10 +24,10 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; -import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.ThreadPool; @@ -52,13 +52,13 @@ public AuthTokenProcessorAction(final Settings settings, final Path configPath, } @Override - protected Tuple handlePost(final RestRequest request, final Client client, - final Settings.Builder additionalSettings) throws Throwable { + protected void handlePost(RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettings) { // Just do nothing here. Eligible authenticators will intercept calls and // provide own responses. - return new Tuple(new String[0], new BytesRestResponse(RestStatus.OK, "")); + channel.sendResponse(new BytesRestResponse(RestStatus.OK, "")); } @Override diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/FlushCacheApiAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/FlushCacheApiAction.java index 705d01f..d7cf091 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/FlushCacheApiAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/FlushCacheApiAction.java @@ -16,23 +16,18 @@ package com.amazon.opendistroforelasticsearch.security.dlic.rest.api; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import org.elasticsearch.action.ActionListener; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesReference; -import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings.Builder; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; -import org.elasticsearch.rest.RestResponse; import org.elasticsearch.threadpool.ThreadPool; import com.amazon.opendistroforelasticsearch.security.action.configupdate.ConfigUpdateAction; @@ -65,11 +60,9 @@ protected Endpoint getEndpoint() { } @Override - protected Tuple handleDelete(RestRequest request, Client client, Builder additionalSettingsBuilder) - throws Throwable { - - final Semaphore sem = new Semaphore(0); - final List exception = new ArrayList(1); + protected void handleDelete(RestChannel channel, + RestRequest request, Client client, Builder additionalSettingsBuilder) + { client.execute( ConfigUpdateAction.INSTANCE, @@ -78,7 +71,7 @@ protected Tuple handleDelete(RestRequest request, Client @Override public void onResponse(ConfigUpdateResponse response) { - sem.release(); + successResponse(channel, "Cache flushed successfully."); if (logger.isDebugEnabled()) { logger.debug("cache flushed successfully"); } @@ -86,43 +79,30 @@ public void onResponse(ConfigUpdateResponse response) { @Override public void onFailure(Exception e) { - sem.release(); - exception.add(e); - logger.error("Cannot flush cache due to {}", e.toString(), e); + logger.error("Cannot flush cache due to", e); + internalErrorResponse(channel, "Cannot flush cache due to "+ e.getMessage()+"."); } } ); - - if (!sem.tryAcquire(30, TimeUnit.SECONDS)) { - logger.error("Cannot flush cache due to timeout"); - return internalErrorResponse("Cannot flush cache due to timeout"); - } - - if (exception.size() > 0) { - logger.error("Cannot flush cache due to", exception.get(0)); - return internalErrorResponse("Cannot flush cache due to "+ exception.get(0).getMessage()); - } - - return successResponse("Cache flushed successfully.", new String[0]); } @Override - protected Tuple handlePost(final RestRequest request, final Client client, - final Settings.Builder additionalSettings) throws Throwable { - return notImplemented(Method.POST); + protected void handlePost(RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettings) { + notImplemented(channel, Method.POST); } @Override - protected Tuple handleGet(final RestRequest request, final Client client, - final Settings.Builder additionalSettings) throws Throwable { - return notImplemented(Method.GET); + protected void handleGet(RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettings) { + notImplemented(channel, Method.GET); } @Override - protected Tuple handlePut(final RestRequest request, final Client client, - final Settings.Builder additionalSettings) throws Throwable { - return notImplemented(Method.PUT); + protected void handlePut(RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettings) { + notImplemented(channel, Method.PUT); } @Override @@ -147,4 +127,4 @@ protected void consumeParameters(final RestRequest request) { // not needed } -} +} \ No newline at end of file diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/GetConfigurationApiAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/GetConfigurationApiAction.java index ae2f783..2f64993 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/GetConfigurationApiAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/GetConfigurationApiAction.java @@ -26,10 +26,10 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; -import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.ThreadPool; @@ -64,32 +64,34 @@ protected Endpoint getEndpoint() { } @Override - protected Tuple handleGet(RestRequest request, Client client, - final Settings.Builder additionalSettingsBuilder) throws Throwable { + protected void handleGet(RestChannel channel, RestRequest request, Client client, + final Settings.Builder additionalSettingsBuilder) { final String configname = request.param("configname"); if (configname == null || configname.length() == 0 || !ConfigConstants.CONFIG_NAMES.contains(configname)) { - return badRequestResponse("No configuration name given, must be one of " + badRequestResponse(channel, "No configuration name given, must be one of " + Joiner.on(",").join(ConfigConstants.CONFIG_NAMES)); + return; } - final Settings.Builder configBuilder = load(configname, true); - filter(configBuilder, configname); - final Settings config = configBuilder.build(); + final Tuple configBuilder = load(configname, true); + filter(configBuilder.v2(), configname); + final Settings config = configBuilder.v2().build(); - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.OK, convertToJson(config))); + channel.sendResponse( + new BytesRestResponse(RestStatus.OK, convertToJson(channel, config))); + return; } protected void filter(Settings.Builder builder, String resourceName) { - // common filtering - filter(builder); - // filter sensitive resources for internal users - if (resourceName.equals("internalusers")) { - filterHashes(builder); - } + // common filtering + filter(builder); + // filter sensitive resources for internal users + if (resourceName.equals("internalusers")) { + filterHashes(builder); + } } @Override @@ -114,13 +116,13 @@ protected void consumeParameters(final RestRequest request) { request.param("configname"); } - private void filterHashes(Settings.Builder builder) { - // replace password hashes in addition. We must not remove them from the - // Builder since this would remove users completely if they - // do not have any addition properties like roles or attributes - Set entries = builder.build().getAsGroups().keySet(); - for (String key : entries) { - builder.put(key + ".hash", ""); - } - } -} + private void filterHashes(Settings.Builder builder) { + // replace password hashes in addition. We must not remove them from the + // Builder since this would remove users completely if they + // do not have any addition properties like roles or attributes + Set entries = builder.build().names(); + for (String key : entries) { + builder.put(key + ".hash", ""); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/InternalUsersApiAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/InternalUsersApiAction.java index 17eaf41..618fb4f 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/InternalUsersApiAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/InternalUsersApiAction.java @@ -24,6 +24,7 @@ import java.util.Set; import org.bouncycastle.crypto.generators.OpenBSDBCrypt; +import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.bytes.BytesReference; @@ -31,11 +32,10 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; -import org.elasticsearch.rest.RestResponse; import org.elasticsearch.threadpool.ThreadPool; import com.fasterxml.jackson.databind.JsonNode; @@ -84,33 +84,37 @@ protected Endpoint getEndpoint() { } @Override - protected Tuple handlePut(final RestRequest request, final Client client, - final Settings.Builder additionalSettingsBuilder) throws Throwable { + protected void handlePut(RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettingsBuilder) { final String username = request.param("name"); if (username == null || username.length() == 0) { - return badRequestResponse("No " + getResourceName() + " specified"); + badRequestResponse(channel, "No " + getResourceName() + " specified."); + return; } if(username.contains(".")) { - return badRequestResponse("No dots are allowed in the name. User the username attribute."); + badRequestResponse(channel, "No dots are allowed in the name."); + return; } // TODO it might be sensible to consolidate this with the overridden method in // order to minimize duplicated logic - final Settings configurationSettings = loadAsSettings(getConfigName(), false); + final Tuple configurationSettings = loadAsSettings(getConfigName(), false); - if (isHidden(configurationSettings, username)) { - return forbidden("Resource '" + username + "' is not available."); + if (isHidden(configurationSettings.v2(), username)) { + forbidden(channel, "Resource '" + username + "' is not available."); + return; } // check if resource is writeable - Boolean readOnly = configurationSettings.getAsBoolean(username + "." + ConfigConstants.CONFIGKEY_READONLY, + Boolean readOnly = configurationSettings.v2().getAsBoolean(username + "." + ConfigConstants.CONFIGKEY_READONLY, Boolean.FALSE); if (readOnly) { - return forbidden("Resource '" + username + "' is read-only."); + forbidden(channel, "Resource '" + username + "' is read-only."); + return; } // if password is set, it takes precedence over hash @@ -121,8 +125,8 @@ protected Tuple handlePut(final RestRequest request, fin } // check if user exists - final Settings.Builder internaluser = load(ConfigConstants.CONFIGNAME_INTERNAL_USERS, false); - final Map config = Utils.convertJsonToxToStructuredMap(internaluser.build()); + final Tuple internaluser = load(ConfigConstants.CONFIGNAME_INTERNAL_USERS, false); + final Map config = Utils.convertJsonToxToStructuredMap(internaluser.v2().build()); final boolean userExisted = config.containsKey(username); @@ -131,7 +135,8 @@ protected Tuple handlePut(final RestRequest request, fin // sanity checks, hash is mandatory for newly created users if (!userExisted && additionalSettingsBuilder.get("hash") == null) { - return badRequestResponse("Please specify either 'hash' or 'password' when creating a new internal user"); + badRequestResponse(channel, "Please specify either 'hash' or 'password' when creating a new internal user."); + return; } // for existing users, hash is optional @@ -140,8 +145,9 @@ protected Tuple handlePut(final RestRequest request, fin @SuppressWarnings("unchecked") Map existingUserSettings = (Map) config.get(username); if (!existingUserSettings.containsKey("hash")) { - return internalErrorResponse( - "Existing user " + username + " has no password, and no new password or hash was specified"); + internalErrorResponse(channel, + "Existing user " + username + " has no password, and no new password or hash was specified."); + return; } additionalSettingsBuilder.put("hash", (String) existingUserSettings.get("hash")); } @@ -151,13 +157,20 @@ protected Tuple handlePut(final RestRequest request, fin // checks complete, create or update the user config.put(username, Utils.convertJsonToxToStructuredMap(additionalSettingsBuilder.build())); - save(client, request, ConfigConstants.CONFIGNAME_INTERNAL_USERS, Utils.convertStructuredMapToBytes(config)); + saveAnUpdateConfigs(client, request, ConfigConstants.CONFIGNAME_INTERNAL_USERS, Utils.convertStructuredMapToBytes(config), new OnSucessActionListener(channel) { + + @Override + public void onResponse(IndexResponse response) { + if (userExisted) { + successResponse(channel, "'" + username + "' updated."); + } else { + createdResponse(channel, "'" + username + "' created."); + } + + } + }, internaluser.v1()); + - if (userExisted) { - return successResponse("'" + username + "' updated", ConfigConstants.CONFIGNAME_INTERNAL_USERS); - } else { - return createdResponse("'" + username + "' created", ConfigConstants.CONFIGNAME_INTERNAL_USERS); - } } @@ -167,29 +180,29 @@ protected void filter(Settings.Builder builder) { // replace password hashes in addition. We must not remove them from the // Builder since this would remove users completely if they // do not have any addition properties like roles or attributes - Set entries = builder.build().getAsGroups().keySet(); + Set entries = builder.build().names(); for (String key : entries) { builder.put(key + ".hash", ""); } } @Override - protected AbstractConfigurationValidator postProcessApplyPatchResult(RestRequest request, JsonNode existingResourceAsJsonNode, - JsonNode updatedResourceAsJsonNode, String resourceName) { - AbstractConfigurationValidator retVal = null; + protected AbstractConfigurationValidator postProcessApplyPatchResult(RestChannel channel, RestRequest request, JsonNode existingResourceAsJsonNode, + JsonNode updatedResourceAsJsonNode, String resourceName) { + AbstractConfigurationValidator retVal = null; JsonNode passwordNode = updatedResourceAsJsonNode.get("password"); if (passwordNode != null) { String plainTextPassword = passwordNode.asText(); try { - XContentBuilder builder = XContentBuilder.builder(JsonXContent.jsonXContent); - builder.startObject(); - builder.field("password", plainTextPassword); - builder.endObject(); - retVal = getValidator(request, BytesReference.bytes(builder), resourceName); - } catch (IOException e) { - log.error(e); - } + XContentBuilder builder = channel.newBuilder(); + builder.startObject(); + builder.field("password", plainTextPassword); + builder.endObject(); + retVal = getValidator(request, BytesReference.bytes(builder), resourceName); + } catch (IOException e) { + log.error(e); + } ((ObjectNode) updatedResourceAsJsonNode).remove("password"); ((ObjectNode) updatedResourceAsJsonNode).set("hash", new TextNode(hash(plainTextPassword.toCharArray()))); diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/OpenDistroSecurityConfigAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/OpenDistroSecurityConfigAction.java index 94e82ae..b8b8985 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/OpenDistroSecurityConfigAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/OpenDistroSecurityConfigAction.java @@ -24,10 +24,10 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; -import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.ThreadPool; @@ -53,25 +53,25 @@ public OpenDistroSecurityConfigAction(final Settings settings, final Path config @Override - protected Tuple handleGet(RestRequest request, Client client, - final Settings.Builder additionalSettingsBuilder) throws Throwable { + protected void handleGet(RestChannel channel, RestRequest request, Client client, + final Settings.Builder additionalSettingsBuilder) { - final Settings configurationSettings = loadAsSettings(getConfigName(), true); + final Tuple configurationSettings = loadAsSettings(getConfigName(), true); - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.OK, convertToJson(configurationSettings))); + channel.sendResponse( + new BytesRestResponse(RestStatus.OK, convertToJson(channel, configurationSettings.v2()))); } @Override - protected Tuple handlePut(final RestRequest request, final Client client, - final Settings.Builder additionalSettings) throws Throwable { - return notImplemented(Method.PUT); + protected void handlePut(RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettings) { + notImplemented(channel, Method.PUT); } @Override - protected Tuple handleDelete(final RestRequest request, final Client client, - final Settings.Builder additionalSettings) throws Throwable { - return notImplemented(Method.DELETE); + protected void handleDelete(RestChannel channel, final RestRequest request, final Client client, + final Settings.Builder additionalSettings) { + notImplemented(channel, Method.DELETE); } @Override diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PatchableResourceApiAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PatchableResourceApiAction.java index 9a4f4c9..1dd1a9f 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PatchableResourceApiAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PatchableResourceApiAction.java @@ -15,11 +15,13 @@ package com.amazon.opendistroforelasticsearch.security.dlic.rest.api; +import java.io.IOException; import java.nio.file.Path; import java.util.Iterator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.elasticsearch.action.index.IndexResponse; import org.elasticsearch.client.Client; import org.elasticsearch.cluster.service.ClusterService; import org.elasticsearch.common.Strings; @@ -29,10 +31,10 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.rest.BytesRestResponse; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; -import org.elasticsearch.rest.RestResponse; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.threadpool.ThreadPool; @@ -44,7 +46,6 @@ import com.amazon.opendistroforelasticsearch.security.dlic.rest.validation.AbstractConfigurationValidator; import com.amazon.opendistroforelasticsearch.security.privileges.PrivilegesEvaluator; import com.amazon.opendistroforelasticsearch.security.ssl.transport.PrincipalExtractor; -import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -56,60 +57,66 @@ public abstract class PatchableResourceApiAction extends AbstractApiAction { protected final Logger log = LogManager.getLogger(this.getClass()); public PatchableResourceApiAction(Settings settings, Path configPath, RestController controller, Client client, - AdminDNs adminDNs, IndexBaseConfigurationRepository cl, ClusterService cs, - PrincipalExtractor principalExtractor, PrivilegesEvaluator evaluator, ThreadPool threadPool, - AuditLog auditLog) { + AdminDNs adminDNs, IndexBaseConfigurationRepository cl, ClusterService cs, + PrincipalExtractor principalExtractor, PrivilegesEvaluator evaluator, ThreadPool threadPool, + AuditLog auditLog) { super(settings, configPath, controller, client, adminDNs, cl, cs, principalExtractor, evaluator, threadPool, auditLog); } - private Tuple handlePatch(final RestRequest request, final Client client) - throws Throwable { + private void handlePatch(RestChannel channel, final RestRequest request, final Client client) + throws IOException { if (request.getXContentType() != XContentType.JSON) { - return badRequestResponse("PATCH accepts only application/json"); + badRequestResponse(channel, "PATCH accepts only application/json"); + return; } String name = request.param("name"); - Settings existingAsSettings = loadAsSettings(getConfigName(), false); + Tuple existingAsSettings = loadAsSettings(getConfigName(), false); JsonNode jsonPatch; try { jsonPatch = DefaultObjectMapper.objectMapper.readTree(request.content().utf8ToString()); - } catch (JsonParseException e) { + } catch (IOException e) { log.debug("Error while parsing JSON patch", e); - return badRequestResponse("Error in JSON patch: " + e.getMessage()); + badRequestResponse(channel, "Error in JSON patch: " + e.getMessage()); + return; } - JsonNode existingAsJsonNode = Utils.convertJsonToJackson(existingAsSettings); + JsonNode existingAsJsonNode = Utils.convertJsonToJackson(existingAsSettings.v2()); if (!(existingAsJsonNode instanceof ObjectNode)) { - return internalErrorResponse("Config " + getConfigName() + " is malformed"); + internalErrorResponse(channel, "Config " + getConfigName() + " is malformed"); + return; } ObjectNode existingAsObjectNode = (ObjectNode) existingAsJsonNode; if (Strings.isNullOrEmpty(name)) { - return handleBulkPatch(request, client, existingAsSettings, existingAsObjectNode, jsonPatch); + handleBulkPatch(channel, request, client, existingAsSettings, existingAsObjectNode, jsonPatch); } else { - return handleSinglePatch(request, client, name, existingAsSettings, existingAsObjectNode, jsonPatch); + handleSinglePatch(channel, request, client, name, existingAsSettings, existingAsObjectNode, jsonPatch); } } - private Tuple handleSinglePatch(RestRequest request, Client client, String name, - Settings existingAsSettings, ObjectNode existingAsObjectNode, JsonNode jsonPatch) throws Throwable { - if (isHidden(existingAsSettings, name)) { - return notFound(getResourceName() + " " + name + " not found."); + private void handleSinglePatch(RestChannel channel, RestRequest request, Client client, String name, + Tuple existingAsSettings, ObjectNode existingAsObjectNode, JsonNode jsonPatch) throws IOException { + if (isHidden(existingAsSettings.v2(), name)) { + notFound(channel, getResourceName() + " " + name + " not found."); + return; } - if (isReadOnly(existingAsSettings, name)) { - return forbidden("Resource '" + name + "' is read-only."); + if (isReadOnly(existingAsSettings.v2(), name)) { + forbidden(channel, "Resource '" + name + "' is read-only."); + return; } - Settings resourceSettings = existingAsSettings.getAsSettings(name); + Settings resourceSettings = existingAsSettings.v2().getAsSettings(name); if (resourceSettings.isEmpty()) { - return notFound(getResourceName() + " " + name + " not found."); + notFound(channel, getResourceName() + " " + name + " not found."); + return; } JsonNode existingResourceAsJsonNode = existingAsObjectNode.get(name); @@ -120,25 +127,29 @@ private Tuple handleSinglePatch(RestRequest request, Cli patchedResourceAsJsonNode = applyPatch(jsonPatch, existingResourceAsJsonNode); } catch (JsonPatchApplicationException e) { log.debug("Error while applying JSON patch", e); - return badRequestResponse(e.getMessage()); + badRequestResponse(channel, e.getMessage()); + return; } - AbstractConfigurationValidator originalValidator = postProcessApplyPatchResult(request, existingResourceAsJsonNode, patchedResourceAsJsonNode, name); + AbstractConfigurationValidator originalValidator = postProcessApplyPatchResult(channel, request, existingResourceAsJsonNode, patchedResourceAsJsonNode, name); if(originalValidator != null) { - if (!originalValidator.validateSettings()) { + if (!originalValidator.validateSettings()) { request.params().clear(); - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.BAD_REQUEST, originalValidator.errorsAsXContent())); + channel.sendResponse( + new BytesRestResponse(RestStatus.BAD_REQUEST, originalValidator.errorsAsXContent(channel))); + return; } } + AbstractConfigurationValidator validator = getValidator(request, patchedResourceAsJsonNode); if (!validator.validateSettings()) { request.params().clear(); - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.BAD_REQUEST, validator.errorsAsXContent())); + channel.sendResponse( + new BytesRestResponse(RestStatus.BAD_REQUEST, validator.errorsAsXContent(channel))); + return; } JsonNode updatedAsJsonNode = existingAsObjectNode.deepCopy().set(name, patchedResourceAsJsonNode); @@ -146,13 +157,18 @@ private Tuple handleSinglePatch(RestRequest request, Cli BytesReference updatedAsBytesReference = new BytesArray( DefaultObjectMapper.objectMapper.writeValueAsString(updatedAsJsonNode).getBytes()); - save(client, request, getConfigName(), updatedAsBytesReference); + saveAnUpdateConfigs(client, request, getConfigName(), updatedAsBytesReference, new OnSucessActionListener(channel){ + + @Override + public void onResponse(IndexResponse response) { + successResponse(channel, "'" + name + "' updated."); - return successResponse("'" + name + "' updated.", getConfigName()); + } + }, existingAsSettings.v1()); } - private Tuple handleBulkPatch(RestRequest request, Client client, - Settings existingAsSettings, ObjectNode existingAsObjectNode, JsonNode jsonPatch) throws Throwable { + private void handleBulkPatch(RestChannel channel, RestRequest request, Client client, + Tuple existingAsSettings, ObjectNode existingAsObjectNode, JsonNode jsonPatch) throws IOException { JsonNode patchedAsJsonNode; @@ -160,38 +176,43 @@ private Tuple handleBulkPatch(RestRequest request, Clien patchedAsJsonNode = applyPatch(jsonPatch, existingAsObjectNode); } catch (JsonPatchApplicationException e) { log.debug("Error while applying JSON patch", e); - return badRequestResponse(e.getMessage()); + badRequestResponse(channel, e.getMessage()); + return; } - for (String resourceName : existingAsSettings.names()) { + for (String resourceName : existingAsSettings.v2().names()) { JsonNode oldResource = existingAsObjectNode.get(resourceName); JsonNode patchedResource = patchedAsJsonNode.get(resourceName); if (oldResource != null && !oldResource.equals(patchedResource)) { - if (isReadOnly(existingAsSettings, resourceName)) { - return forbidden("Resource '" + resourceName + "' is read-only."); + if (isReadOnly(existingAsSettings.v2(), resourceName)) { + forbidden(channel, "Resource '" + resourceName + "' is read-only."); + return; } - if (isHidden(existingAsSettings, resourceName)) { - return badRequestResponse("Resource name '" + resourceName + "' is reserved"); + if (isHidden(existingAsSettings.v2(), resourceName)) { + badRequestResponse(channel, "Resource name '" + resourceName + "' is reserved"); + return; } } } + for (Iterator fieldNamesIter = patchedAsJsonNode.fieldNames(); fieldNamesIter.hasNext();) { String resourceName = fieldNamesIter.next(); JsonNode oldResource = existingAsObjectNode.get(resourceName); JsonNode patchedResource = patchedAsJsonNode.get(resourceName); - AbstractConfigurationValidator originalValidator = postProcessApplyPatchResult(request, oldResource, patchedResource, resourceName); + AbstractConfigurationValidator originalValidator = postProcessApplyPatchResult(channel, request, oldResource, patchedResource, resourceName); if(originalValidator != null) { - if (!originalValidator.validateSettings()) { + if (!originalValidator.validateSettings()) { request.params().clear(); - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.BAD_REQUEST, originalValidator.errorsAsXContent())); + channel.sendResponse( + new BytesRestResponse(RestStatus.BAD_REQUEST, originalValidator.errorsAsXContent(channel))); + return; } } @@ -200,8 +221,9 @@ private Tuple handleBulkPatch(RestRequest request, Clien if (!validator.validateSettings()) { request.params().clear(); - return new Tuple(new String[0], - new BytesRestResponse(RestStatus.BAD_REQUEST, validator.errorsAsXContent())); + channel.sendResponse( + new BytesRestResponse(RestStatus.BAD_REQUEST, validator.errorsAsXContent(channel))); + return; } } } @@ -209,28 +231,33 @@ private Tuple handleBulkPatch(RestRequest request, Clien BytesReference updatedAsBytesReference = new BytesArray( DefaultObjectMapper.objectMapper.writeValueAsString(patchedAsJsonNode).getBytes()); - save(client, request, getConfigName(), updatedAsBytesReference); + saveAnUpdateConfigs(client, request, getConfigName(), updatedAsBytesReference, new OnSucessActionListener(channel) { + + @Override + public void onResponse(IndexResponse response) { + successResponse(channel, "Resource updated."); + } + }, existingAsSettings.v1()); - return successResponse("Resource updated.", getConfigName()); } private JsonNode applyPatch(JsonNode jsonPatch, JsonNode existingResourceAsJsonNode) { return JsonPatch.apply(jsonPatch, existingResourceAsJsonNode); } - protected AbstractConfigurationValidator postProcessApplyPatchResult(RestRequest request, JsonNode existingResourceAsJsonNode, JsonNode updatedResourceAsJsonNode, String resourceName) { + protected AbstractConfigurationValidator postProcessApplyPatchResult(RestChannel channel, RestRequest request, JsonNode existingResourceAsJsonNode, JsonNode updatedResourceAsJsonNode, String resourceName) { // do nothing by default - return null; + return null; } @Override - protected Tuple handleApiRequest(final RestRequest request, final Client client) - throws Throwable { + protected void handleApiRequest(RestChannel channel, final RestRequest request, final Client client) + throws IOException { if (request.method() == Method.PATCH) { - return handlePatch(request, client); + handlePatch(channel, request, client); } else { - return super.handleApiRequest(request, client); + super.handleApiRequest(channel, request, client); } } @@ -240,4 +267,4 @@ private AbstractConfigurationValidator getValidator(RestRequest request, JsonNod DefaultObjectMapper.objectMapper.writeValueAsString(patchedResource).getBytes()); return getValidator(request, patchedResourceAsByteReference); } -} +} \ No newline at end of file diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PermissionsInfoAction.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PermissionsInfoAction.java index 84e50ea..f520add 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PermissionsInfoAction.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/PermissionsInfoAction.java @@ -93,7 +93,7 @@ public void accept(RestChannel channel) throws Exception { final User user = (User) threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); final TransportAddress remoteAddress = (TransportAddress) threadPool.getThreadContext() .getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); - Set userRoles = privilegesEvaluator.mapSecurityRoles(user, remoteAddress); + Set userRoles = privilegesEvaluator.mapRoles(user, remoteAddress); Boolean hasApiAccess = restApiPrivilegesEvaluator.currentUserHasRestApiAccess(userRoles); Map> disabledEndpoints = restApiPrivilegesEvaluator.getDisabledEndpointsForCurrentUser(user.getName(), userRoles); diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java index fdea300..1c71357 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RestApiPrivilegesEvaluator.java @@ -342,7 +342,7 @@ private String checkRoleBasedAccessPermissions(RestRequest request, Endpoint end final TransportAddress remoteAddress = (TransportAddress) threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); // map the users Security roles - Set userRoles = privilegesEvaluator.mapSecurityRoles(user, remoteAddress); + Set userRoles = privilegesEvaluator.mapRoles(user, remoteAddress); // check if user has any role that grants access if (currentUserHasRestApiAccess(userRoles)) { diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/validation/AbstractConfigurationValidator.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/validation/AbstractConfigurationValidator.java index dd02322..ef6af90 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/validation/AbstractConfigurationValidator.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/validation/AbstractConfigurationValidator.java @@ -30,9 +30,9 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentBuilder; -import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.rest.RestRequest.Method; @@ -44,253 +44,246 @@ public abstract class AbstractConfigurationValidator { - JsonFactory factory = new JsonFactory(); + JsonFactory factory = new JsonFactory(); - /* public for testing */ - public final static String INVALID_KEYS_KEY = "invalid_keys"; + /* public for testing */ + public final static String INVALID_KEYS_KEY = "invalid_keys"; - /* public for testing */ - public final static String MISSING_MANDATORY_KEYS_KEY = "missing_mandatory_keys"; + /* public for testing */ + public final static String MISSING_MANDATORY_KEYS_KEY = "missing_mandatory_keys"; - /* public for testing */ - public final static String MISSING_MANDATORY_OR_KEYS_KEY = "specify_one_of"; + /* public for testing */ + public final static String MISSING_MANDATORY_OR_KEYS_KEY = "specify_one_of"; - protected final Logger log = LogManager.getLogger(this.getClass()); + protected final Logger log = LogManager.getLogger(this.getClass()); - /** Define the various keys for this validator */ - protected final Map allowedKeys = new HashMap<>(); + /** Define the various keys for this validator */ + protected final Map allowedKeys = new HashMap<>(); - protected final Set mandatoryKeys = new HashSet<>(); + protected final Set mandatoryKeys = new HashSet<>(); - protected final Set mandatoryOrKeys = new HashSet<>(); + protected final Set mandatoryOrKeys = new HashSet<>(); - protected final Map wrongDatatypes = new HashMap<>(); + protected final Map wrongDatatypes = new HashMap<>(); - /** Contain errorneous keys */ - protected final Set missingMandatoryKeys = new HashSet<>(); + /** Contain errorneous keys */ + protected final Set missingMandatoryKeys = new HashSet<>(); - protected final Set invalidKeys = new HashSet<>(); - - protected final Set missingMandatoryOrKeys = new HashSet<>(); - - /** The error type */ - protected ErrorType errorType = ErrorType.NONE; - - /** Behaviour regarding payload */ - protected boolean payloadMandatory = false; - - protected boolean payloadAllowed = true; - - private Settings.Builder settingsBuilder; - - protected final Method method; - - protected final BytesReference content; - - protected final Settings esSettings; - - protected final RestRequest request; - - protected final Object[] param; - - public AbstractConfigurationValidator(final RestRequest request, final BytesReference ref, final Settings esSettings, Object... param) { - this.content = ref; - this.method = request.method(); - this.esSettings = esSettings; - this.request = request; - this.param = param; - } - - /** - * - * @return false if validation fails - */ - public boolean validateSettings() { - // no payload for DELETE and GET requests - if (method.equals(Method.DELETE) || method.equals(Method.GET)) { - return true; - } - // try to parse payload - try { - this.settingsBuilder = toSettingsBuilder(content); - } catch (ElasticsearchException e) { - this.errorType = ErrorType.BODY_NOT_PARSEABLE; - return false; - } - - Settings settings = settingsBuilder.build(); - - Set requested = new HashSet(settings.names()); - // check if payload is accepted at all - if (!this.payloadAllowed && !requested.isEmpty()) { - this.errorType = ErrorType.PAYLOAD_NOT_ALLOWED; - return false; - } - // check if payload is mandatory - if (this.payloadMandatory && requested.isEmpty()) { - this.errorType = ErrorType.PAYLOAD_MANDATORY; - return false; - } - - // mandatory settings, one of ... - if (Collections.disjoint(requested, mandatoryOrKeys)) { - this.missingMandatoryOrKeys.addAll(mandatoryOrKeys); - } - - // mandatory settings - Set mandatory = new HashSet<>(mandatoryKeys); - mandatory.removeAll(requested); - missingMandatoryKeys.addAll(mandatory); - - // invalid settings - Set allowed = new HashSet<>(allowedKeys.keySet()); - requested.removeAll(allowed); - this.invalidKeys.addAll(requested); - boolean valid = missingMandatoryKeys.isEmpty() && invalidKeys.isEmpty() && missingMandatoryOrKeys.isEmpty(); - if (!valid) { - this.errorType = ErrorType.INVALID_CONFIGURATION; - } - - // check types - try { - if (!checkDatatypes()) { - this.errorType = ErrorType.WRONG_DATATYPE; - return false; - } - } catch (Exception e) { - this.errorType = ErrorType.BODY_NOT_PARSEABLE; - return false; - } - - return valid; - } - - private boolean checkDatatypes() throws Exception { - String contentAsJson = XContentHelper.convertToJson(content, false, XContentType.YAML); - try(JsonParser parser = factory.createParser(contentAsJson)) { - JsonToken token = null; - while ((token = parser.nextToken()) != null) { - if(token.equals(JsonToken.FIELD_NAME)) { - String currentName = parser.getCurrentName(); - DataType dataType = allowedKeys.get(currentName); - if(dataType != null) { - JsonToken valueToken = parser.nextToken(); - switch (dataType) { - case STRING: - if(!valueToken.equals(JsonToken.VALUE_STRING)) { - wrongDatatypes.put(currentName, "String expected"); - } - break; - case ARRAY: - if(!valueToken.equals(JsonToken.START_ARRAY) && !valueToken.equals(JsonToken.END_ARRAY)) { - wrongDatatypes.put(currentName, "Array expected"); - } - break; - case OBJECT: - if(!valueToken.equals(JsonToken.START_OBJECT) && !valueToken.equals(JsonToken.END_OBJECT)) { - wrongDatatypes.put(currentName, "Object expected"); - } - break; - } - } - } - } - return wrongDatatypes.isEmpty(); - } - } - - public XContentBuilder errorsAsXContent() { - try { - final XContentBuilder builder = XContentFactory.jsonBuilder(); - builder.startObject(); - switch (this.errorType) { - case NONE: - builder.field("status", "error"); - builder.field("reason", errorType.getMessage()); - break; - case INVALID_CONFIGURATION: - builder.field("status", "error"); - builder.field("reason", ErrorType.INVALID_CONFIGURATION.getMessage()); - addErrorMessage(builder, INVALID_KEYS_KEY, invalidKeys); - addErrorMessage(builder, MISSING_MANDATORY_KEYS_KEY, missingMandatoryKeys); - addErrorMessage(builder, MISSING_MANDATORY_OR_KEYS_KEY, missingMandatoryKeys); - break; - case INVALID_PASSWORD: - builder.field("status", "error"); - builder.field("reason", esSettings.get(ConfigConstants.OPENDISTRO_SECURITY_RESTAPI_PASSWORD_VALIDATION_ERROR_MESSAGE,"Password does not match minimum criterias")); - break; - case WRONG_DATATYPE: - builder.field("status", "error"); - builder.field("reason", ErrorType.WRONG_DATATYPE.getMessage()); - for (Entry entry : wrongDatatypes.entrySet()) { - builder.field( entry.getKey(), entry.getValue()); - } - break; - default: - builder.field("status", "error"); - builder.field("reason", errorType.getMessage()); - } - builder.endObject(); - return builder; - } catch (IOException ex) { - log.error("Cannot build error settings", ex); - return null; - } - } - - public Settings.Builder settingsBuilder() { - return settingsBuilder; - } - - private void addErrorMessage(final XContentBuilder builder, final String message, final Set keys) - throws IOException { - if (!keys.isEmpty()) { - builder.startObject(message); - builder.field("keys", Joiner.on(",").join(keys.toArray(new String[0]))); - builder.endObject(); - } - } - - private Settings.Builder toSettingsBuilder(final BytesReference ref) { - if (ref == null || ref.length() == 0) { - return Settings.builder(); - } - - try { - return Settings.builder().loadFromSource(ref.utf8ToString(), XContentType.JSON); - } catch (final Exception e) { - throw ExceptionsHelper.convertToElastic(e); - } - } - - public static enum DataType { - STRING, - ARRAY, - OBJECT; - } - - public static enum ErrorType { - NONE("ok"), - INVALID_CONFIGURATION("Invalid configuration"), - INVALID_PASSWORD("Invalid password"), - WRONG_DATATYPE("Wrong datatype"), - BODY_NOT_PARSEABLE("Could not parse content of request."), - PAYLOAD_NOT_ALLOWED("Request body not allowed for this action."), - PAYLOAD_MANDATORY("Request body required for this action."), - OPENDISTRO_SECURITY_NOT_INITIALIZED("Open Distro Security index not initialized."); - - private String message; - - private ErrorType(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - } - - protected final boolean hasParams() { - return param != null && param.length>0; - } -} + protected final Set invalidKeys = new HashSet<>(); + + protected final Set missingMandatoryOrKeys = new HashSet<>(); + + /** The error type */ + protected ErrorType errorType = ErrorType.NONE; + + /** Behaviour regarding payload */ + protected boolean payloadMandatory = false; + + protected boolean payloadAllowed = true; + + private Settings.Builder settingsBuilder; + + protected final Method method; + + protected final BytesReference content; + + protected final Settings esSettings; + + protected final RestRequest request; + + protected final Object[] param; + + public AbstractConfigurationValidator(final RestRequest request, final BytesReference ref, final Settings esSettings, Object... param) { + this.content = ref; + this.method = request.method(); + this.esSettings = esSettings; + this.request = request; + this.param = param; + } + + /** + * + * @return false if validation fails + */ + public boolean validateSettings() { + // no payload for DELETE and GET requests + if (method.equals(Method.DELETE) || method.equals(Method.GET)) { + return true; + } + // try to parse payload + try { + this.settingsBuilder = toSettingsBuilder(content); + } catch (ElasticsearchException e) { + this.errorType = ErrorType.BODY_NOT_PARSEABLE; + return false; + } + + Settings settings = settingsBuilder.build(); + + Set requested = new HashSet(settings.names()); + // check if payload is accepted at all + if (!this.payloadAllowed && !requested.isEmpty()) { + this.errorType = ErrorType.PAYLOAD_NOT_ALLOWED; + return false; + } + // check if payload is mandatory + if (this.payloadMandatory && requested.isEmpty()) { + this.errorType = ErrorType.PAYLOAD_MANDATORY; + return false; + } + + // mandatory settings, one of ... + if (Collections.disjoint(requested, mandatoryOrKeys)) { + this.missingMandatoryOrKeys.addAll(mandatoryOrKeys); + } + + // mandatory settings + Set mandatory = new HashSet<>(mandatoryKeys); + mandatory.removeAll(requested); + missingMandatoryKeys.addAll(mandatory); + + // invalid settings + Set allowed = new HashSet<>(allowedKeys.keySet()); + requested.removeAll(allowed); + this.invalidKeys.addAll(requested); + boolean valid = missingMandatoryKeys.isEmpty() && invalidKeys.isEmpty() && missingMandatoryOrKeys.isEmpty(); + if (!valid) { + this.errorType = ErrorType.INVALID_CONFIGURATION; + } + + // check types + try { + if (!checkDatatypes()) { + this.errorType = ErrorType.WRONG_DATATYPE; + return false; + } + } catch (Exception e) { + this.errorType = ErrorType.BODY_NOT_PARSEABLE; + return false; + } + + return valid; + } + + private boolean checkDatatypes() throws Exception { + String contentAsJson = XContentHelper.convertToJson(content, false, XContentType.JSON); + try (JsonParser parser = factory.createParser(contentAsJson)) { + JsonToken token = null; + while ((token = parser.nextToken()) != null) { + if (token.equals(JsonToken.FIELD_NAME)) { + String currentName = parser.getCurrentName(); + DataType dataType = allowedKeys.get(currentName); + if (dataType != null) { + JsonToken valueToken = parser.nextToken(); + switch (dataType) { + case STRING: + if (!valueToken.equals(JsonToken.VALUE_STRING)) { + wrongDatatypes.put(currentName, "String expected"); + } + break; + case ARRAY: + if (!valueToken.equals(JsonToken.START_ARRAY) && !valueToken.equals(JsonToken.END_ARRAY)) { + wrongDatatypes.put(currentName, "Array expected"); + } + break; + case OBJECT: + if (!valueToken.equals(JsonToken.START_OBJECT) && !valueToken.equals(JsonToken.END_OBJECT)) { + wrongDatatypes.put(currentName, "Object expected"); + } + break; + } + } + } + } + return wrongDatatypes.isEmpty(); + } + } + + public XContentBuilder errorsAsXContent(RestChannel channel) { + try { + final XContentBuilder builder = channel.newBuilder(); + builder.startObject(); + switch (this.errorType) { + case NONE: + builder.field("status", "error"); + builder.field("reason", errorType.getMessage()); + break; + case INVALID_CONFIGURATION: + builder.field("status", "error"); + builder.field("reason", ErrorType.INVALID_CONFIGURATION.getMessage()); + addErrorMessage(builder, INVALID_KEYS_KEY, invalidKeys); + addErrorMessage(builder, MISSING_MANDATORY_KEYS_KEY, missingMandatoryKeys); + addErrorMessage(builder, MISSING_MANDATORY_OR_KEYS_KEY, missingMandatoryKeys); + break; + case INVALID_PASSWORD: + builder.field("status", "error"); + builder.field("reason", esSettings.get(ConfigConstants.OPENDISTRO_SECURITY_RESTAPI_PASSWORD_VALIDATION_ERROR_MESSAGE, + "Password does not match minimum criterias")); + break; + case WRONG_DATATYPE: + builder.field("status", "error"); + builder.field("reason", ErrorType.WRONG_DATATYPE.getMessage()); + for (Entry entry : wrongDatatypes.entrySet()) { + builder.field(entry.getKey(), entry.getValue()); + } + break; + default: + builder.field("status", "error"); + builder.field("reason", errorType.getMessage()); + } + builder.endObject(); + return builder; + } catch (IOException ex) { + log.error("Cannot build error settings", ex); + return null; + } + } + + public Settings.Builder settingsBuilder() { + return settingsBuilder; + } + + private void addErrorMessage(final XContentBuilder builder, final String message, final Set keys) throws IOException { + if (!keys.isEmpty()) { + builder.startObject(message); + builder.field("keys", Joiner.on(",").join(keys.toArray(new String[0]))); + builder.endObject(); + } + } + + private Settings.Builder toSettingsBuilder(final BytesReference ref) { + if (ref == null || ref.length() == 0) { + return Settings.builder(); + } + + try { + return Settings.builder().loadFromSource(ref.utf8ToString(), XContentType.JSON); + } catch (final Exception e) { + throw ExceptionsHelper.convertToElastic(e); + } + } + + public static enum DataType { + STRING, ARRAY, OBJECT; + } + + public static enum ErrorType { + NONE("ok"), INVALID_CONFIGURATION("Invalid configuration"), INVALID_PASSWORD("Invalid password"), WRONG_DATATYPE("Wrong datatype"), + BODY_NOT_PARSEABLE("Could not parse content of request."), PAYLOAD_NOT_ALLOWED("Request body not allowed for this action."), + PAYLOAD_MANDATORY("Request body required for this action."), OPENDISTRO_SECURITY_NOT_INITIALIZED("Open Distro Security index not initialized."); + + private String message; + + private ErrorType(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + } + + protected final boolean hasParams() { + return param != null && param.length > 0; + } +} \ No newline at end of file diff --git a/src/test/java/com/amazon/dlic/auth/ldap/LdapBackendTest.java b/src/test/java/com/amazon/dlic/auth/ldap/LdapBackendTest.java index 7c9b9c1..283bbfe 100755 --- a/src/test/java/com/amazon/dlic/auth/ldap/LdapBackendTest.java +++ b/src/test/java/com/amazon/dlic/auth/ldap/LdapBackendTest.java @@ -21,13 +21,13 @@ import java.util.TreeSet; import org.elasticsearch.ElasticsearchSecurityException; -import org.elasticsearch.common.settings.SecureSettings; import org.elasticsearch.common.settings.Settings; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; import org.ldaptive.Connection; +import org.ldaptive.LdapAttribute; import org.ldaptive.LdapEntry; import com.amazon.dlic.auth.ldap.LdapUser; @@ -464,6 +464,30 @@ public void testLdapAuthorizationOnly() throws Exception { Assert.assertEquals("ceo", new ArrayList(new TreeSet(user.getRoles())).get(0)); } + + @Test + public void testLdapAuthorizationNonDNEntry() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "description") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .build(); + + final User user = new User("jacksonm"); + + new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); + + Assert.assertNotNull(user); + Assert.assertEquals("jacksonm", user.getName()); + Assert.assertEquals(2, user.getRoles().size()); + Assert.assertEquals("ceo-ceo", new ArrayList(new TreeSet(user.getRoles())).get(0)); + } + + @Test public void testLdapAuthorizationNested() throws Exception { @@ -847,6 +871,129 @@ public void testLdapAuthorizationNonDNRoles() throws Exception { Assert.assertTrue("Roles do not contain non-LDAP role 'anotherrole' from second role name", user.getRoles().contains("anotherrole")); } + + @Test + public void testLdapSpecial186() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "description") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true) + .build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("spec186", "spec186" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + Assert.assertEquals("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST", user.getName()); + Assert.assertEquals("AA BB/CC (DD) my, company end=with=whitespace ", user.getUserEntry().getAttribute("cn").getStringValue()); + new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); + + Assert.assertEquals(3, user.getRoles().size()); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("spec186"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB\\/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + } + + @Test + public void testLdapSpecial186_2() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "dn") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true) + .build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("spec186", "spec186" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + Assert.assertEquals("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST", user.getName()); + Assert.assertEquals("AA BB/CC (DD) my, company end=with=whitespace ", user.getUserEntry().getAttribute("cn").getStringValue()); + new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); + + Assert.assertEquals(3, user.getRoles().size()); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("spec186"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB\\/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + } + + @Test + public void testOperationalAttributes() throws Exception { + + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("jacksonm", "secret" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + LdapAttribute operationAttribute = user.getUserEntry().getAttribute("entryUUID"); + Assert.assertNotNull(operationAttribute); + Assert.assertNotNull(operationAttribute.getStringValue()); + Assert.assertTrue(operationAttribute.getStringValue().length() > 10); + Assert.assertTrue(operationAttribute.getStringValue().split("-").length == 5); + } + + @Test + public void testMultiCn() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "cn") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true) + .build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("multi", "multi" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + Assert.assertEquals("cn=cabc,ou=people,o=TEST", user.getName()); + System.out.println(user.getUserEntry().getAttribute("cn")); + } + + @AfterClass public static void tearDown() throws Exception { diff --git a/src/test/java/com/amazon/dlic/auth/ldap/UtilsTest.java b/src/test/java/com/amazon/dlic/auth/ldap/UtilsTest.java index 78fd556..259aac5 100644 --- a/src/test/java/com/amazon/dlic/auth/ldap/UtilsTest.java +++ b/src/test/java/com/amazon/dlic/auth/ldap/UtilsTest.java @@ -25,21 +25,6 @@ public class UtilsTest { - @Test - public void testRfc2254StringEscape() throws Exception { - Assert.assertEquals("", Utils.escapeStringRfc2254("")); - Assert.assertEquals("abc", Utils.escapeStringRfc2254("abc")); - Assert.assertEquals("abc<>;./", Utils.escapeStringRfc2254("abc<>;./")); - Assert.assertEquals("abc\\2a", Utils.escapeStringRfc2254("abc*")); - Assert.assertEquals("\\2a", Utils.escapeStringRfc2254("*")); - Assert.assertEquals("\\2a\\2a", Utils.escapeStringRfc2254("**")); - Assert.assertEquals("\\28\\2a\\29", Utils.escapeStringRfc2254("(*)")); - Assert.assertEquals("\\28\\2a\\29", Utils.escapeStringRfc2254("(*)")); - Assert.assertEquals("\\5c\\28\\2a\\29", Utils.escapeStringRfc2254("\\(*)")); - Assert.assertEquals("\\5c\\28\\2a\\29\\00", Utils.escapeStringRfc2254("\\(*)\0")); - Assert.assertEquals("\\5c\\28abc\\2adef\\29\\00", Utils.escapeStringRfc2254("\\(abc*def)\0")); - Assert.assertNotEquals("\\5c\\28abc\\2adef\\29\\00", Utils.escapeStringRfc2254(Utils.escapeStringRfc2254("\\(abc*def)\0"))); - } @Test public void testLDAPName() throws Exception { diff --git a/src/test/java/com/amazon/dlic/auth/ldap/srv/LdapServer.java b/src/test/java/com/amazon/dlic/auth/ldap/srv/LdapServer.java index 37cf457..9cf3960 100644 --- a/src/test/java/com/amazon/dlic/auth/ldap/srv/LdapServer.java +++ b/src/test/java/com/amazon/dlic/auth/ldap/srv/LdapServer.java @@ -182,8 +182,8 @@ private synchronized int configureAndStartServer(String... ldifFiles) throws Exc config.setEnforceAttributeSyntaxCompliance(false); config.setEnforceSingleStructuralObjectClass(false); - config.setLDAPDebugLogHandler(DEBUG_HANDLER); - config.setAccessLogHandler(DEBUG_HANDLER); + //config.setLDAPDebugLogHandler(DEBUG_HANDLER); + //config.setAccessLogHandler(DEBUG_HANDLER); //config.addAdditionalBindCredentials(configuration.getBindDn(), configuration.getPassword()); server = new InMemoryDirectoryServer(config); @@ -248,7 +248,7 @@ private static class DebugHandler extends Handler { @Override public void publish(LogRecord logRecord) { - LOG.debug(ToStringBuilder.reflectionToString(logRecord, ToStringStyle.MULTI_LINE_STYLE)); + //LOG.debug(ToStringBuilder.reflectionToString(logRecord, ToStringStyle.MULTI_LINE_STYLE)); } @Override diff --git a/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestClientCert2.java b/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestClientCert2.java index 1038e1d..01ef6f4 100644 --- a/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestClientCert2.java +++ b/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestClientCert2.java @@ -27,7 +27,6 @@ import org.junit.Test; import com.amazon.dlic.auth.ldap.LdapUser; -import com.amazon.dlic.auth.ldap.backend.LDAPAuthenticationBackend; import com.amazon.dlic.auth.ldap.util.ConfigConstants; import com.amazon.opendistroforelasticsearch.security.ssl.util.ExceptionUtils; import com.amazon.opendistroforelasticsearch.security.user.AuthCredentials; @@ -57,7 +56,7 @@ public void testNoAuth() throws Exception { @SuppressWarnings("unused") LdapUser user; try { - user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.fail(); @@ -85,7 +84,7 @@ public void testNoAuthX() throws Exception { @SuppressWarnings("unused") LdapUser user; try { - user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.fail(); @@ -113,7 +112,7 @@ public void testNoAuthY() throws Exception { @SuppressWarnings("unused") LdapUser user; try { - user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.fail(); @@ -142,7 +141,7 @@ public void testBindDnAuthLocalhost() throws Exception { .put("path.home",".") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); @@ -165,7 +164,7 @@ public void testLdapSslAuth() throws Exception { .put("path.home",".") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); @@ -191,7 +190,7 @@ public void testLdapSslAuthPem() throws Exception { // .put(ConfigConstants.LDAP_PASSWORD, "ldapbinder") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); @@ -214,7 +213,7 @@ public void testLdapSslAuthNo() throws Exception { .put("path.home",".") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); @@ -246,7 +245,7 @@ public void testLdapAuthenticationSSL() throws Exception { .put("path.home",".") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("ldap_hr_employee" , "ldap_hr_employee" .getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); diff --git a/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestNewStyleConfig2.java b/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestNewStyleConfig2.java index cb22b21..8570177 100644 --- a/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestNewStyleConfig2.java +++ b/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestNewStyleConfig2.java @@ -33,13 +33,15 @@ import org.junit.runners.Parameterized.Parameters; import org.ldaptive.Connection; import org.ldaptive.LdapEntry; +import org.ldaptive.LdapAttribute; + import com.amazon.dlic.auth.ldap.LdapUser; import com.amazon.dlic.auth.ldap.srv.EmbeddedLDAPServer; import com.amazon.dlic.auth.ldap.util.ConfigConstants; import com.amazon.dlic.auth.ldap.util.LdapHelper; -import com.amazon.dlic.auth.ldap2.LDAPAuthenticationBackend; -import com.amazon.dlic.auth.ldap2.LDAPAuthorizationBackend; +import com.amazon.dlic.auth.ldap.backend.LDAPAuthenticationBackend; +import com.amazon.dlic.auth.ldap.backend.LDAPAuthorizationBackend; import com.amazon.dlic.auth.ldap2.LDAPConnectionFactoryFactory; import com.amazon.opendistroforelasticsearch.security.support.WildcardMatcher; import com.amazon.opendistroforelasticsearch.security.test.helper.file.FileHelper; @@ -90,7 +92,7 @@ public void testLdapAuthentication() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -103,7 +105,7 @@ public void testLdapAuthenticationFakeLogin() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").put(ConfigConstants.LDAP_FAKE_LOGIN_ENABLED, true).build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("unknown", "unknown".getBytes(StandardCharsets.UTF_8))); } @@ -117,7 +119,7 @@ public void testLdapInjection() throws Exception { String injectString = "*jack*"; @SuppressWarnings("unused") - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials(injectString, "secret".getBytes(StandardCharsets.UTF_8))); } @@ -130,7 +132,7 @@ public void testLdapAuthenticationBindDn() throws Exception { .put(ConfigConstants.LDAP_BIND_DN, "cn=Captain Spock,ou=people,o=TEST") .put(ConfigConstants.LDAP_PASSWORD, "spocksecret").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -146,7 +148,7 @@ public void testLdapAuthenticationWrongBindDn() throws Exception { .put(ConfigConstants.LDAP_BIND_DN, "cn=Captain Spock,ou=people,o=TEST") .put(ConfigConstants.LDAP_PASSWORD, "wrong").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected exception"); } catch (Exception e) { @@ -161,7 +163,7 @@ public void testLdapAuthenticationBindFail() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "wrong".getBytes(StandardCharsets.UTF_8))); } @@ -172,7 +174,7 @@ public void testLdapAuthenticationNoUser() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("UNKNOWN", "UNKNOWN".getBytes(StandardCharsets.UTF_8))); } @@ -183,7 +185,7 @@ public void testLdapAuthenticationFail() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "xxxxx".getBytes(StandardCharsets.UTF_8))); } @@ -197,7 +199,7 @@ public void testLdapAuthenticationSSL() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/truststore.jks")) .put("verify_hostnames", false).put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -213,7 +215,7 @@ public void testLdapAuthenticationSSLPEMFile() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/root-ca.pem").toFile().getName()) .put("verify_hostnames", false).put("path.home", ".") .put("path.conf", FileHelper.getAbsoluteFilePathFromClassPath("ldap/root-ca.pem").getParent()).build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, Paths.get("src/test/resources/ldap")) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, Paths.get("src/test/resources/ldap")) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -232,7 +234,7 @@ public void testLdapAuthenticationSSLPEMText() throws Exception { .getAbsolutePath())) .build(); Settings settings = Settings.builder().put(settingsFromFile).putList("hosts", "localhost:"+ldapsPort).build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -249,7 +251,7 @@ public void testLdapAuthenticationSSLSSLv3() throws Exception { .put("verify_hostnames", false).putList("enabled_ssl_protocols", "SSLv3").put("path.home", ".").build(); try { - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected Exception"); } catch (Exception e) { @@ -270,7 +272,7 @@ public void testLdapAuthenticationSSLUnknownCipher() throws Exception { .put("verify_hostnames", false).putList("enabled_ssl_ciphers", "AAA").put("path.home", ".").build(); try { - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected Exception"); } catch (Exception e) { @@ -291,7 +293,7 @@ public void testLdapAuthenticationSpecialCipherProtocol() throws Exception { .put("verify_hostnames", false).putList("enabled_ssl_protocols", "TLSv1") .putList("enabled_ssl_ciphers", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA").put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -308,7 +310,7 @@ public void testLdapAuthenticationSSLNoKeystore() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/truststore.jks")) .put("verify_hostnames", false).put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -322,7 +324,7 @@ public void testLdapAuthenticationSSLFailPlain() throws Exception { .put("users.u1.search", "(uid={0})").put(ConfigConstants.LDAPS_ENABLE_SSL, true).build(); try { - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected exception"); } catch (final Exception e) { @@ -337,7 +339,7 @@ public void testLdapExists() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").build(); - final LDAPAuthenticationBackend lbe = new LDAPAuthenticationBackend(settings, null); + final LDAPAuthenticationBackend2 lbe = new LDAPAuthenticationBackend2(settings, null); Assert.assertTrue(lbe.exists(new User("jacksonm"))); Assert.assertFalse(lbe.exists(new User("doesnotexist"))); } @@ -354,7 +356,7 @@ public void testLdapAuthorization() throws Exception { // "(uniqueMember={0})") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -396,7 +398,7 @@ public void testLdapEscape() throws Exception { .put(ConfigConstants.LDAP_AUTHZ_USERROLENAME, "description") // no memberOf OID .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true).build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("ssign", "ssignsecret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Special\\, Sign,ou=people,o=TEST", user.getName()); @@ -415,7 +417,7 @@ public void testLdapAuthorizationRoleSearchUsername() throws Exception { .put("roles.g1.base", "ou=groups,o=TEST").put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "cn") .put("roles.g1.search", "(uniqueMember=cn={1},ou=people,o=TEST)").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("Michael Jackson", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -519,7 +521,7 @@ public void testLdapAuthorizationDn() throws Exception { .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, false) .put("roles.g1.search", "(uniqueMember={0})").build(); - final User user = new LDAPAuthenticationBackend(settings, null) + final User user = new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes())); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -538,7 +540,7 @@ public void testLdapAuthenticationUserNameAttribute() throws Exception { .put("users.u1.base", "ou=people,o=TEST").put("users.u1.search", "(uid={0})") .put(ConfigConstants.LDAP_AUTHC_USERNAME_ATTRIBUTE, "uid").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("jacksonm", user.getName()); @@ -554,7 +556,7 @@ public void testLdapAuthenticationStartTLS() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/truststore.jks")) .put("verify_hostnames", false).put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -570,7 +572,7 @@ public void testLdapAuthorizationSkipUsers() throws Exception { .put("roles.g1.search", "(uniqueMember={0})") .putList(ConfigConstants.LDAP_AUTHZ_SKIP_USERS, "cn=Michael Jackson,ou*people,o=TEST").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -702,7 +704,7 @@ public void testCustomAttributes() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").build(); - LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -714,7 +716,7 @@ public void testCustomAttributes() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put("users.u1.search", "(uid={0})").put(ConfigConstants.LDAP_CUSTOM_ATTR_MAXVAL_LEN, 0).build(); - user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertEquals(user.getCustomAttributesMap().toString(), 2, user.getCustomAttributesMap().size()); @@ -724,7 +726,7 @@ public void testCustomAttributes() throws Exception { .put("users.u1.search", "(uid={0})") .putList(ConfigConstants.LDAP_CUSTOM_ATTR_WHITELIST, "*objectclass*", "entryParentId").build(); - user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertEquals(user.getCustomAttributesMap().toString(), 2, user.getCustomAttributesMap().size()); @@ -766,7 +768,7 @@ public void testChainedLdapAuthentication1() throws Exception { .put("users.u1.search", "(uid={0})").put("users.u1.base", "ou=people,o=TEST") .put("users.u2.search", "(uid={0})").put("users.u2.base", "ou=people2,o=TEST").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -780,7 +782,7 @@ public void testChainedLdapAuthentication2() throws Exception { .put("users.u1.search", "(uid={0})").put("users.u1.base", "ou=people,o=TEST") .put("users.u2.search", "(uid={0})").put("users.u2.base", "ou=people2,o=TEST").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("presleye", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Elvis Presley,ou=people2,o=TEST", user.getName()); @@ -795,7 +797,7 @@ public void testChainedLdapAuthenticationDuplicate() throws Exception { .put("users.u1.base", "ou=people,o=TEST").put("users.u2.search", "(uid={0})") .put("users.u2.base", "ou=people2,o=TEST").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); // Fails with ElasticsearchSecurityException because two possible instances are @@ -810,7 +812,7 @@ public void testChainedLdapExists() throws Exception { .put("users.u1.search", "(uid={0})").put("users.u2.search", "(uid={0})") .put("users.u2.base", "ou=people2,o=TEST").build(); - final LDAPAuthenticationBackend lbe = new LDAPAuthenticationBackend(settings, null); + final LDAPAuthenticationBackend2 lbe = new LDAPAuthenticationBackend2(settings, null); Assert.assertTrue(lbe.exists(new User("jacksonm"))); Assert.assertTrue(lbe.exists(new User("presleye"))); Assert.assertFalse(lbe.exists(new User("doesnotexist"))); @@ -826,7 +828,7 @@ public void testChainedLdapAuthorization() throws Exception { .put("roles.g1.search", "(uniqueMember={0})").put("roles.g2.base", "ou=groups2,o=TEST") .put("roles.g2.search", "(uniqueMember={0})").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -852,7 +854,7 @@ public void testCrossChainedLdapAuthorization() throws Exception { .put("roles.g1.search", "(uniqueMember={0})").put("roles.g2.base", "ou=groups2,o=TEST") .put("roles.g2.search", "(uniqueMember={0})").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("mercuryf", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -865,6 +867,129 @@ public void testCrossChainedLdapAuthorization() throws Exception { // The user is NOT in crossnested1! } + @Test + public void testLdapAuthorizationNonDNEntry() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "description") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .build(); + + final User user = new User("jacksonm"); + + new LDAPAuthorizationBackend2(settings, null).fillRoles(user, null); + + Assert.assertNotNull(user); + Assert.assertEquals("jacksonm", user.getName()); + Assert.assertEquals(2, user.getRoles().size()); + Assert.assertEquals("ceo-ceo", new ArrayList(new TreeSet(user.getRoles())).get(0)); + } + + @Test + public void testLdapSpecial186() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "description") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true) + .build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("spec186", "spec186" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + Assert.assertEquals("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST", user.getName()); + Assert.assertEquals("AA BB/CC (DD) my, company end=with=whitespace ", user.getUserEntry().getAttribute("cn").getStringValue()); + new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); + + Assert.assertEquals(3, user.getRoles().size()); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("spec186"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB\\/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + } + + @Test + public void testLdapSpecial186_2() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "dn") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true) + .build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("spec186", "spec186" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + Assert.assertEquals("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST", user.getName()); + Assert.assertEquals("AA BB/CC (DD) my, company end=with=whitespace ", user.getUserEntry().getAttribute("cn").getStringValue()); + new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); + + Assert.assertEquals(3, user.getRoles().size()); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("spec186"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB\\/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + } + + @Test + public void testOperationalAttributes() throws Exception { + + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("jacksonm", "secret" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + LdapAttribute operationAttribute = user.getUserEntry().getAttribute("entryUUID"); + Assert.assertNotNull(operationAttribute); + Assert.assertNotNull(operationAttribute.getStringValue()); + Assert.assertTrue(operationAttribute.getStringValue().length() > 10); + Assert.assertTrue(operationAttribute.getStringValue().split("-").length == 5); + } + @AfterClass public static void tearDown() throws Exception { diff --git a/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestOldStyleConfig2.java b/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestOldStyleConfig2.java index 5158c13..c94a4fc 100755 --- a/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestOldStyleConfig2.java +++ b/src/test/java/com/amazon/dlic/auth/ldap2/LdapBackendTestOldStyleConfig2.java @@ -35,13 +35,14 @@ import org.junit.runners.Parameterized.Parameters; import org.ldaptive.Connection; import org.ldaptive.LdapEntry; +import org.ldaptive.LdapAttribute; import com.amazon.dlic.auth.ldap.LdapUser; import com.amazon.dlic.auth.ldap.srv.EmbeddedLDAPServer; import com.amazon.dlic.auth.ldap.util.ConfigConstants; import com.amazon.dlic.auth.ldap.util.LdapHelper; -import com.amazon.dlic.auth.ldap2.LDAPAuthenticationBackend; -import com.amazon.dlic.auth.ldap2.LDAPAuthorizationBackend; +import com.amazon.dlic.auth.ldap.backend.LDAPAuthenticationBackend; +import com.amazon.dlic.auth.ldap.backend.LDAPAuthorizationBackend; import com.amazon.dlic.auth.ldap2.LDAPConnectionFactoryFactory; import com.amazon.opendistroforelasticsearch.security.support.WildcardMatcher; import com.amazon.opendistroforelasticsearch.security.test.helper.file.FileHelper; @@ -92,7 +93,7 @@ public void testLdapAuthentication() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -106,7 +107,7 @@ public void testLdapAuthenticationPooled() throws Exception { .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").put(ConfigConstants.LDAP_POOL_ENABLED, true) .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -120,7 +121,7 @@ public void testLdapAuthenticationFakeLogin() throws Exception { .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") .put(ConfigConstants.LDAP_FAKE_LOGIN_ENABLED, true).build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("unknown", "unknown".getBytes(StandardCharsets.UTF_8))); } @@ -134,7 +135,7 @@ public void testLdapInjection() throws Exception { String injectString = "*jack*"; @SuppressWarnings("unused") - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials(injectString, "secret".getBytes(StandardCharsets.UTF_8))); } @@ -148,7 +149,7 @@ public void testLdapAuthenticationBindDn() throws Exception { .put(ConfigConstants.LDAP_BIND_DN, "cn=Captain Spock,ou=people,o=TEST") .put(ConfigConstants.LDAP_PASSWORD, "spocksecret").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -164,7 +165,7 @@ public void testLdapAuthenticationWrongBindDn() throws Exception { .put(ConfigConstants.LDAP_BIND_DN, "cn=Captain Spock,ou=people,o=TEST") .put(ConfigConstants.LDAP_PASSWORD, "wrong").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected exception"); } catch (Exception e) { @@ -179,7 +180,7 @@ public void testLdapAuthenticationBindFail() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "wrong".getBytes(StandardCharsets.UTF_8))); } @@ -190,7 +191,7 @@ public void testLdapAuthenticationNoUser() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("UNKNOWN", "UNKNOWN".getBytes(StandardCharsets.UTF_8))); } @@ -201,7 +202,7 @@ public void testLdapAuthenticationFail() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "xxxxx".getBytes(StandardCharsets.UTF_8))); } @@ -213,7 +214,7 @@ public void testLdapAuthenticationFailPooled() throws Exception { .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").put(ConfigConstants.LDAP_POOL_ENABLED, true) .build(); - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "xxxxx".getBytes(StandardCharsets.UTF_8))); } @@ -227,7 +228,7 @@ public void testLdapAuthenticationSSL() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/truststore.jks")) .put("verify_hostnames", false).put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -244,7 +245,7 @@ public void testLdapAuthenticationSSLPooled() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/truststore.jks")) .put("verify_hostnames", false).put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -260,7 +261,7 @@ public void testLdapAuthenticationSSLPEMFile() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/root-ca.pem").toFile().getName()) .put("verify_hostnames", false).put("path.home", ".") .put("path.conf", FileHelper.getAbsoluteFilePathFromClassPath("ldap/root-ca.pem").getParent()).build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, Paths.get("src/test/resources/ldap")) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, Paths.get("src/test/resources/ldap")) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -279,7 +280,7 @@ public void testLdapAuthenticationSSLPEMText() throws Exception { .getAbsolutePath())) .build(); Settings settings = Settings.builder().put(settingsFromFile).putList("hosts", "localhost:"+ldapsPort).build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -296,7 +297,7 @@ public void testLdapAuthenticationSSLSSLv3() throws Exception { .put("verify_hostnames", false).putList("enabled_ssl_protocols", "SSLv3").put("path.home", ".").build(); try { - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected Exception"); } catch (Exception e) { @@ -317,7 +318,7 @@ public void testLdapAuthenticationSSLUnknowCipher() throws Exception { .put("verify_hostnames", false).putList("enabled_ssl_ciphers", "AAA").put("path.home", ".").build(); try { - new LDAPAuthenticationBackend(settings, null) + new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected Exception"); } catch (Exception e) { @@ -338,7 +339,7 @@ public void testLdapAuthenticationSpecialCipherProtocol() throws Exception { .put("verify_hostnames", false).putList("enabled_ssl_protocols", "TLSv1") .putList("enabled_ssl_ciphers", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA").put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -355,7 +356,7 @@ public void testLdapAuthenticationSSLNoKeystore() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/truststore.jks")) .put("verify_hostnames", false).put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -370,7 +371,7 @@ public void testLdapAuthenticationSSLFailPlain() throws Exception { .build(); try { - new LDAPAuthenticationBackend(settings, new File("").toPath()) + new LDAPAuthenticationBackend2(settings, new File("").toPath()) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.fail("Expected exception"); } catch (final Exception e) { @@ -385,7 +386,7 @@ public void testLdapExists() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); - final LDAPAuthenticationBackend lbe = new LDAPAuthenticationBackend(settings, null); + final LDAPAuthenticationBackend2 lbe = new LDAPAuthenticationBackend2(settings, null); Assert.assertTrue(lbe.exists(new User("jacksonm"))); Assert.assertFalse(lbe.exists(new User("doesnotexist"))); } @@ -404,7 +405,7 @@ public void testLdapAuthorization() throws Exception { // "(uniqueMember={0})") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -431,7 +432,7 @@ public void testLdapAuthorizationPooled() throws Exception { // "(uniqueMember={0})") .build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -475,7 +476,7 @@ public void testLdapEscape() throws Exception { .put(ConfigConstants.LDAP_AUTHZ_USERROLENAME, "description") // no memberOf OID .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true).build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("ssign", "ssignsecret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Special\\, Sign,ou=people,o=TEST", user.getName()); @@ -496,7 +497,7 @@ public void testLdapAuthorizationRoleSearchUsername() throws Exception { .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "cn") .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember=cn={1},ou=people,o=TEST)").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("Michael Jackson", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -611,7 +612,7 @@ public void testLdapAuthorizationDn() throws Exception { .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, false) .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})").build(); - final User user = new LDAPAuthenticationBackend(settings, null) + final User user = new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes())); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -631,7 +632,7 @@ public void testLdapAuthenticationUserNameAttribute() throws Exception { .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") .put(ConfigConstants.LDAP_AUTHC_USERNAME_ATTRIBUTE, "uid").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("jacksonm", user.getName()); @@ -648,7 +649,7 @@ public void testLdapAuthenticationStartTLS() throws Exception { FileHelper.getAbsoluteFilePathFromClassPath("ldap/truststore.jks")) .put("verify_hostnames", false).put("path.home", ".").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -666,7 +667,7 @@ public void testLdapAuthorizationSkipUsers() throws Exception { .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") .putList(ConfigConstants.LDAP_AUTHZ_SKIP_USERS, "cn=Michael Jackson,ou*people,o=TEST").build(); - final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); @@ -811,7 +812,7 @@ public void testCustomAttributes() throws Exception { .putList(ConfigConstants.LDAP_HOSTS, "127.0.0.1:4", "localhost:" + ldapPort) .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); - LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertNotNull(user); Assert.assertEquals("cn=Michael Jackson,ou=people,o=TEST", user.getName()); @@ -824,7 +825,7 @@ public void testCustomAttributes() throws Exception { .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") .put(ConfigConstants.LDAP_CUSTOM_ATTR_MAXVAL_LEN, 0).build(); - user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertEquals(user.getCustomAttributesMap().toString(), 2, user.getCustomAttributesMap().size()); @@ -834,7 +835,7 @@ public void testCustomAttributes() throws Exception { .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") .putList(ConfigConstants.LDAP_CUSTOM_ATTR_WHITELIST, "*objectclass*", "entryParentId").build(); - user = (LdapUser) new LDAPAuthenticationBackend(settings, null) + user = (LdapUser) new LDAPAuthenticationBackend2(settings, null) .authenticate(new AuthCredentials("jacksonm", "secret".getBytes(StandardCharsets.UTF_8))); Assert.assertEquals(user.getCustomAttributesMap().toString(), 2, user.getCustomAttributesMap().size()); @@ -871,6 +872,131 @@ public void testLdapAuthorizationNonDNRoles() throws Exception { user.getRoles().contains("anotherrole")); } + + @Test + public void testLdapAuthorizationNonDNEntry() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "description") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .build(); + + final User user = new User("jacksonm"); + + new LDAPAuthorizationBackend2(settings, null).fillRoles(user, null); + + Assert.assertNotNull(user); + Assert.assertEquals("jacksonm", user.getName()); + Assert.assertEquals(2, user.getRoles().size()); + Assert.assertEquals("ceo-ceo", new ArrayList(new TreeSet(user.getRoles())).get(0)); + } + + @Test + public void testLdapSpecial186() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "description") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true) + .build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("spec186", "spec186" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + Assert.assertEquals("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST", user.getName()); + Assert.assertEquals("AA BB/CC (DD) my, company end=with=whitespace ", user.getUserEntry().getAttribute("cn").getStringValue()); + new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); + + Assert.assertEquals(3, user.getRoles().size()); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("spec186"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB\\/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLEx(186n) consists of\\, special=")); + Assert.assertTrue(user.getRoles().toString().contains("ROLE/(186nn) consists of\\, special=")); + } + + @Test + public void testLdapSpecial186_2() throws Exception { + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})") + .put(ConfigConstants.LDAP_AUTHC_USERBASE, "ou=people,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLEBASE, "ou=groups,o=TEST") + .put(ConfigConstants.LDAP_AUTHZ_ROLENAME, "dn") + .put(ConfigConstants.LDAP_AUTHZ_ROLESEARCH, "(uniqueMember={0})") + .put(ConfigConstants.LDAP_AUTHZ_RESOLVE_NESTED_ROLES, true) + .build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend(settings, null).authenticate(new AuthCredentials("spec186", "spec186" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + Assert.assertEquals("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST", user.getName()); + Assert.assertEquals("AA BB/CC (DD) my, company end=with=whitespace ", user.getUserEntry().getAttribute("cn").getStringValue()); + new LDAPAuthorizationBackend(settings, null).fillRoles(user, null); + + Assert.assertEquals(3, user.getRoles().size()); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("spec186"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + + new LDAPAuthorizationBackend(settings, null).fillRoles(new User("CN=AA BB\\/CC (DD) my\\, company end\\=with\\=whitespace\\ ,ou=people,o=TEST"), null); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186n) consists of\\, special\\=chars\\ ")); + Assert.assertTrue(user.getRoles().toString().contains("cn=ROLE/(186nn) consists of\\, special\\=chars\\ ")); + } + + @Test + public void testOperationalAttributes() throws Exception { + + + final Settings settings = Settings.builder() + .putList(ConfigConstants.LDAP_HOSTS, "localhost:" + ldapPort) + .put(ConfigConstants.LDAP_AUTHC_USERSEARCH, "(uid={0})").build(); + + final LdapUser user = (LdapUser) new LDAPAuthenticationBackend2(settings, null).authenticate(new AuthCredentials("jacksonm", "secret" + .getBytes(StandardCharsets.UTF_8))); + Assert.assertNotNull(user); + LdapAttribute operationAttribute = user.getUserEntry().getAttribute("entryUUID"); + Assert.assertNotNull(operationAttribute); + Assert.assertNotNull(operationAttribute.getStringValue()); + Assert.assertTrue(operationAttribute.getStringValue().length() > 10); + Assert.assertTrue(operationAttribute.getStringValue().split("-").length == 5); + } + + @AfterClass public static void tearDown() throws Exception { diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java index ce46f48..a7a30c9 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java @@ -184,9 +184,7 @@ public void testInternalConfig() throws Exception { HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin")); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - System.out.println(response.getBody()); Thread.sleep(1500); - System.out.println(TestAuditlogImpl.sb.toString()); Assert.assertTrue(TestAuditlogImpl.messages.size() > 25); Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_READ")); Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_WRITE")); @@ -315,10 +313,8 @@ public void testUpdatePerf() throws Exception { for(int i=0; i<1; i++) { HttpResponse response = rh.executePostRequest("humanresources/employees/"+i+"", "{\"customer\": {\"Age\":"+i+"}}", encodeBasicHeader("admin", "admin")); Assert.assertEquals(HttpStatus.SC_CREATED, response.getStatusCode()); - System.out.println("=================="); response = rh.executePostRequest("humanresources/employees/"+i+"", "{\"customer\": {\"Age\":"+(i+2)+"}}", encodeBasicHeader("admin", "admin")); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - System.out.println("=================="); response = rh.executePostRequest("humanresources/employees/"+i+"/_update?pretty", "{\"doc\": {\"doesel\":"+(i+3)+"}}", encodeBasicHeader("admin", "admin")); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); } diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java index 36cdd77..60a0e0e 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java @@ -212,9 +212,12 @@ public void testRestInternalConfigRead() throws Exception { System.out.println("req"); HttpResponse response = rh.executeGetRequest("_opendistro/_security/api/internalusers/admin?pretty"); Thread.sleep(1500); + System.out.println(" ===== == = = = ====== == ===== ====== ===="); + System.out.println(response.getBody()); + System.out.println(" ===== == = = = ====== == ===== ====== ===="); System.out.println(TestAuditlogImpl.sb.toString()); + System.out.println(TestAuditlogImpl.messages.toString()); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); - System.out.println(response.getBody()); Assert.assertTrue(TestAuditlogImpl.messages.size()+"",TestAuditlogImpl.messages.size() == 1); Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("audit_request_effective_user")); Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_READ")); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/CachingTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/CachingTest.java new file mode 100644 index 0000000..58f330c --- /dev/null +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/CachingTest.java @@ -0,0 +1,111 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http:/www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.security.cache; + +import org.apache.http.HttpStatus; +import org.apache.http.message.BasicHeader; +import org.elasticsearch.common.settings.Settings; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.amazon.opendistroforelasticsearch.security.test.DynamicSecurityConfig; +import com.amazon.opendistroforelasticsearch.security.test.SingleClusterTest; +import com.amazon.opendistroforelasticsearch.security.test.helper.rest.RestHelper; +import com.amazon.opendistroforelasticsearch.security.test.helper.rest.RestHelper.HttpResponse; + +public class CachingTest extends SingleClusterTest{ + + @Override + protected String getResourceFolder() { + return "cache"; + } + + @Before + public void reset() { + DummyHTTPAuthenticator.reset(); + DummyAuthorizer.reset(); + DummyAuthenticationBackend.reset(); + + } + + @Test + public void testRestCaching() throws Exception { + setup(Settings.EMPTY, new DynamicSecurityConfig(), Settings.EMPTY); + final RestHelper rh = nonSslRestHelper(); + HttpResponse res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty"); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty"); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty"); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + + Assert.assertEquals(3, DummyHTTPAuthenticator.getCount()); + Assert.assertEquals(1, DummyAuthorizer.getCount()); + Assert.assertEquals(3, DummyAuthenticationBackend.getAuthCount()); + Assert.assertEquals(0, DummyAuthenticationBackend.getExistsCount()); + } + + @Test + public void testRestNoCaching() throws Exception { + final Settings settings = Settings.builder().put("opendistro_security.cache.ttl_minutes", 0).build(); + setup(Settings.EMPTY, new DynamicSecurityConfig(), settings); + final RestHelper rh = nonSslRestHelper(); + HttpResponse res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty"); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty"); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty"); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + + Assert.assertEquals(3, DummyHTTPAuthenticator.getCount()); + Assert.assertEquals(3, DummyAuthorizer.getCount()); + Assert.assertEquals(3, DummyAuthenticationBackend.getAuthCount()); + Assert.assertEquals(0, DummyAuthenticationBackend.getExistsCount()); + } + + @Test + public void testRestCachingWithImpersonation() throws Exception { + final Settings settings = Settings.builder().putList("opendistro_security.authcz.rest_impersonation_user.dummy", "*").build(); + setup(Settings.EMPTY, new DynamicSecurityConfig(), settings); + final RestHelper rh = nonSslRestHelper(); + HttpResponse res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty", new BasicHeader("opendistro_security_impersonate_as", "impuser")); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty", new BasicHeader("opendistro_security_impersonate_as", "impuser")); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty", new BasicHeader("opendistro_security_impersonate_as", "impuser")); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + res = rh.executeGetRequest("_opendistro/_security/authinfo?pretty", new BasicHeader("opendistro_security_impersonate_as", "impuser2")); + System.out.println(res.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, res.getStatusCode()); + + Assert.assertEquals(4, DummyHTTPAuthenticator.getCount()); + Assert.assertEquals(3, DummyAuthorizer.getCount()); + Assert.assertEquals(4, DummyAuthenticationBackend.getAuthCount()); + Assert.assertEquals(2, DummyAuthenticationBackend.getExistsCount()); + + } + +} diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthenticationBackend.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthenticationBackend.java new file mode 100644 index 0000000..446b398 --- /dev/null +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthenticationBackend.java @@ -0,0 +1,66 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.security.cache; + +import java.nio.file.Path; + +import org.elasticsearch.ElasticsearchSecurityException; +import org.elasticsearch.common.settings.Settings; + +import com.amazon.opendistroforelasticsearch.security.auth.AuthenticationBackend; +import com.amazon.opendistroforelasticsearch.security.auth.AuthorizationBackend; +import com.amazon.opendistroforelasticsearch.security.user.AuthCredentials; +import com.amazon.opendistroforelasticsearch.security.user.User; + + +public class DummyAuthenticationBackend implements AuthenticationBackend { + + private static volatile long authCount; + private static volatile long existsCount; + + public DummyAuthenticationBackend(final Settings settings, final Path configPath) { + } + + @Override + public String getType() { + return "dummy"; + } + + @Override + public User authenticate(AuthCredentials credentials) throws ElasticsearchSecurityException { + authCount++; + return new User(credentials.getUsername()); + } + + @Override + public boolean exists(User user) { + existsCount++; + return true; + } + + public static long getAuthCount() { + return authCount; + } + + public static long getExistsCount() { + return existsCount; + } + + public static void reset() { + authCount=0; + existsCount=0; + } +} \ No newline at end of file diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthorizer.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthorizer.java new file mode 100644 index 0000000..c6a2d12 --- /dev/null +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyAuthorizer.java @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.security.cache; + +import java.nio.file.Path; + +import org.elasticsearch.ElasticsearchSecurityException; +import org.elasticsearch.common.settings.Settings; + +import com.amazon.opendistroforelasticsearch.security.auth.AuthorizationBackend; +import com.amazon.opendistroforelasticsearch.security.user.AuthCredentials; +import com.amazon.opendistroforelasticsearch.security.user.User; + + +public class DummyAuthorizer implements AuthorizationBackend { + + private static volatile long count; + + public DummyAuthorizer(final Settings settings, final Path configPath) { + } + + @Override + public String getType() { + return "dummy"; + } + + @Override + public void fillRoles(User user, AuthCredentials credentials) throws ElasticsearchSecurityException { + count++; + user.addRole("role_" + user.getName() + "_" + System.currentTimeMillis() + "_" + count); + + } + + public static long getCount() { + return count; + } + + public static void reset() { + count=0; + } + +} diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyHTTPAuthenticator.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyHTTPAuthenticator.java new file mode 100644 index 0000000..db7f0d6 --- /dev/null +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/cache/DummyHTTPAuthenticator.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.security.cache; + +import java.nio.file.Path; + +import org.elasticsearch.ElasticsearchSecurityException; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.rest.RestChannel; +import org.elasticsearch.rest.RestRequest; + +import com.amazon.opendistroforelasticsearch.security.auth.HTTPAuthenticator; +import com.amazon.opendistroforelasticsearch.security.user.AuthCredentials; + +public class DummyHTTPAuthenticator implements HTTPAuthenticator { + + private static volatile long count; + + public DummyHTTPAuthenticator(final Settings settings, final Path configPath) { + } + + @Override + public String getType() { + return "dummy"; + } + + @Override + public AuthCredentials extractCredentials(RestRequest request, ThreadContext context) throws ElasticsearchSecurityException { + count++; + return new AuthCredentials("dummy").markComplete(); + } + + @Override + public boolean reRequestAuthentication(RestChannel channel, AuthCredentials credentials) { + return false; + } + + public static long getCount() { + return count; + } + + public static void reset() { + count=0; + } +} diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DateMathTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DateMathTest.java new file mode 100644 index 0000000..f9926d5 --- /dev/null +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DateMathTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.amazon.opendistroforelasticsearch.security.dlic.dlsfls; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +import com.amazon.opendistroforelasticsearch.security.dlic.rest.support.Utils; +import org.apache.http.HttpStatus; +import org.elasticsearch.action.index.IndexRequest; +import org.elasticsearch.action.support.WriteRequest.RefreshPolicy; +import org.elasticsearch.client.transport.TransportClient; +import org.elasticsearch.common.xcontent.XContentType; +import org.junit.Assert; +import org.junit.Test; + +import com.amazon.opendistroforelasticsearch.security.support.OpenDistroSecurityUtils; +import com.amazon.opendistroforelasticsearch.security.test.helper.file.FileHelper; +import com.amazon.opendistroforelasticsearch.security.test.helper.rest.RestHelper.HttpResponse; + +public class DateMathTest extends AbstractDlsFlsTest{ + + + protected void populate(TransportClient tc) { + + tc.index(new IndexRequest(".opendistro_security").type("security").id("config").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("config", FileHelper.readYamlContent("dlsfls/config.yml"))).actionGet(); + tc.index(new IndexRequest(".opendistro_security").type("security").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("internalusers") + .source("internalusers", FileHelper.readYamlContent("dlsfls/internal_users.yml"))).actionGet(); + tc.index(new IndexRequest(".opendistro_security").type("security").id("roles").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("roles", FileHelper.readYamlContent("dlsfls/roles.yml"))).actionGet(); + tc.index(new IndexRequest(".opendistro_security").type("security").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("rolesmapping") + .source("rolesmapping", FileHelper.readYamlContent("dlsfls/roles_mapping.yml"))).actionGet(); + tc.index(new IndexRequest(".opendistro_security").type("security").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("actiongroups") + .source("actiongroups", FileHelper.readYamlContent("dlsfls/action_groups.yml"))).actionGet(); + + SimpleDateFormat sdf = new SimpleDateFormat("YYYY.MM.dd", OpenDistroSecurityUtils.EN_Locale); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + + String date = sdf.format(new Date()); + tc.index(new IndexRequest("logstash-"+date).type("logs").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{\"message\":\"mymsg1a\", \"ipaddr\": \"10.0.0.0\",\"msgid\": \"12\"}", XContentType.JSON)).actionGet(); + + tc.index(new IndexRequest("logstash-"+date).type("logs").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{\"message\":\"mymsg1b\", \"ipaddr\": \"10.0.0.1\",\"msgid\": \"14\"}", XContentType.JSON)).actionGet(); + + tc.index(new IndexRequest("logstash-1-"+date).type("logs").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{\"message\":\"mymsg1c\", \"ipaddr\": \"10.0.0.2\",\"msgid\": \"12\"}", XContentType.JSON)).actionGet(); + + tc.index(new IndexRequest("logstash-1-"+date).type("logs").setRefreshPolicy(RefreshPolicy.IMMEDIATE) + .source("{\"message\":\"mymsg1d\", \"ipaddr\": \"10.0.0.3\",\"msgid\": \"14\"}", XContentType.JSON)).actionGet(); + } + + @Test + public void testSearch() throws Exception { + + setup(); + + HttpResponse res; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("%3Clogstash-%7Bnow%2Fd%7D%3E/logs/_search?pretty", encodeBasicHeader("admin", "admin"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("\"total\" : 2,\n \"max_")); + Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); + Assert.assertTrue(res.getBody().contains("ipaddr")); + Assert.assertTrue(res.getBody().contains("message")); + Assert.assertTrue(res.getBody().contains("mymsg")); + Assert.assertTrue(res.getBody().contains("msgid")); + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("%3Clogstash-%7Bnow%2Fd%7D%3E/logs/_search?pretty", encodeBasicHeader("logstash", "password"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("\"total\" : 1,\n \"max_")); + Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); + Assert.assertFalse(res.getBody().contains("ipaddr")); + Assert.assertFalse(res.getBody().contains("message")); + Assert.assertFalse(res.getBody().contains("mymsg")); + Assert.assertTrue(res.getBody().contains("msgid")); + } + + @Test + public void testFieldCaps() throws Exception { + + setup(); + + HttpResponse res; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("%3Clogstash-%7Bnow%2Fd%7D%3E/_field_caps?fields=*&pretty", encodeBasicHeader("admin", "admin"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("ipaddr")); + Assert.assertTrue(res.getBody().contains("message")); + Assert.assertTrue(res.getBody().contains("msgid")); + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("%3Clogstash-%7Bnow%2Fd%7D%3E/_field_caps?fields=*&pretty", encodeBasicHeader("logstash", "password"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertFalse(res.getBody().contains("ipaddr")); + Assert.assertFalse(res.getBody().contains("message")); + Assert.assertTrue(res.getBody().contains("msgid")); + } + + @Test + public void testSearchWc() throws Exception { + + setup(); + + HttpResponse res; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("logstash-*/logs/_search?pretty", encodeBasicHeader("admin", "admin"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("\"total\" : 4,\n \"max_")); + Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); + Assert.assertTrue(res.getBody().contains("ipaddr")); + Assert.assertTrue(res.getBody().contains("message")); + Assert.assertTrue(res.getBody().contains("mymsg")); + Assert.assertTrue(res.getBody().contains("msgid")); + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("logstash-*/logs/_search?pretty", encodeBasicHeader("logstash", "password"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("\"total\" : 2,\n \"max_")); + Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); + Assert.assertFalse(res.getBody().contains("ipaddr")); + Assert.assertFalse(res.getBody().contains("message")); + Assert.assertFalse(res.getBody().contains("mymsg")); + Assert.assertTrue(res.getBody().contains("msgid")); + } + + @Test + public void testSearchWc2() throws Exception { + + setup(); + + HttpResponse res; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("logstash-1-*,logstash-20*/logs/_search?pretty", encodeBasicHeader("admin", "admin"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("\"total\" : 4,\n \"max_")); + Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); + Assert.assertTrue(res.getBody().contains("ipaddr")); + Assert.assertTrue(res.getBody().contains("message")); + Assert.assertTrue(res.getBody().contains("mymsg")); + Assert.assertTrue(res.getBody().contains("msgid")); + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("logstash-1-*,logstash-20*/logs/_search?pretty", encodeBasicHeader("regex", "password"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("\"total\" : 2,\n \"max_")); + Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); + Assert.assertFalse(res.getBody().contains("ipaddr")); + Assert.assertFalse(res.getBody().contains("message")); + Assert.assertFalse(res.getBody().contains("mymsg")); + Assert.assertTrue(res.getBody().contains("msgid")); + } +} diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java index 2e5da08..153d248 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsFlsCrossClusterSearchTest.java @@ -45,12 +45,12 @@ protected String getResourceFolder() { return "dlsfls"; } - private void setupCcs() throws Exception { + private void setupCcs(String remoteRoles) throws Exception { System.setProperty("security.display_lic_none","true"); cl2Info = cl2.startCluster(minimumSecuritySettings(Settings.EMPTY), ClusterConfiguration.DEFAULT); - initialize(cl2Info, Settings.EMPTY, new DynamicSecurityConfig().setSecurityRoles("roles_983.yml")); + initialize(cl2Info, Settings.EMPTY, new DynamicSecurityConfig().setSecurityRoles(remoteRoles)); System.out.println("### cl2 complete ###"); //cl1 is coordinating @@ -74,7 +74,7 @@ private Settings crossClusterNodeSettings(ClusterInfo remote) { @Test public void testCcs() throws Exception { - setupCcs(); + setupCcs("roles_983.yml"); try (TransportClient tc = getInternalTransportClient(cl1Info, Settings.EMPTY)) { tc.index(new IndexRequest("twitter").type("tweet").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") @@ -126,6 +126,146 @@ public void testCcs() throws Exception { Assert.assertTrue(ccs.getBody().contains("salary1")); Assert.assertFalse(ccs.getBody().contains("secret1")); Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); - Assert.assertFalse(ccs.getBody().contains("xxx1")); + Assert.assertFalse(ccs.getBody().contains("xxx1")); Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); } -} + + @Test + public void testCcsDifferentConfig() throws Exception { + setupCcs("roles_ccs2.yml"); + + try (TransportClient tc = getInternalTransportClient(cl1Info, Settings.EMPTY)) { + tc.index(new IndexRequest("twitter").type("tweet").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") + .source("{\"cluster\": \""+cl1Info.clustername+"\"}", XContentType.JSON)).actionGet(); + } + + try (TransportClient tc = getInternalTransportClient(cl2Info, Settings.EMPTY)) { + tc.index(new IndexRequest("twutter").type("tweet").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") + .source("{\"cluster\": \""+cl2Info.clustername+"\"}", XContentType.JSON)).actionGet(); + tc.index(new IndexRequest("humanresources").type("hr").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") + .source("{\"cluster\": \""+cl2Info.clustername+"\","+ + "\"Designation\": \"CEO\","+ + "\"FirstName\": \"__fn__"+cl2Info.clustername+"\","+ + "\"LastName\": \"lastname0\","+ + "\"Salary\": \"salary0\","+ + "\"SecretFiled\": \"secret0\","+ + "\"AnotherSecredField\": \"anothersecret0\","+ + "\"XXX\": \"xxx0\"" + + "}", XContentType.JSON)).actionGet(); + + tc.index(new IndexRequest("humanresources").type("hr").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("1") + .source("{\"cluster\": \""+cl2Info.clustername+"\","+ + "\"Designation\": \"someoneelse\","+ + "\"FirstName\": \"__fn__"+cl2Info.clustername+"\","+ + "\"LastName\": \"lastname1\","+ + "\"Salary\": \"salary1\","+ + "\"SecretFiled\": \"secret1\","+ + "\"AnotherSecredField\": \"anothersecret1\","+ + "\"XXX\": \"xxx1\"" + + "}", XContentType.JSON)).actionGet(); + + } + + HttpResponse ccs = null; + + System.out.println("###################### query 1"); + //on coordinating cluster + ccs = new RestHelper(cl1Info, false, false, getResourceFolder()).executeGetRequest("cross_cluster_two:humanresources/_search?pretty", encodeBasicHeader("human_resources_trainee", "password")); + System.out.println(ccs.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, ccs.getStatusCode()); + Assert.assertFalse(ccs.getBody().contains("crl1")); + Assert.assertTrue(ccs.getBody().contains("crl2")); + Assert.assertTrue(ccs.getBody().contains("\"total\" : 1,\n \"max_score")); + Assert.assertTrue(ccs.getBody().contains("XXX")); + Assert.assertTrue(ccs.getBody().contains("xxx")); + Assert.assertFalse(ccs.getBody().contains("Designation")); + Assert.assertFalse(ccs.getBody().contains("salary1")); + Assert.assertTrue(ccs.getBody().contains("salary0")); + Assert.assertFalse(ccs.getBody().contains("secret0")); + Assert.assertTrue(ccs.getBody().contains("__fn__crl2")); + Assert.assertFalse(ccs.getBody().contains("secret1")); + Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); + Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); + } + + @Test + public void testCcsDifferentConfigBoth() throws Exception { + setupCcs("roles_ccs2.yml"); + + try (TransportClient tc = getInternalTransportClient(cl1Info, Settings.EMPTY)) { + tc.index(new IndexRequest("twitter").type("tweet").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") + .source("{\"cluster\": \""+cl1Info.clustername+"\"}", XContentType.JSON)).actionGet(); + + tc.index(new IndexRequest("humanresources").type("hr").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") + .source("{\"cluster\": \""+cl1Info.clustername+"\","+ + "\"Designation\": \"CEO\","+ + "\"FirstName\": \"__fn__"+cl1Info.clustername+"\","+ + "\"LastName\": \"lastname0\","+ + "\"Salary\": \"salary0\","+ + "\"SecretFiled\": \"secret3\","+ + "\"AnotherSecredField\": \"anothersecret3\","+ + "\"XXX\": \"xxx0\"" + + "}", XContentType.JSON)).actionGet(); + + tc.index(new IndexRequest("humanresources").type("hr").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("1") + .source("{\"cluster\": \""+cl1Info.clustername+"\","+ + "\"Designation\": \"someoneelse\","+ + "\"FirstName\": \"__fn__"+cl1Info.clustername+"\","+ + "\"LastName\": \"lastname1\","+ + "\"Salary\": \"salary1\","+ + "\"SecretFiled\": \"secret4\","+ + "\"AnotherSecredField\": \"anothersecret4\","+ + "\"XXX\": \"xxx1\"" + + "}", XContentType.JSON)).actionGet(); + } + + try (TransportClient tc = getInternalTransportClient(cl2Info, Settings.EMPTY)) { + tc.index(new IndexRequest("twutter").type("tweet").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") + .source("{\"cluster\": \""+cl2Info.clustername+"\"}", XContentType.JSON)).actionGet(); + tc.index(new IndexRequest("humanresources").type("hr").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("0") + .source("{\"cluster\": \""+cl2Info.clustername+"\","+ + "\"Designation\": \"CEO\","+ + "\"FirstName\": \"__fn__"+cl2Info.clustername+"\","+ + "\"LastName\": \"lastname0\","+ + "\"Salary\": \"salary0\","+ + "\"SecretFiled\": \"secret0\","+ + "\"AnotherSecredField\": \"anothersecret0\","+ + "\"XXX\": \"xxx0\"" + + "}", XContentType.JSON)).actionGet(); + + tc.index(new IndexRequest("humanresources").type("hr").setRefreshPolicy(RefreshPolicy.IMMEDIATE).id("1") + .source("{\"cluster\": \""+cl2Info.clustername+"\","+ + "\"Designation\": \"someoneelse\","+ + "\"FirstName\": \"__fn__"+cl2Info.clustername+"\","+ + "\"LastName\": \"lastname1\","+ + "\"Salary\": \"salary1\","+ + "\"SecretFiled\": \"secret1\","+ + "\"AnotherSecredField\": \"anothersecret1\","+ + "\"XXX\": \"xxx1\"" + + "}", XContentType.JSON)).actionGet(); + + } + + HttpResponse ccs = null; + + System.out.println("###################### query 1"); + //on coordinating cluster + ccs = new RestHelper(cl1Info, false, false, getResourceFolder()).executeGetRequest("cross_cluster_two:humanresources,humanresources/_search?pretty", encodeBasicHeader("human_resources_trainee", "password")); + System.out.println(ccs.getBody()); + Assert.assertEquals(HttpStatus.SC_OK, ccs.getStatusCode()); + Assert.assertTrue(ccs.getBody().contains("crl1")); + Assert.assertTrue(ccs.getBody().contains("crl2")); + Assert.assertTrue(ccs.getBody().contains("\"total\" : 2,\n \"max_score")); + Assert.assertTrue(ccs.getBody().contains("XXX")); + Assert.assertTrue(ccs.getBody().contains("xxx")); + Assert.assertTrue(ccs.getBody().contains("Designation")); + Assert.assertTrue(ccs.getBody().contains("salary1")); + Assert.assertTrue(ccs.getBody().contains("salary0")); + Assert.assertFalse(ccs.getBody().contains("secret0")); + Assert.assertTrue(ccs.getBody().contains("__fn__crl2")); + Assert.assertTrue(ccs.getBody().contains("__fn__crl1")); + Assert.assertFalse(ccs.getBody().contains("secret1")); + Assert.assertFalse(ccs.getBody().contains("AnotherSecredField")); + Assert.assertTrue(ccs.getBody().contains("someoneelse")); + Assert.assertEquals(ccs.getHeaders().toString(), 1, ccs.getHeaders().size()); + } +} \ No newline at end of file diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsTest.java index c0cb3cd..c16854c 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/DlsTest.java @@ -115,6 +115,7 @@ public void testDls() throws Exception { Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("/deals/_search?pretty", encodeBasicHeader("dept_manager", "password"))).getStatusCode()); Assert.assertTrue(res.getBody().contains("\"total\" : 1,\n \"max_")); Assert.assertTrue(res.getBody().contains("\"failed\" : 0")); + Assert.assertEquals(res.getHeaders().toString(), 1, res.getHeaders().size()); Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("/deals/_search?pretty", encodeBasicHeader("admin", "admin"))).getStatusCode()); Assert.assertTrue(res.getBody().contains("\"total\" : 2,\n \"max_")); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/IndexPatternTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/IndexPatternTest.java index bd229d4..25fbf40 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/IndexPatternTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/dlsfls/IndexPatternTest.java @@ -78,6 +78,26 @@ public void testSearch() throws Exception { Assert.assertTrue(res.getBody().contains("msgid")); } + @Test + public void testFieldCaps() throws Exception { + + setup(); + + HttpResponse res; + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("/logstash-2016/_field_caps?fields=*&pretty", encodeBasicHeader("admin", "admin"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertTrue(res.getBody().contains("ipaddr")); + Assert.assertTrue(res.getBody().contains("message")); + Assert.assertTrue(res.getBody().contains("msgid")); + + Assert.assertEquals(HttpStatus.SC_OK, (res = rh.executeGetRequest("/logstash-2016/_field_caps?fields=*&pretty", encodeBasicHeader("logstash", "password"))).getStatusCode()); + System.out.println(res.getBody()); + Assert.assertFalse(res.getBody().contains("ipaddr")); + Assert.assertFalse(res.getBody().contains("message")); + Assert.assertTrue(res.getBody().contains("msgid")); + } + @Test public void testSearchWc() throws Exception { diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RolesApiTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RolesApiTest.java index 928f376..f010838 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RolesApiTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/RolesApiTest.java @@ -62,6 +62,14 @@ public void testRolesApi() throws Exception { // GET, new URL endpoint in security response = rh.executeGetRequest("/_opendistro/_security/api/roles", new Header[0]); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + Assert.assertTrue(response.getBody().contains("\"cluster\":[\"*\"]")); + Assert.assertFalse(response.getBody().contains("\"cluster\" : [")); + + // GET, new URL endpoint in security, pretty + response = rh.executeGetRequest("/_opendistro/_security/api/roles?pretty", new Header[0]); + Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + Assert.assertFalse(response.getBody().contains("\"cluster\":[\"*\"]")); + Assert.assertTrue(response.getBody().contains("\"cluster\" : [")); // hidden role response = rh.executeGetRequest("/_opendistro/_security/api/roles/internal", new Header[0]); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/UserApiTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/UserApiTest.java index ad53b5f..1a70c16 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/UserApiTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/dlic/rest/api/UserApiTest.java @@ -73,7 +73,7 @@ public void testUserApi() throws Exception { // -- PUT // no username given - response = rh.executePutRequest("/_opendistro/_security/api/user/", "{hash: \"123\"}", new Header[0]); + response = rh.executePutRequest("/_opendistro/_security/api/user/", "{\"hash\": \"123\"}", new Header[0]); Assert.assertEquals(HttpStatus.SC_METHOD_NOT_ALLOWED, response.getStatusCode()); // Faulty JSON payload @@ -316,6 +316,11 @@ public void testUserApi() throws Exception { addUserWithPassword("$1aAAAAAAAAC", "$1aAAAAAAAAC", HttpStatus.SC_CREATED); addUserWithPassword("abc", "abc", HttpStatus.SC_CREATED); + + + // check tabs in json + response = rh.executePutRequest("/_opendistro/_security/api/user/userwithtabs", "\t{\"hash\": \t \"123\"\t} ", new Header[0]); + Assert.assertEquals(response.getBody(), HttpStatus.SC_CREATED, response.getStatusCode()); } @Test diff --git a/src/test/resources/cache/action_groups.yml b/src/test/resources/cache/action_groups.yml new file mode 100644 index 0000000..4a220e5 --- /dev/null +++ b/src/test/resources/cache/action_groups.yml @@ -0,0 +1,92 @@ +UNLIMITED: + - "*" + +###### INDEX LEVEL ###### + +INDICES_ALL: + - "indices:*" + +# for backward compatibility +ALL: + - INDICES_ALL + +MANAGE: + - "indices:monitor/*" + - "indices:admin/*" + +CREATE_INDEX: + - "indices:admin/create" + - "indices:admin/mapping/put" + +MANAGE_ALIASES: + - "indices:admin/aliases*" + +# for backward compatibility +MONITOR: + - INDICES_MONITOR + +INDICES_MONITOR: + - "indices:monitor/*" + +DATA_ACCESS: + - "indices:data/*" + - CRUD + +WRITE: + - "indices:data/write*" + - "indices:admin/mapping/put" + +READ: + - "indices:data/read*" + - "indices:admin/mappings/fields/get*" + +DELETE: + - "indices:data/write/delete*" + +CRUD: + - READ + - WRITE + - DELETE + +SEARCH: + - "indices:data/read/search*" + - "indices:data/read/msearch*" + - SUGGEST + +SUGGEST: + - "indices:data/read/suggest*" + +INDEX: + - "indices:data/write/index*" + - "indices:data/write/update*" + - "indices:admin/mapping/put" + - "indices:data/write/bulk*" + +GET: + - "indices:data/read/get*" + - "indices:data/read/mget*" + +###### CLUSTER LEVEL ###### + +CLUSTER_ALL: + - "cluster:*" + +CLUSTER_MONITOR: + - "cluster:monitor/*" + +CLUSTER_COMPOSITE_OPS_RO: + - "indices:data/read/mget" + - "indices:data/read/msearch" + - "indices:data/read/mtv" + - "indices:data/read/coordinate-msearch*" + - "indices:admin/aliases/exists*" + - "indices:admin/aliases/get*" + +CLUSTER_COMPOSITE_OPS: + - "indices:data/write/bulk" + - "indices:admin/aliases*" + - CLUSTER_COMPOSITE_OPS_RO + +MANAGE_SNAPSHOTS: + - "cluster:admin/snapshot/*" + - "cluster:admin/repository/*" \ No newline at end of file diff --git a/src/test/resources/cache/config.yml b/src/test/resources/cache/config.yml new file mode 100644 index 0000000..1ae2db4 --- /dev/null +++ b/src/test/resources/cache/config.yml @@ -0,0 +1,20 @@ +opendistro_security: + dynamic: + http: + anonymous_auth_enabled: false + xff: + enabled: false + authc: + dummy_c_domain: + enabled: true + order: 0 + http_authenticator: + type: com.amazon.opendistroforelasticsearch.security.cache.DummyHTTPAuthenticator + challenge: false + authentication_backend: + type: com.amazon.opendistroforelasticsearch.security.cache.DummyAuthenticationBackend + authz: + dummy_z_domain: + enabled: true + authorization_backend: + type: com.amazon.opendistroforelasticsearch.security.cache.DummyAuthorizer diff --git a/src/test/resources/cache/internal_users.yml b/src/test/resources/cache/internal_users.yml new file mode 100644 index 0000000..764b51c --- /dev/null +++ b/src/test/resources/cache/internal_users.yml @@ -0,0 +1,84 @@ +# This is the internal user database +# The hash value is a bcrypt hash and can be generated with plugin/tools/hash.sh +admin: + hash: $2a$12$VcCDgh2NDk07JGN0rjGbM.Ad41qVR/YFJcgHp0UGns5JDymv..TOG + #password is: admin +#keys cannot contain dots +#if you have a username with dots then specify it with username: XXX + +snapshotrestore: + hash: $2a$12$6sre4JH7O4Rgh7ubWmeyWus6UIIA13MqW8eR8KD5Qbxn06CDbJG/G + +hr_employee: + hash: $2a$12$7QIoVBGdO41qSCNoecU3L.yyXb9vGrCvEtVlpnC4oWLt/q0AsAN52 + #password is: hr_employee + +hr.employee: + hash: $2a$12$LRNG7ETwMcO68VNh14B3AuKPkvOaC0k26.QnSrv9AvbmT1JRNMJum + #password is: hr.employee + +hr_trainee: + hash: $2a$12$s6rC7o345lvXp.JpTrA91O6xYAGVCCxKdVclsNkWJTaquW4GK9E9u + #password is: hr_trainee + +finance_employee: + hash: $2a$12$Kv.4sU5r1zy2ZqnSDm99Ae6ImCMKtjJq4enT.9d3c55cA0O2LGNH6 + #password is: finance_employee + +finance_trainee: + hash: $2a$12$c26Pnq6yiZcgi8PxNEyp5O3wIn1G1eJfCvJFifEKosQJeojUZf/D6 + #password is: finance_trainee + +no_roles: + hash: $2a$12$gxsE8oEicXy3mNBkGEO5K.P3J/CDq3GHXDYeQTmVI/v3AA84vqIXm + #password is: no_roles + +mister_picard: + username: mister.picard + hash: $2a$12$wkY2BsRneCU5za1OPYlzsehQit6gu2vprVv/4jHiSEEBv2ThunaTS + #password is: picard +spock: + hash: $2a$12$GI9JXffO3WUjTsU7Yy3E4.LBxC2ILo66Zg/rr79BpikSL2IIRezQa + #password is: spock + roles: + - vulcan + - starfleet +kirk: + hash: $2a$12$xZOcnwYPYQ3zIadnlQIJ0eNhX1ngwMkTN.oMwkKxoGvDVPn4/6XtO + #password is: kirk + roles: + - captains + - starfleet +worf: + hash: $2a$12$A41IxPXV1/Dx46C6i1ufGubv.p3qYX7xVcY46q33sylYbIqQVwTMu + #password is: worf +logstash: + hash: $2a$12$u1ShR4l4uBS3Uv59Pa2y5.1uQuZBrZtmNfqB3iM/.jL0XoV9sghS2 + #password is: logstash +kibanaserver: + hash: $2a$12$4AcgAt3xwOWadA5s5blL6ev39OXDNhmOesEoo33eZtrq2N0YrU3H. + #password is: kibanaserver +kibanaro: + hash: $2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC + #password is: kibanaro +readall: + hash: $2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2 + #password is: readall +dlsflsuser: + hash: $2a$12$30rb6oabnodiSdysdWJnhO.4sVRkyNudPC1woYCJFhXja3rkyXbam + #password is: dlsflsuser +test: + hash: $2a$12$1HqHxm3QTfzwkse7vwzhFOV4gDv787cZ8BwmCwNEyJhn0CZoo8VVu + #password is: test + +user_a: + hash: $2a$04$NDy7mGbRNrmPMh9nSnIB.OTMFkcioEd69A04ReSGkJDd7QHxnCcVC + #password is: user_a + roles: + - ua + +user_b: + hash: $2a$04$idGSEpNOhFbyiRL6toGPT.orh7ENOEU8kAqwkRFaXWRdA6wVgyqUu + #password is: user_b + roles: + - ub \ No newline at end of file diff --git a/src/test/resources/cache/kirk-keystore.jks b/src/test/resources/cache/kirk-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..dd7562ef81822291c9e48be60b6daef2883288a9 GIT binary patch literal 4525 zcmdVccT^MWwgB){LPGDoiG|JtLlcmi&;&(_6h#7rP(uq{DS;pYA_Afmm4HYQP!tF# zO+it?LY1P3NK;Ux3W)H6$9wKM=ic}3T5sJy?)_ud%--LgH8bDr`F*qXdFwL>1cER& z@XrnA;}zt?2;6|iLvA1t91KukKLBYi?ob|vHEVPXTvFy98Cf88WeS!f7A?SFPvZg)V z7v6j;sEtmwpQf&7q@7zop%6{r2s~o4`h$O*s-O*b>U-1znW*c#zSZBch%8xV*PK!@M#{2`WZYarXxm&{`QatOX?4u9M`&!g z&8@j*V|x00YnIuVYvX+t0oL3*%ZApt%D}TBgx;82*eFi6zP*KyC^rTTB}T2_jyE(_ z;f-sVA5zg)Q)lMvlIJLf*G91S{6-yTXOSiB%Pl6=)ezz52b@>D2PMdN`A#<=9WZ-| z7KW!1+>q&M9FI4m)Uo2)<*krrUc5S@Z)L7G7dgI!>|D_H@?HQ2<&8$8eAM5V`nS_j zNpG-+#Ccy*OAKTS(OGuQwm4>u0HI3?WwUI`J?nS!O@}YqX!P0n!lx^wZ-xy>9X%}X z%zmko=qLu(IqcUGH-zYG9NL}p(8Yv5_N2}HLil{#_GY+#+?8~+txTk4E?9{>Y6;8Q zEZMQQG+Zc7(ONjc_Z{R_?;uOgps(gW9B#f(6USN@W|Q~IWxj{osPJ=+k~BeQ@cQL| z$FtVYsZN!ibBkZ5nzYpH;ZHJ%>jHUIYX_z7yg_w(Pt?L{^X(1y1V;C7nhA@9^p>1jxzk4S}xL;ZtiB54PN2WN{SYvv3Rh9rlt7aOkInvb_wo?IeDCPpYvkB~u~s|dnp zzlO4kJt=!g)8MF%;L*>C7`w|&Q5;Zw^-N`BMf^2O*~k34r_QaBMiXr@B`1*OXV;sA z9gZ{qm~Tz#GUS?VMvHuD!B$x1U&)(!$F*Sp&b2~cEiXb}fOVHxav+Cc&lruM_R&V( z{43J!IB|M3ZPcwnQbKw_ebQNXOK%`r0lA@di3f&@c4}>O5mAX}`oa7CAntB*PQV6H z^L0WBm#47xTa;TPT3rIKe{dQg49MoR+S7kZUoEgm8$1*sT7hyWeFN zn%<%O^kC4vVk0^76VKU1bP6oo&2Wj(7bV7r$yWzRuT)$$b)}BoAG@t&D^9#0)4o5t z3!Xb!WQChtbPDCZqnv#{@Cy@D^Gw)@;l6aTSvhyQ&XWfqXs~EWy=uctyrPgS<;cB3 ziFkO@!mSh-w6rR5LZ+*OP##Ou?k-vUl=T*#5wv)XW2;UyNTZj8}9GudhAJ(K5qN?vK^qvH6t3LGn(B3(e+O(u*EW1 zgo5DqN~(Y+2m~`?c&GqQ{_o?!Vs@#_RA&(9SEpn!M%ab_5c z29yHk05S3?2n7rV(d*;#xkoZwk^xiERpN!)4V9H=l4Tn>l)s)UWPUlhW+IW-e}Nd?fsk@0y>16?UrrsO!Hfv0>DAFyj~ z4;Oe+Dtg6e1A$uu6@_fEHn~Tu=r`1LiAGjPWDh&kDWabnW};Hzp#zbs1*P^&lDQn3 zhr`|MiT6!+C&i|>I%eo`0eSDaQI)&vJ`Nd~4)V@wtQn-jj?ItB_B_)Xwk^s@lernM zO6lqvz0uD;>}Ldz03>%+rJfw%-gl1kJbl2iG7Yh2s)~NTOz*9{732Q!JpEGs6V6Or z?v@;35->T4fIz_@a8cZQAa0bgx9TTIG8{VR`$aOlLZ$GqH0yMO z@Y6xHgC39jse&KLbn`{{Q=Dt?sE>A+&5;&KL6?ewqvkimo8(4^)y-6@f|g&srG3>K zOR=S%Q-k-M?40&}c8i-mnBH1sjS;pxgNV#%ZPwef`YO#$*D?j$PZ$l zv{pW;Ex{36V{U;rt7KE%$r;XTZ-@9eYiWqK{GxeG!gsw=N<4qP;JB$*gcAmX#ZTkm zq-$D~{KvLfP0R=BdS#-bj(0KEcF*w_t(~hvN!;6+PE{lAeAu>?ljx1Nff@jeHBiRi z;8P7Z7*;-^Yp6&xT-dBKQxS9QF4n?^yDy1p2?1 z{23BJ<8P2)b|7Jd|FHo6E0OvuRKyY-<<1OEd3a0BM&Rq!?9JVYy#B1+Qd!I5#H&na zk30jH2V789$$yAG~m0RXuJD{3u#WSeCxe~2|f62_g>oeC8H2- ze_UH=t}fpaj}759iKB>E5tqFUk`YL%VC@Mru%ad`VPXugO{!w0WjwP{qm4bia|eAw z%*&+wPLE(jG-lJ5O7spY(>%Nwf zJoPod6ROYp-O|9-fNjCN;-j5sdwH&MMsRxO-8N?0U+SP@S1s;CxMp2EWy){1+$1-p z-DsI!@Z4bXQA?=y)QQ#3-$e2N-2dzmme zbbuEP3)|)lIMxspuEbuJf&jNE&eccAgGAH(;O4$ri-*b+2mD#*+5{X{T+%#m+hwnw zkf-(~ntl3qGJvy}XGYaA_O+L)P&_p4@%st9PcpPWv(828(5o1EE|+dIyIAoDXB5Rx z2VB*Q-H5fTW<9l){gQ8BNW*?6U`6S{ffb#a&3)ZV>)Im)?txTeS&1&gRUu8nNMOgv z+(FN+c$pG?jSUeB*?EX{HtNS#Gz{^Sc$kU#;o^jQwe_IHf@kKow7!c`4=eK)3_^Qx ze7?GX&f}#QwMYDVJliV?LGc3@|E>ar@q@9&P+{tq3gEv?*MFl5|8b7}D-UjPy+2V- zR(n5asG`m*?|e`x$VJQF^3>hqpOW4P>L+*@OdT!pG`Rd)sD7&MgFN|kU=M2@8(Y6w ztRiYPqHa?A_K*Cd0VS>?dqH_B%{`uuhc64=N%<(+p1=57=C#>Iv?CgYyQ>hI6)OpE zamrO&M9IHI$%MboyIg}=uimEGEuTd;oGskcyAn}lHDB0@@N+%$RGxMe6Cr4funqQ$ z(@D`B1+Ln_7dr$zQ;nCclkF8IVs{r0xqV`xp_b29-!1B1zY$=RRk4Wz8NU~@x|U=) z^7_KegkHLFMM4Z#0{ z2OPh900*E95B~5Q@=w3@{|^RoP6f5c3mqhA=})^K1scsL<<#R9%F6QEx{2Rb`ky~z zrY&|YhQ9HW!AbMWFK9)w`=or^w-wr%%ogmWARu$h2!fZ<*ZkVuCOohaSsN5nyOO&>*~TNv9yR<|${+cysDRR3}7H>+p?a9c8v%bU%E#C4CGWvaeW$ oIl4xa=rqgDw#QA?#n3);-E>Npf^^i;kSQ<9A)UYFuMDdGAEFEd8UO$Q literal 0 HcmV?d00001 diff --git a/src/test/resources/cache/node-0-keystore.jks b/src/test/resources/cache/node-0-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..5693b7bf88ae5acc096a179fe2d4c9832dabd71a GIT binary patch literal 4593 zcmdUxXH-+$w#QR80YYd72pp=SNDVvm-m4<0^j-s@B?zI3(xeFpB8n76suWR(QY;ju zhzckmMZiMuh=L*@M=3Ha7xqfr*EbS~oAP^X}L4Py^ zk>rM#0IAXytj7&NAT&@g8rTMz5J)iu6o87tSfNlF2oMd-_0m;uVszPZHlFdlsJy{9 zL%fbt;HkbjHMh4O+w0k#4->j*hiOlN`0`b%=KNS4rP+7uBBcFz3W2Q z)g-ni){9hfC5eWepNd1R3(qAC=(QVVWt3)_3af6&Fa&;};0uilDHF-&HVaB=Qh}>2 z=ggVgam<{J18>(}FBv;rvfPljMDtfm8iQSw;w7y9!#n7-)of@?>67#{mgz?;N{cJZ z-#@NyrmDIhmyuJyR7a;b!K@c(_kC6*N`BT`=8-{w5@Pi-MrpT#=h*bcY=5<#T>X`5 z+{ruA7H$F&WnLADB4m*}G!NcIQ@oBXPd!8yc_lQR>?+AZgg0Gi^J7ft)4JO++vP<6 z0u|aC;TZp5sQGHpM0nyynL)#wTTG8`&zpR9@{N(_Reu(@&}cARaK8X7OiZ=#*U?KK z7m@5ER|@xcms=1T1V05IPPVgNtmUHD$&J<#OHl2CD+Fmhz zYiU8(-2k9`%;=`6OfhD@A9*+8jFpjF+R|7RN23`qF#v{5WtFAqh1??yh)x83bmV67 zY+=Y04Rmf+ZzWraRrJd7b;SOr;&L=7eO1t6%cqzg4Kbsuk$e`mp#5<7>tdLlWpay0 zcU%v;AE&>QQ$m}nHmkia=9&ZE?)spU^-00vSsc#6kMd&dfUpdE$%FWWF}HJV_XGKs zxEKzt2`i?IB6(QEcn|xhO`f!>+j`Ksf|)I!xBwvz9xpO(QSsA5^UkMCC=~jYva~mZmauEu<0rapM*G&ItkI| zX4n=+s;WMpTB76dmA|1aR0pw$*fLnZU}7VanWN6E?!tA=Do=Sat0(^DOTe)!e7iF? zPs31DZ!n^0l0Q*lci^LOYTM*%xlW)lTVtXxTh58QaR-ld95SFMuD>3N1G)QOR))r9 z6)QSp@2m{Z_1dFTqFDOyUGmmt%KdXV6U17RMC?PD$ZH0yP!1;Gf?2Y|x6ZzlPEJZg zjnM5j*Ph+UTD$R@$Spe2iYFd88vk{_V+RS+27u)Q!H!FXMxJG1k!!8^V zc06%=(12ZSHAwDwdbLIO;^KP<*9|FM@8bICZmE?=Vfb9R&NsAA0_Vx3@QTPFO$Az-vS>F|l_&usPd9Eye zCd&}`>q3I3W*+5mD*BK(?ABhXU}sGVE#x`w3oZ$@ZxuT*8@_#=bjeD2jKj`WlK6H* zNkoll!x^j>XqG0h`DpFw3L;9*+dDK()mpB-NW&@EnbhughHpT zwe<1SFaEZA%Z6Y_;H(>GR<#6YnrLB~iWPh}C}y1Ba@P;IbR@0-%E$0h!PR#sYTtJq zjXQyW?R*SrUf#DMCAmOhhLTWgyIpl%wY?p(_({T*ywKE-4DPB57trNhLQP5@2fgi91twGB?^E}hI69h8ZFF;9B(%(54 z>)}Zz1iAzU;Qd`mL^7U8-n&~#cOR0!vqvD2xF-UMo@6pLRwSGQ0`TMjNnhsxGTvX( zA0I^W#=Bu%{e3{$y*XF`EGQoR7oieO??ZBR_92i0$nih~wFXI`5rqRZG=B^q>IYD;ht>KCAMp1EfiS2nmB`#FmqUx=Qh}7oD)2;r%m{8jF zHrDRLDL4DR!hOKjgiQz_#H~$t8w$NS@p)G)Vq7`!{xE=`lLb@WVLn7iC!unV9F4r0 zGwr#iG9sjn3t?;No?b7`W>T*Te%Kp!VlIgv!&Maduyx;l)_b2Oj(-_7G)1X$Rnotz zjhnHWZ$J8i(!W0V)FJO$fTP+0FH>`-i;W?STTRy2r$!6nD%dr+@3{#y+J_T5xaYFX z{{bWhDv)TwXxQ>EAi@50Twqk{{~eI_|A-?hkU0MZNGC}o@*a(TxxoLu%b!rB@H>iR zexOLf=D!x9{|RON6;ix0Hlp!E)9zmT7ebxy%UKiS5TOcfqY-z`+8B$JMd`Pe&4er zGTaIaDXnw44o6F_cGS%vo6J&&p5>}%FK9{d`INnHx;S}G1n7%mrW|;rj1`L=ikW;0PkS?{BvY_yJ<8PQ2PaAR^`O+Xdtx|7%F|SK& zs;wzVW!iqN<0s18LAQU#7!(2S+k5!vJ*?4z24!+P7|L+-}bwgz&&+SyKz6gJ(_8ONWe&kg9$f6oy=bUhf zrotw7G}&PYwGg(w6AsWm_b@@wHLpy%QB2?Pe=0kkImT{&PClh8|DXblTB7VwsWAO-%as3rRQRu3@qgmMP1Z5{a2wItgT=%DIC&b8SPxYc|85#SJL`opnLTjf~s28 z$*h*ypzOw4d^u15;C#WB#`VxUCzcBy)B3u^KNrhCDHF=6Pir3F8>O10_#VtPqgiRk z5R&!3m?m0@PevXr9&(*$$VYxTPq|mvvvJc;=W@js5~4rGb>c>X@yKA>?4-s;{fd%4 zf$@hn##nFXlKQtEgwM3{l{L~1w$igX$7*8ot8?p1`LF!sfh*|zFFauWI}d0;IF$!~ zs9yf^t^aReAZnXeV^?6!pMUXrPunS-S!~vQXYsPKoR%JsZ(sYnUeM*Qbgl%w^%d3= zD%F*ppHbw08ep@u%!;nSy)r?Q%}op1DmIdIcz?-4JFRp{j`;C;6hYc zGIruPmTV=^uKPVoCU({AsiwPqm}$&BDs~Vk(_?q2vA?YyyVmbM(XB;6E+BP4?V|&Y zz;NO*@#xO==LHFyf@yWbvCF9g5qAs4H?8#piY^}Wx&1XC+1b^lL8g1W{vm1GC&r>y zW!W5PmKkbL%?ql2&XP6Wm^>NQ0h3TRxtXXc$#|g7*SXxdZWJ1LxLArVyqd?uc7ciU fkgKedwpBVyQD_3{J;vvymZy^>PpdUX45j%uEA}fN literal 0 HcmV?d00001 diff --git a/src/test/resources/cache/roles.yml b/src/test/resources/cache/roles.yml new file mode 100644 index 0000000..411c437 --- /dev/null +++ b/src/test/resources/cache/roles.yml @@ -0,0 +1,331 @@ +#: +# cluster: +# - '' +# indices: +# '': +# '': +# - '' +# _dls_: '' +# _fls_: +# - '' +# - '' + +# When a user make a request to elasticsearch then the following roles will be evaluated to see if the user has +# permissions for the request. A request is always associated with an action and is executed against and index (or alias) +# and a type. If a request is executed against all indices (or all types) then the asterix ('*') is needed. +# Every role a user has will be examined if it allows the action against an index (or type). At least one role must match +# for the request to be successful. If no role match then the request will be denied. Currently a match must happen within +# one single role - that means that permissions can not span multiple roles. + +# For , and simple wildcards are possible. +# A asterix (*) will match any character sequence (or an empty sequence) +# A question mark (?) will match any single character (but NOT empty character) +# Example: '*my*index' will match 'my_first_index' as well as 'myindex' but not 'myindex1' +# Example: '?kibana' will match '.kibana' but not 'kibana' + +# For , and are also regular expressions possible. +# You have to pre- and apend a '/' to use regex instead of simple wildcards +# '//' +# Example: '/\S*/' will match any non whitespace characters + +# DLS (Document level security) - NOT FREE FOR COMMERCIAL +# Install https://github.com/floragunncom/search-guard-module-dlsfls +# Per Index you can define a DLS query +# If more than one DLS query match they will be OR'ed + +# FLS (Field level security) - NOT FREE FOR COMMERCIAL +# Per Index you can define a FLS fields +# If more than one FLS config match the field will be appended +# Install https://github.com/floragunncom/search-guard-module-dlsfls + +# Kibana multitenancy - NOT FREE FOR COMMERCIAL +# Per role you can define on ore more tenants +# https://github.com/floragunncom/search-guard-docs/blob/master/multitenancy.md + +# Demo roles for sample data (Kibana included) +human_resources: + cluster: + - CLUSTER_COMPOSITE_OPS + indices: + 'humanresources': + '*': + - '*' + '?kibana': + '*': + - ALL + tenants: + human_resources: RW + performance_data: RW + business_intelligence: RO + management: RW + PerFormance ___Reviews/&%%/&&/ : RW + PerFormance ___Reviews%%%!!! : RW + +human_resources_trainee: + cluster: + - CLUSTER_COMPOSITE_OPS_RO + indices: + 'humanresources': + '*': + - CRUD + _dls_: '{ "bool": { "must_not": { "match": { "Designation": "CEO" }}}}' + _fls_: + - 'Designation' + - 'FirstName' + - 'LastName' + - 'Salary' + '?kibana': + '*': + - ALL + tenants: + human_resources: RO + performance_data: RO + business_intelligence: RO + +finance: + cluster: + - CLUSTER_COMPOSITE_OPS_RO + indices: + 'finance': + '*': + - '*' + 'humanresources': + '*': + - READ + _fls_: + - 'Designation' + - 'FirstName' + - 'LastName' + - 'Salary' + '?kibana': + '*': + - ALL + tenants: + finance: RW + finance_management: RW + performance_data: RW + management: RW + business_intelligence: RW + human_resources: RO + +finance_trainee: + cluster: + - CLUSTER_COMPOSITE_OPS_RO + indices: + 'finance': + 'revenue': + - CRUD + '?kibana': + '*': + - ALL + tenants: + finance: RO + +# End sample data demo, default roles below + +# Default role for all users (including anonymous) +public: + cluster: + - cluster:monitor/main + - CLUSTER_COMPOSITE_OPS_RO + +# Allows everything +# but not changes to searchguard config/index +all_access: + cluster: + - UNLIMITED + indices: + '*': + '*': + - UNLIMITED + tenants: + adm_tenant: RW + test_tenant_ro: RW + +# Read all and monitor, but no write permissions +readonly_and_monitor: + cluster: + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS_RO + indices: + '*': + '*': + - INDICES_ALL + +# Read all, but no write permissions +readall: + cluster: + - CLUSTER_COMPOSITE_OPS_RO + indices: + '*': + '*': + - READ + +# For users which use kibana +kibana: + cluster: + - MONITOR + - CLUSTER_COMPOSITE_OPS_RO + indices: + '?kibana': + '*': + - MANAGE + - INDEX + - READ + - DELETE + '?kibana-6': + '*': + - MANAGE + - INDEX + - READ + - DELETE + '*': + '*': + - indices:data/read/field_caps* + +# For the kibana server +kibana_server: + cluster: + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS + indices: + '?kibana': + '*': + - INDICES_ALL + +# For logstash and beats +logstash: + cluster: + - indices:admin/template/get + - indices:admin/template/put + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS + indices: + 'logstash-*': + '*': + - CRUD + - CREATE_INDEX + '*beat*': + '*': + - CRUD + - CREATE_INDEX + +# Allows each user to access own named index +own_index: + cluster: + - CLUSTER_COMPOSITE_OPS + indices: + '${user_name}': + '*': + - INDICES_ALL + +# Allows adding and modifying repositories +# and creating and restoring snapshots +manage_snapshots: + cluster: + - MANAGE_SNAPSHOTS + indices: + '*': + '*': + - "indices:data/write/index" + - "indices:admin/create" + +# Make xpack monitoring work +monitor: + cluster: + - "cluster:admin/xpack/monitoring/*" + - "indices:admin/template/get" + - "indices:admin/template/put" + - "indices:admin/*get" + - CLUSTER_MONITOR + - CLUSTER_COMPOSITE_OPS + indices: + '.monitoring*': + '*': + - INDICES_ALL + +# Examples +role_starfleet: + cluster: + - CLUSTER_COMPOSITE_OPS + indices: + sf: + ships: + - READ + public: + - INDICES_ALL + students: + - READ + alumni: + - READ + 'pub*': + '*': + - READ + tenants: + enterprise_tenant: RW + test_tenant_ro: RW + +role_starfleet_captains: + indices: + sf: + '*': + - CRUD + pub*: + '*': + - CRUD + cluster: + - 'cluster:monitor*' + - CLUSTER_COMPOSITE_OPS + tenants: + command_tenant: RW + +readonly_dlsfls: + cluster: + - CLUSTER_COMPOSITE_OPS_RO + indices: + '/\S*/': + '*': + - READ + _dls_: '{"term" : {"_type" : "legends"}}' + _fls_: + - 'aaa' + - 'bbb' + +kibana_testindex: + cluster: + - CLUSTER_COMPOSITE_OPS_RO + indices: + 'test*': + '*': + - READ + - indices:admin/mappings/fields/get* + '.kibana': + '*': + - INDICES_ALL + tenants: + test_tenant_rw: RW + test_tenant_ro: RO + +ua: + cluster: + - '*' + indices: + 'indexa*': + '*': + - '*' + 'permitnotexistentindex': + '*': + - '*' + '?kibana': + '*': + - ALL + '*': + '*': + - indices:data/read/field_caps +ub: + cluster: + - '*' + indices: + 'indexb': + '*': + - '*' \ No newline at end of file diff --git a/src/test/resources/cache/roles_mapping.yml b/src/test/resources/cache/roles_mapping.yml new file mode 100644 index 0000000..dfcdc6c --- /dev/null +++ b/src/test/resources/cache/roles_mapping.yml @@ -0,0 +1,92 @@ +# In this file users, backendroles and hosts can be mapped to search guard roles. +# What a role is allowed to do you specify in roles.yml + +# For sample data demo +human_resources: + backendroles: + - hr + - jwt_hr + users: + - hr_employee + - hr.employee + - kerberos_hr_employee + +human_resources_trainee: + users: + - hr_trainee + backendroles: + - hr_trainee + +finance: + backendroles: + - finance + users: + - finance_employee + - kerberos_finance_employee + +finance_trainee: + users: + - finance_trainee + backendroles: + - finance_trainee + +manage_snapshots: + users: + - snapshotrestore + +role_starfleet: + backendroles: + - starfleet + - captains + - defectors + - 'cn=ldaprole,ou=groups,dc=example,dc=com' + hosts: + - "*.starfleetintranet.com" + users: + - worf + +role_starfleet_captains: + backendroles: + - captains + +role_klingons1: + backendroles: + - klingon + hosts: + - "*.klingongov.kli" + users: + - worf + +logstash: + users: + #- 'CN=logstash,OU=client,O=client,L=Test,C=DE' + #- 'CN=topbeat,OU=IT Department,O=floragunn,L=City,ST=Berlin,C=DE' + - logstash + +kibana_server: + users: + - kibanaserver + +kibana: + users: + - kibanaro + +all_access: + users: + - admin + - adm + - 'cn=admin2,ou=people,dc=example,dc=com' + +readall: + users: + - readall + - riker + - troid + +readonly_dlsfls: + users: + - dlsflsuser + +kibana4_testindex: + users: + - test diff --git a/src/test/resources/cache/spock-keystore.jks b/src/test/resources/cache/spock-keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..7ceed76f7c515338c4d6a8b14d284b2f94674c5d GIT binary patch literal 4528 zcmdVdc{r5q{s8dV42H1{gY2|e$1|2t$r@QALY9aaMi|44k+j(NcOs%9`x2=TrbR-9 zLX1MzB0Evm$nqQ2dC&X4=lp)>x_;OB1cERQ z@Q;IuMD%s_VnnV$jbShd!~_AtU|RqO93ck>!@#l-ZZMb$1Pg;ruk~&Abx9_zG=`7d z-C!2o%V(OAX=@-awRH6B%T&HJDMT`><{WRBv77Kq-rZ>LH;)u9`t6Q-c$3ZM!dR|Q zBv!cKWy4;tEaXj9gA*3j%?uOp8Gwy?QaUlZ}nWLS)^ z2G8R{>(zE;Q?%yAoSRM$cI{f@3y(iJeVmx~6B z>X=0Ce)4C%KJxt=;a=GOXc0CE2M850S+?4l)_Of5lJb5gFg^YmJ|~4b$M!*NnSGCy zHZ4h4Ayv?)MzuqYD6`zL#WD3(s5jYm(i8t2$vJoC@J}>o^e7+*8@lGsNGhb>E?B()P`O zmF>gVo#}lo-&3tahN9DhN-s$*arbq_PRL%UpFOS_v1M*FZ_CXz^2oY+Ut#840@cut0U zvNy3kR%m;;415GDOqa3ExL5vIK%FZ-?FPic+5-5j%wE9BE@_*TLn)&r1ZFou_m}1m zqC+bK3porNnl(~*jKy(ZrC&j!DlN_JcpgZop^e|9WXbhiOJ(ZWC7GN(fM=fmELe0X z+@$9P(#x3Eek>{IJ*m+h)2>jXx2TgkV=8Bvg`CQ+v(9Jl=)8SJ`zZn)zxNyU zoXPjlMfAe(+{y+$&S*6k`@qrW{h%IU*wk&Cb=9{_PQhK;ed04$rD^?BO+?gdrA3YF z$~sTYCXQNlMC5C}R~MO%9ji9tPy24JX%r``nn?*WWG!jW&^mLWRvl$nJJc+mpceC_ zv0jSx`-f5mu^fsOn~Y#(iu@oncU zBtnv*%O48vH`@eeokh_D+3M{+lNNnK$I@UN&IP>7Omj4*E~@dWap3)yzB`(?xtax4 zPh+ShUNF8j{*`6U8;{?cGE9Zl}dcRf0+BR!AGj$;MWm^wW0)9 zUbXM|Cb35;OYn9~^GJf;i!BzzT`W5Ix5wuf+Vlgi;ze`N+SKc_g$w#e9>H89?zP49 zDhiucTqW`xztAf-j@j=^irCzIF^Q?lWjeXEZoKT4c!(n`st3!lWv@RJov%9Ls(c<^ zhO~tQe(=V-4k9Zk?GVRWw>X8p6natG0jtJp-f8awFZPZn3b=qv zHm5ZVwx);m1==_BitM_sZ=TuuZZ#E2osdJ@1hI$W<7z17mjM370qp35V!)WnCO0VL z@(rb&v=x` zEom1%&43XKVzR}k0vaF?><9xy9Uu%UlMRJ{ArLS#fP}NkMjee>MS{U>Odufi8Wsg` z!x5}k_r@kK$h(0B(NI@kA1pt>_opN??wpGc(Hn;q1o+`lYZ!u^>&VZHxSp?%xDmk> zgWU~C?hLW93;lIS{Dh|m!IR)2uBY?2D(L@N1&Hz^vFd;-po&#dR>P{=^COjZLZx3J zV1{J_Sa*I6j)LeP!6E^+oq!nyv%-q@JqsEBp z?0w^wJrg;Bz49FM6@yiY8A&EVZ;FQB4Qn$WGcO#wttLC}J2jUzz14YI#q#r1IM2Ne zm)sj`i3&cX8yd7cq2|YZj&uD_qU=@P&Nl8k0I4`N8mnN!?(AS&a*~(Qs<;McF-8y4 zW!-UAmitaOaNu5+8+rA%z1P_r`(=~+K0J8#lr>G2WB${=I=?w_dkXQHQtI@PkMGdh z(dpg9wYD~m-Dy|$l{TFg*z(Cg4S|9|;JnabAasbaze24=IKkj;7z_$w%KotqA}AOH z00AO9LwjIofEUif*n^pvn4#d`zd?QubHLCr-ev9~UnlcoyOEmjC0Y{Ed``W#>44bI z*-|h803E`c5;&5KWz(vjXc?oP)z5$N30D6RII~kA26F)HfRowQ*(ahb@MJQPbU;zj z-z5;^;Yr2^oCzS|{9SzsWX7^%7@e%>?(OUE;t@a~>;wS>PcoU&Aqp-e5{^t#^l>4P zasG<_IEt?q&JE-0@BPOvVB8>#E&ziRiPUrFH93%e^DV<@{Oyk3tYqrd8|)id@y&Pm zis|#z?4qN%^A1+a!A52%o=Cco2}wgOaL(j8v1M~CQqoy2&AW|8s#gDabw%EfoDgjV z8H+SrI~Bo2zRMC@A#@;1M>dxT+rFaQCKqgTujIQT*Ld?e9udMTSD+TWFa=T?oPU}!Ro5RST7(6j4~#ae5Cc|M!wVO9H6>O%jzINf_aOtbqSYq^?pWkqCR)=-zSnvl25wE)VzHnz(uJ z`m)m{QkFzLGU2huUxQ1CNvcX24cKU>H!5^5HZ8v6`zgks{SzoJ+}525v$7>y!*)k$ zq(S=Lob5FI03Kh1=aidTe2XY6Tn~XW&!aRnbsHqjA5+TS8K`R!8L_BxSubl$^@x-- zJ{2)k)A@GxuvdUdv4qdXeib408S0CvOed3~v=-U~;+fTz-sViLn=`r!qTaMA4 z3dW7l#!F9S#}Cz*C1$tjj=g+FIW+FL((+Rzcfjq>9)aNi&yEtP9iLbLIO78X{sq$y zdHyLZf0W0e=~0=%-1ds@xYKAm&$pANX92#ywD}Je1p3``qTIm&kuq8^So0Clrrq6V z9iNt0T{5J~TIC(!#lix&xriqp`JcmZ6vs1zpDWBh43Yy0Q+=3Byi*oTOQO1c*=L{g z+bug&@ntrtD~@uJ@1l&SS5FhU-l8T|?L&GzRRzPL)W+da7q2*IS8}zpglWfRIc{gV zu}z3*eYAq;MPjCQ$a;uPIs1jJ)ONn^UUl0^;xeZGz_MoL#y|y*?xO9<@`npgU*{PR(CU|q}5{&tkhdG?3VPVv*{KR{)ulVF`DII71 zPIi_p7$oP-srmA38p=~Uc#mj)II=@bK=i=1zpKD_?SgJGR2cuI0{Ab}_1~z%f1G3g z%7a4gVaF2k{^9p}N@~1vPKPo6&If(XE<89j9Wx-H6XC8qZdruaz1}1EaJ>4H9QmSO zCwny#*<~D}fLIBt9y?UFnPEvRIwQ0fl&)0UiMKbuE?5yiCHx{|p+~yMc>R(+7Gdx} zo{|zG&h*UT=KckQTsuPgTu=J-N|m+p?MR!YSk_0exf|NIg6>(&=hiX%oQZxVmt(0C zBw)yFP4WrVjMo?fGOd^vn)vYvhD#^nY~@Eob{F)zPP648mSW2vBp)faw;=xZi`*} z>47WY@(&(x{_X)20A+aahsThAdawU~Fpzb~t~!-#Czg}=ivH41e-d-!p^JQRar$$* z$Lexd+iRAbh1LbifRD6+6k6`B#p`mB>Y_Mjdy@;1dzk?2$dNVgo*>D#yEe zkt<#9pE`8Q5i#M9ILvS$_qeJ$>)9qt)f8_|$nMK_h@L5MeN!!6w$Swce*nkT8J_?E literal 0 HcmV?d00001 diff --git a/src/test/resources/cache/truststore.jks b/src/test/resources/cache/truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..7a1b59a8d24993d19aeda0d1651ce9df702385ed GIT binary patch literal 1096 zcmezO_TO6u1_mY|W(3o`Mfv$9y2**U$r*{6c|g%*gFU(?46G4)rUsS_3@l6rP0arc znwXv~U}j=uVq#=8;AP{~YV&CO&dbQi&B|cV*l#FoAjrnWIZ3T^@+X}XMoBT|{q7A_tESBQv$bAGOZXI`?Np`w92NQ#?B0$oZW2S<-O|Fpv(2Ga@$jz4S(kZ6)aDZR%4iLwC_?zl>ZFLP2DeLj?DRVUE{jf zuevBhF1O7(Wm6gySocTIGW*1(eV$9B;`;0vTTFg#{NEb(rIT}K=b}Fjb1T;czF%~J zJvXWSr1ruflS&D9_R!+oM%!-7dj|7DSU(*X&**Xg65JJ`^Q1v#>CNO<+zYwBbZ*?d z_~OrH1uj$8{NZA7e<&F^zr*kL^@%T@I81O~v;2y}qg_#cdfAD~_uNU(_{eU)eyed@ zwDF7CzFS)sZke}dh5S?|W=00a#mNSV27JKuA$^-_(6PO5TDh68Aut( zf&}z_U`{Tr)P0b&I|nr_sKF41*b1rWflnou?Fl4P*XQB8W&mqSg(_*WGTVCEAIq&SU!A5i zKTa;{_@z8?`>odZ{nzR@E!O!J>0Z2Kf^W{+uM4@(oIUGM!g1io{qDaxEn(Yi-iNXr zGP$|ec0t*elL9jz?dy3`eVR$v+J9M>tv;{P_T0o(e%l`~mZ~o`;;7pqlOFw=k5@I> eG{HGy@~6JmhcBkqNFSY0=&xpc>Cc;)T}A+=vX}z^ literal 0 HcmV?d00001 diff --git a/src/test/resources/dlsfls/roles_983.yml b/src/test/resources/dlsfls/roles_983.yml index 3ca2c9c..97d0fb1 100644 --- a/src/test/resources/dlsfls/roles_983.yml +++ b/src/test/resources/dlsfls/roles_983.yml @@ -12,6 +12,7 @@ opendistro_security_human_resources_trainee: - 'FirstName' - 'LastName' - 'Salary' + - 'LocalRules' '?kibana': '*': - ALL diff --git a/src/test/resources/dlsfls/roles_ccs2.yml b/src/test/resources/dlsfls/roles_ccs2.yml new file mode 100644 index 0000000..9758f8a --- /dev/null +++ b/src/test/resources/dlsfls/roles_ccs2.yml @@ -0,0 +1,24 @@ +opendistro_security_human_resources_trainee: + cluster: + - '*' + indices: + 'humanresources': + '*': + - READ + - indices:admin/shards/search_shards # needed for CCS + _dls_: '{ "bool": { "must_not": { "match": { "Salary": "salary1" }}}}' + _fls_: + - 'FirstName' + - 'LastName*' + - 'Salary*' + - 'XXX' + - 'CCSRules' + '?kibana': + '*': + - ALL + '*': + '*': + - indices:data/read/field_caps + tenants: + human_resources: RO + performance_data: RO \ No newline at end of file diff --git a/src/test/resources/ldap/base.ldif b/src/test/resources/ldap/base.ldif index 4fe7434..cdf2283 100755 --- a/src/test/resources/ldap/base.ldif +++ b/src/test/resources/ldap/base.ldif @@ -97,10 +97,11 @@ uniqueMember: cn=Michael Jackson,ou=people,o=TEST uniqueMember: cn=Captain Spock,ou=people,o=TEST uniqueMember: cn=hnelson,ou=people,o=TEST uniqueMember: cn=Special\, Sign,ou=people,o=TEST +description: ceo-ceo dn: cn=spec\,group,ou=groups,o=TEST objectClass: groupOfUniqueNames -cn: special2 +cn: special2nest uniqueMember: cn=Special\, Sign,ou=people,o=TEST dn: cn=spec\, groupnest,ou=groups,o=TEST @@ -114,6 +115,7 @@ cn: role2 uniqueMember: cn=Michael Jackson,ou=people,o=TEST uniqueMember: cn=nested1,ou=groups,o=TEST uniqueMember: cn=Non DN Roles,ou=people,o=TEST +description: role2-role2 dn: cn=nested1,ou=groups,o=TEST objectClass: groupOfUniqueNames @@ -161,6 +163,22 @@ description: krb user #krb5PrincipalName: hnelson@EXAMPLE.COM #krb5KeyVersionNumber: 0 +dn: cn=cabc,ou=people,o=TEST +objectclass: inetOrgPerson +cn: xyz +cn: hij +cn: 123 +cn: 456 +cn: abc +cn: aaa +cn: zzz +cn: bbb +cn: acc +sn: multi +uid: multi +userPassword: multi +mail: multi@example.com + dn: cn=krbtgt,ou=people,o=TEST objectclass: inetOrgPerson #objectclass: krb5principal @@ -212,3 +230,35 @@ mail: httploc@example.com description: krb user #krb5PrincipalName: HTTP/localhost@EXAMPLE.COM #krb5KeyVersionNumber: 0 + +dn: CN=AA BB/CC (DD) my\, comp + any end\=with\=whitespace\ ,ou=people,o=TEST +objectclass: inetOrgPerson +cn: "AA BB/CC (DD) my\, company end\=with\=whitespace\ " +sn: spec186 +uid: spec186 +userpassword: spec186 +mail: spec186@example.com + +dn: cn=ROLE/(186) con + sists of\, special\=chars\ ,ou=groups,o=TEST +objectClass: groupOfUniqueNames +description: ROLE/(186) con + sists of\, special= +uniqueMember: CN=AA BB/CC (DD) my\, company end\=with\=whitespace\ ,ou=people,o=TEST + +dn: cn=ROLE/(186n) con + sists of\, special\=chars\ ,ou=groups,o=TEST +objectClass: groupOfUniqueNames +description: ROLEx(186n) con + sists of\, special= +uniqueMember: cn=ROLE/(186) con + sists of\, special\=chars\ ,ou=groups,o=TEST + +dn: cn=ROLE/(186nn) con + sists of\, special\=chars\ ,ou=groups,o=TEST +objectClass: groupOfUniqueNames +description: ROLE/(186nn) con + sists of\, special= +uniqueMember: cn=ROLE/(186n) con + sists of\, special\=chars\ ,ou=groups,o=TEST \ No newline at end of file diff --git a/src/test/resources/ldap/config_ldap2.yml b/src/test/resources/ldap/config_ldap2.yml index 2754008..da79500 100644 --- a/src/test/resources/ldap/config_ldap2.yml +++ b/src/test/resources/ldap/config_ldap2.yml @@ -29,7 +29,7 @@ opendistro_security: http_authenticator: type: basic authentication_backend: - type: com.amazon.dlic.auth.ldap2.LDAPAuthenticationBackend + type: com.amazon.dlic.auth.ldap2.LDAPAuthenticationBackend2 config: hosts: localhost:${ldapsPort} usersearch: "(uid={0})" From 65c0ef8d00d5d60626c9a99534dfbdf6f1ff5ba3 Mon Sep 17 00:00:00 2001 From: Nihal Harish Date: Mon, 22 Apr 2019 11:23:46 -0700 Subject: [PATCH 2/3] final changes --- output.txt | 0 pom.xml | 6 +++--- .../auth/http/kerberos/util/KrbConstants.java | 1 + .../com/amazon/dlic/auth/ldap/util/Utils.java | 2 +- .../security/auditlog/impl/AbstractAuditLog.java | 2 -- .../security/auditlog/impl/AuditLogImpl.java | 16 ++++++++-------- .../security/auditlog/impl/AuditMessage.java | 2 +- .../ComplianceIndexingOperationListenerImpl.java | 2 +- .../compliance/ComplianceAuditlogTest.java | 3 ++- .../RestApiComplianceAuditlogTest.java | 6 ++---- .../auditlog/helper/MockRestRequest.java | 2 +- .../security/util/FakeRestRequest.java | 2 +- 12 files changed, 21 insertions(+), 23 deletions(-) create mode 100644 output.txt diff --git a/output.txt b/output.txt new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml index e5c008b..e8d2868 100755 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ com.amazon.opendistroforelasticsearch opendistro_security_parent - 0.8.0.0 + 0.8.0.1 opendistro_security_advanced_modules @@ -34,7 +34,7 @@ 0.8.0.1 - 6.6.2 + 6.7.1 2.11.1 @@ -54,7 +54,7 @@ https://github.com/opendistro-for-elasticsearch/security-advanced-modules scm:git:git@github.com:opendistro-for-elasticsearch/security-advanced-modules.git scm:git:git@github.com:opendistro-for-elasticsearch/security-advanced-modules.git - v0.8.0.0 + v0.8.0.1 diff --git a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java index 13808ae..5b3d334 100644 --- a/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java +++ b/src/main/java/com/amazon/dlic/auth/http/kerberos/util/KrbConstants.java @@ -24,6 +24,7 @@ public final class KrbConstants { Oid spnegoTmp = null; Oid krbTmp = null; try { + spnegoTmp = new Oid("1.3.6.1.5.5.2"); krbTmp = new Oid("1.2.840.113554.1.2.2"); } catch (final GSSException e) { diff --git a/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java b/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java index 9d4bd8a..6873ef6 100644 --- a/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java +++ b/src/main/java/com/amazon/dlic/auth/ldap/util/Utils.java @@ -65,7 +65,7 @@ public Object run() throws Exception { } public static List> getOrderedBaseSettings(Settings settings) { - return getOrderedBaseSettings(settings.getAsGroups(true)); + return getOrderedBaseSettings(settings.getAsGroups()); } public static List> getOrderedBaseSettings(Map settingsMap) { diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AbstractAuditLog.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AbstractAuditLog.java index 974383d..d7d16fc 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AbstractAuditLog.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AbstractAuditLog.java @@ -816,7 +816,6 @@ private boolean checkComplianceFilter(final Category category, final String effe if(log.isTraceEnabled()) { log.trace("Skipped compliance log message because of user {} is ignored", effectiveUser); } - return false; } } @@ -828,7 +827,6 @@ private boolean checkComplianceFilter(final Category category, final String effe if(log.isTraceEnabled()) { log.trace("Skipped compliance log message because of user {} is ignored", effectiveUser); } - return false; } } diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java index 8da650e..0919054 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditLogImpl.java @@ -101,30 +101,30 @@ protected void save(final AuditMessage msg) { } @Override - public void logFailedLogin(String effectiveUser, boolean sgadmin, String initiatingUser, TransportRequest request, Task task) { + public void logFailedLogin(String effectiveUser, boolean securityAdmin, String initiatingUser, TransportRequest request, Task task) { if (enabled) { - super.logFailedLogin(effectiveUser, sgadmin, initiatingUser, request, task); + super.logFailedLogin(effectiveUser, securityAdmin, initiatingUser, request, task); } } @Override - public void logFailedLogin(String effectiveUser, boolean sgadmin, String initiatingUser, RestRequest request) { + public void logFailedLogin(String effectiveUser, boolean securityAdmin, String initiatingUser, RestRequest request) { if (enabled) { - super.logFailedLogin(effectiveUser, sgadmin, initiatingUser, request); + super.logFailedLogin(effectiveUser, securityAdmin, initiatingUser, request); } } @Override - public void logSucceededLogin(String effectiveUser, boolean sgadmin, String initiatingUser, TransportRequest request, String action, Task task) { + public void logSucceededLogin(String effectiveUser, boolean securityAdmin, String initiatingUser, TransportRequest request, String action, Task task) { if (enabled) { - super.logSucceededLogin(effectiveUser, sgadmin, initiatingUser, request, action, task); + super.logSucceededLogin(effectiveUser, securityAdmin, initiatingUser, request, action, task); } } @Override - public void logSucceededLogin(String effectiveUser, boolean sgadmin, String initiatingUser, RestRequest request) { + public void logSucceededLogin(String effectiveUser, boolean securityAdmin, String initiatingUser, RestRequest request) { if (enabled) { - super.logSucceededLogin(effectiveUser, sgadmin, initiatingUser, request); + super.logSucceededLogin(effectiveUser, securityAdmin, initiatingUser, request); } } diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditMessage.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditMessage.java index 6ee9dc1..cad4cb2 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditMessage.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/auditlog/impl/AuditMessage.java @@ -440,7 +440,7 @@ public static enum Category { COMPLIANCE_DOC_WRITE, COMPLIANCE_EXTERNAL_CONFIG, COMPLIANCE_INTERNAL_CONFIG_READ, - COMPLIANCE_INTERNAL_CONFIG_WRITE; + COMPLIANCE_INTERNAL_CONFIG_WRITE } } diff --git a/src/main/java/com/amazon/opendistroforelasticsearch/security/compliance/ComplianceIndexingOperationListenerImpl.java b/src/main/java/com/amazon/opendistroforelasticsearch/security/compliance/ComplianceIndexingOperationListenerImpl.java index d4691f9..9c811d6 100644 --- a/src/main/java/com/amazon/opendistroforelasticsearch/security/compliance/ComplianceIndexingOperationListenerImpl.java +++ b/src/main/java/com/amazon/opendistroforelasticsearch/security/compliance/ComplianceIndexingOperationListenerImpl.java @@ -98,7 +98,7 @@ public Index preIndex(final ShardId shardId, final Index index) { try { final GetResult getResult = shard.getService().getForUpdate(index.type(), index.id(), - index.version(), index.versionType()); + index.version(), index.versionType(), index.getIfSeqNo(), index.getIfPrimaryTerm()); if (getResult.isExists()) { threadContext.set(new Context(getResult)); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java index a7a30c9..fe4a332 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/ComplianceAuditlogTest.java @@ -15,6 +15,7 @@ package com.amazon.opendistroforelasticsearch.security.auditlog.compliance; +import com.amazon.opendistroforelasticsearch.security.auditlog.impl.AuditMessage; import org.apache.http.Header; import org.apache.http.HttpStatus; import org.elasticsearch.action.index.IndexRequest; @@ -175,7 +176,6 @@ public void testInternalConfig() throws Exception { setup(additionalSettings); try (TransportClient tc = getInternalTransportClient()) { - for(IndexRequest ir: new DynamicSecurityConfig().setSecurityRoles("roles_2.yml").getDynamicConfig(getResourceFolder())) { tc.index(ir).actionGet(); } @@ -185,6 +185,7 @@ public void testInternalConfig() throws Exception { HttpResponse response = rh.executeGetRequest("_search?pretty", encodeBasicHeader("admin", "admin")); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Thread.sleep(1500); + System.out.println(TestAuditlogImpl.sb.toString()); Assert.assertTrue(TestAuditlogImpl.messages.size() > 25); Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_READ")); Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("COMPLIANCE_INTERNAL_CONFIG_WRITE")); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java index 60a0e0e..a708cdd 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/compliance/RestApiComplianceAuditlogTest.java @@ -15,6 +15,7 @@ package com.amazon.opendistroforelasticsearch.security.auditlog.compliance; +import com.amazon.opendistroforelasticsearch.security.auditlog.impl.AuditMessage; import org.apache.http.HttpStatus; import org.elasticsearch.common.settings.Settings; import org.junit.Assert; @@ -193,6 +194,7 @@ public void testRestInternalConfigRead() throws Exception { Settings additionalSettings = Settings.builder() .put("opendistro_security.audit.type", TestAuditlogImpl.class.getName()) .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_TRANSPORT, true) + .put(ConfigConstants.OPENDISTRO_SECURITY_RESTAPI_ROLES_ENABLED, "opendistro_security_all_access") .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_ENABLE_REST, true) .put(ConfigConstants.OPENDISTRO_SECURITY_AUDIT_RESOLVE_BULK_REQUESTS, false) .put(ConfigConstants.OPENDISTRO_SECURITY_COMPLIANCE_HISTORY_EXTERNAL_CONFIG_ENABLED, false) @@ -212,11 +214,7 @@ public void testRestInternalConfigRead() throws Exception { System.out.println("req"); HttpResponse response = rh.executeGetRequest("_opendistro/_security/api/internalusers/admin?pretty"); Thread.sleep(1500); - System.out.println(" ===== == = = = ====== == ===== ====== ===="); - System.out.println(response.getBody()); - System.out.println(" ===== == = = = ====== == ===== ====== ===="); System.out.println(TestAuditlogImpl.sb.toString()); - System.out.println(TestAuditlogImpl.messages.toString()); Assert.assertEquals(HttpStatus.SC_OK, response.getStatusCode()); Assert.assertTrue(TestAuditlogImpl.messages.size()+"",TestAuditlogImpl.messages.size() == 1); Assert.assertTrue(TestAuditlogImpl.sb.toString().contains("audit_request_effective_user")); diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/helper/MockRestRequest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/helper/MockRestRequest.java index f509b11..707ffe7 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/helper/MockRestRequest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/auditlog/helper/MockRestRequest.java @@ -43,7 +43,7 @@ public boolean hasContent() { } @Override - public BytesReference content() { + protected BytesReference innerContent() { return null; } } \ No newline at end of file diff --git a/src/test/java/com/amazon/opendistroforelasticsearch/security/util/FakeRestRequest.java b/src/test/java/com/amazon/opendistroforelasticsearch/security/util/FakeRestRequest.java index 2b36caa..1b8670a 100644 --- a/src/test/java/com/amazon/opendistroforelasticsearch/security/util/FakeRestRequest.java +++ b/src/test/java/com/amazon/opendistroforelasticsearch/security/util/FakeRestRequest.java @@ -61,7 +61,7 @@ public boolean hasContent() { } @Override - public BytesReference content() { + protected BytesReference innerContent() { return content; } From 964f0d40dcefa25fb1bca4d5760e538a5e656ff6 Mon Sep 17 00:00:00 2001 From: Nihal Harish Date: Mon, 22 Apr 2019 11:40:27 -0700 Subject: [PATCH 3/3] 0.9.0.0 pom update --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index e8d2868..7a4d218 100755 --- a/pom.xml +++ b/pom.xml @@ -20,11 +20,11 @@ com.amazon.opendistroforelasticsearch opendistro_security_parent - 0.8.0.1 + 0.9.0.0 opendistro_security_advanced_modules - 0.8.0.1 + 0.9.0.0 jar Open Distro Security Advanced Modules for Elasticsearch @@ -33,7 +33,7 @@ 2016 - 0.8.0.1 + 0.9.0.0 6.7.1 @@ -54,7 +54,7 @@ https://github.com/opendistro-for-elasticsearch/security-advanced-modules scm:git:git@github.com:opendistro-for-elasticsearch/security-advanced-modules.git scm:git:git@github.com:opendistro-for-elasticsearch/security-advanced-modules.git - v0.8.0.1 + v0.9.0.0