diff --git a/README.adoc b/README.adoc index c11631b..9dbdb7a 100644 --- a/README.adoc +++ b/README.adoc @@ -24,8 +24,8 @@ There are four implementation modules: * cassandra-2.2 - builds against version 2.2.19 * cassandra-3.0 - builds against version 3.0.23 -* cassandra-3.11 - builds against version 3.11.9 -* cassandra-4.0 - builds against version 4.0-beta3 +* cassandra-3.11 - builds against version 3.11.10 +* cassandra-4.0 - builds against version 4.0-beta4 Project is built as: @@ -89,6 +89,21 @@ The content of the configuration file is as follows: |load_ldap_service |defaults to false, if it is true, SPI mechanism will look on class path to load custom implementation of `LDAPUserRetriever`. + +|eligibility_class_name +|defaults to `NoOpLoginEligibilityCheck` + +|eligibility_cassandra_keyspace +|defaults to `login_eligibility` + +|eligibility_cassandra_table +|defaults to `login_eligibility` + +|eligibility_cassandra_user_column +|defaults to `user` + +|eligibility_cassandra_access_column +|defaults to `has_access` |=== @@ -158,6 +173,70 @@ may have a different search filter based on your need, a lot of people use e.g. If you try to log in with `cqlsh -u cn=myuserinldap`, there will be no replacement done and this will be used as a search filter instead. +## Login eligibility checks + +There is an additional mechanims implemented for login eligibility resoultion after user is authenticated +in LDAP. If user is authenticated against LDAP and he is not able to log in (e.g. submitted password is wrong), +this check is bypassed. However, even if a user in LDAP is able to log in, Cassandra database administrators +can use additional checks to decide if a user who passed LDAP authentication procedure is indeed able to log in or not. + +The login eligibility check implementation is driven by the configuration property `eligibility_class_name` which +defaults to the no-op implementation which means that login to LDAP will make such user eligible to log in to Cassandra +without any further restrictions / checks. + +There is a default Cassandra implementation provided which might be used by setting `eligibility_class_name` to +`com.instaclustr.cassandra.ldap.auth.Cassandra311LoginEligibilityCheck` for C* 3.11. If you are running Cassandra 4.0, please +set this property to `com.instaclustr.cassandra.ldap.auth.Cassandra40LoginEligibilityCheck`. +IF you use 3.0 or 2.2, use `CassandraLoginEligibilityCheck`. + +Before using this feature (when you are using Cassandra check), respective keyspace +and table need to be created which will capture eligibility data. The default script +is located in `conf/eligibility_check.cql`. Please keep in mind that you might +alter this keyspace to e.g. reflect your replication strategy requirements - this duty is +left to Cassandra operator. + +Operator is responsible for the population of this table, plugin just reads from this table +and it expects that such record is found. For example, let's say that operator inserted this record: + +---- +cqlsh> INSERT INTO login_eligibility.login_eligibility (user , has_access ) VALUES ( 'cn=stefan,dc=example,dc=org', true) USING TTL 60; +cqlsh> select user, has_access, ttl(has_access) from login_eligibility.login_eligibility where user = 'stefan'; + + user | has_access | ttl(has_access) +-----------------------------+------------+----------------- + cn=stefan,dc=example,dc=org | True | 55 + +(1 rows) +---- + +The default Cassandra check implementation will do this check upon login: + +---- +clqsh> select user, has_access from login_eligibility.login_eligibility where user = 'cn=stefan,dc=example,dc=org'; +---- + +Then the plugin looks into `has_access` and it has to be `true`. + +TTL is optional, here it is used with advantage that a user is eligible to be logged in +just for 1 minute. There might be e.g. some company policy to enable a user to log in +during 2 days, for example, which would mimic this policy. After 2 days, such record +is not present in database anymore which renders a user to be unable to log in. + +If you want to use custom eligibility check implementation, you need to firstly implement the interface +`com.instaclustr.cassandra.ldap.auth.LoginEligibilityCheck`, then you need to +create a JAR with this class and put it on Cassandra's class path. In your JAR, +you need to specify your implemenatation in `src/main/resources/META-INF/services` where you need +to put a file with name of FQCN of interface and its content will be FQCN of your implementation which +implements this interface. + +If you do not want to use SPI mechanism mentioned above, you still have to put your +implementation on the class path but you have to specify your implementation +in `eligibility_class_name` configuration property. + +If you want to use different configuration properties for your custom implementation, you have them +available in interfaces' method `init` where they are passed from plugin internals upon +its setup. + ## How it Works LDAPAuthenticator currently supports plain text authorization requests only in the form of a username and password. diff --git a/base/pom.xml b/base/pom.xml index bd9ee79..e98e53c 100644 --- a/base/pom.xml +++ b/base/pom.xml @@ -16,7 +16,7 @@ --> cassandra-ldap-base - 1.1.1 + 1.2.0 Cassandra LDAP Authenticator common code Common code for Apache Cassandra LDAP plugin diff --git a/base/src/main/java/com/instaclustr/cassandra/ldap/auth/BaseCassandraLoginEligibilityCheck.java b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/BaseCassandraLoginEligibilityCheck.java new file mode 100644 index 0000000..7ad5bf5 --- /dev/null +++ b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/BaseCassandraLoginEligibilityCheck.java @@ -0,0 +1,85 @@ +package com.instaclustr.cassandra.ldap.auth; + +import static com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_ACCESS_COLUMN; +import static com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_KEYSPACE; +import static com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_TABLE; +import static com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_USER_COLUMN; + +import java.util.Properties; + +import com.instaclustr.cassandra.ldap.User; +import com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration; +import org.apache.cassandra.serializers.BooleanSerializer; +import org.apache.cassandra.service.ClientState; +import org.apache.cassandra.transport.messages.ResultMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public abstract class BaseCassandraLoginEligibilityCheck implements LoginEligibilityCheck +{ + private static final Logger logger = LoggerFactory.getLogger(BaseCassandraLoginEligibilityCheck.class); + + private static final String BASE_SELECT_USER_STATEMENT_TEMPLATE = "select %s from %s.%s where %s = ?"; + + protected ClientState clientState; + protected Properties configProperties; + protected String selectStatement; + + @Override + public void init(final ClientState clientState, final Properties configProperties) + { + this.clientState = clientState; + this.configProperties = configProperties; + + this.selectStatement = String.format(BASE_SELECT_USER_STATEMENT_TEMPLATE, + configProperties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_ACCESS_COLUMN), + configProperties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_KEYSPACE), + configProperties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_TABLE), + configProperties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_USER_COLUMN)); + + } + + protected abstract ResultMessage.Rows getRows(final String loginName); + + @Override + public boolean isEligibleToLogin(final User user, final String loginName) + { + + // all non-ldap users are free to log in just fine + if (user.getLdapDN() == null) + { + return true; + } + + assert clientState != null; + + final ResultMessage.Rows rows = getRows(loginName); + + final boolean noResults = rows.result.isEmpty(); + + if (noResults) + { + logger.debug(String.format("User with login name '%s' is not eligible to be logged in!", loginName)); + return false; + } + + if (rows.result.size() != 1) + { + throw new IllegalStateException("There was more than one record returned from eligibility check select query!"); + } + + if (rows.result.rows.get(0).size() != 1) + { + throw new IllegalStateException("There was more than one column returned from eligibility check select query!"); + } + + if (BooleanSerializer.instance.deserialize(rows.result.rows.get(0).get(0))) + { + logger.debug(String.format("User with login name '%s' is eligible to be logged in!", loginName)); + return true; + } + + logger.debug(String.format("User with login name '%s' is not eligible to be logged in!", loginName)); + return false; + } +} diff --git a/base/src/main/java/com/instaclustr/cassandra/ldap/auth/CassandraLoginEligibilityCheck.java b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/CassandraLoginEligibilityCheck.java new file mode 100644 index 0000000..f4b5bdf --- /dev/null +++ b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/CassandraLoginEligibilityCheck.java @@ -0,0 +1,20 @@ +package com.instaclustr.cassandra.ldap.auth; + +import static java.util.Collections.singletonList; + +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.service.QueryState; +import org.apache.cassandra.transport.messages.ResultMessage.Rows; +import org.apache.cassandra.utils.ByteBufferUtil; + +public class CassandraLoginEligibilityCheck extends BaseCassandraLoginEligibilityCheck +{ + @Override + protected Rows getRows(final String loginName) + { + final SelectStatement selStmt = (SelectStatement) QueryProcessor.getStatement(selectStatement, clientState).statement; + return selStmt.execute(new QueryState(clientState), QueryOptions.forInternalCalls(singletonList(ByteBufferUtil.bytes(loginName)))); + } +} diff --git a/base/src/main/java/com/instaclustr/cassandra/ldap/auth/LoginEligibilityCheck.java b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/LoginEligibilityCheck.java new file mode 100644 index 0000000..4823357 --- /dev/null +++ b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/LoginEligibilityCheck.java @@ -0,0 +1,15 @@ +package com.instaclustr.cassandra.ldap.auth; + +import java.util.Properties; + +import com.instaclustr.cassandra.ldap.User; +import org.apache.cassandra.service.ClientState; + +public interface LoginEligibilityCheck +{ + + void init(final ClientState clientState, final Properties configProperties); + + boolean isEligibleToLogin(final User user, final String loginName); + +} diff --git a/base/src/main/java/com/instaclustr/cassandra/ldap/auth/NoOpLoginEligibilityCheck.java b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/NoOpLoginEligibilityCheck.java new file mode 100644 index 0000000..04e218d --- /dev/null +++ b/base/src/main/java/com/instaclustr/cassandra/ldap/auth/NoOpLoginEligibilityCheck.java @@ -0,0 +1,22 @@ +package com.instaclustr.cassandra.ldap.auth; + +import java.util.Properties; + +import com.instaclustr.cassandra.ldap.User; +import org.apache.cassandra.service.ClientState; + +public final class NoOpLoginEligibilityCheck implements LoginEligibilityCheck +{ + + @Override + public void init(final ClientState clientState, final Properties properties) + { + + } + + @Override + public boolean isEligibleToLogin(final User user, final String loginName) + { + return true; + } +} diff --git a/base/src/main/java/com/instaclustr/cassandra/ldap/conf/LdapAuthenticatorConfiguration.java b/base/src/main/java/com/instaclustr/cassandra/ldap/conf/LdapAuthenticatorConfiguration.java index db591f1..34a9774 100644 --- a/base/src/main/java/com/instaclustr/cassandra/ldap/conf/LdapAuthenticatorConfiguration.java +++ b/base/src/main/java/com/instaclustr/cassandra/ldap/conf/LdapAuthenticatorConfiguration.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.Properties; +import com.instaclustr.cassandra.ldap.auth.NoOpLoginEligibilityCheck; import org.apache.cassandra.exceptions.ConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -60,6 +61,19 @@ public final class LdapAuthenticatorConfiguration public static final String CASSANDRA_LDAP_ADMIN_USER = "cassandra.ldap.admin.user"; + public static final String ELIGIBILITY_CHECK_CLASS_NAME = "eligibility_class_name"; + public static final String DEFAULT_ELIGIBILITY_CHECK_CLASS_NAME = NoOpLoginEligibilityCheck.class.getCanonicalName(); + + public static final String CASSANDRA_ELIGIBILITY_CHECK_KEYSPACE = "eligibility_cassandra_keyspace"; + public static final String CASSANDRA_ELIGIBILITY_CHECK_TABLE = "eligibility_cassandra_table"; + public static final String CASSANDRA_ELIGIBILITY_CHECK_USER_COLUMN = "eligibility_cassandra_user_column"; + public static final String CASSANDRA_ELIGIBILITY_CHECK_ACCESS_COLUMN = "eligibility_cassandra_access_column"; + + public static final String DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_KEYSPACE = "login_eligibility"; + public static final String DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_TABLE = "login_eligibility"; + public static final String DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_USER_COLUMN = "user"; + public static final String DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_ACCESS_COLUMN = "has_access"; + public static final String CONSISTENCY_FOR_ROLE = "consistency_for_role"; public static final String DEFAULT_CONSISTENCY_FOR_ROLE = "LOCAL_ONE"; @@ -139,10 +153,22 @@ public Properties parseProperties() throws ConfigurationException properties.setProperty(FILTER_TEMPLATE, filterTemplate); + properties.setProperty(LdapAuthenticatorConfiguration.CONTEXT_FACTORY_PROP, properties.getProperty(CONTEXT_FACTORY_PROP, DEFAULT_CONTEXT_FACTORY)); + properties.setProperty(LdapAuthenticatorConfiguration.LDAP_URI_PROP, properties.getProperty(LDAP_URI_PROP)); + + properties.setProperty(LdapAuthenticatorConfiguration.ELIGIBILITY_CHECK_CLASS_NAME, properties.getProperty(ELIGIBILITY_CHECK_CLASS_NAME, DEFAULT_ELIGIBILITY_CHECK_CLASS_NAME)); + + properties.setProperty(LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_KEYSPACE, + properties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_KEYSPACE, DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_KEYSPACE)); + + properties.setProperty(LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_TABLE, + properties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_TABLE, DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_TABLE)); + properties.setProperty(LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_USER_COLUMN, + properties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_USER_COLUMN, DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_USER_COLUMN)); - properties.put(LdapAuthenticatorConfiguration.CONTEXT_FACTORY_PROP, properties.getProperty(CONTEXT_FACTORY_PROP, DEFAULT_CONTEXT_FACTORY)); - properties.put(LdapAuthenticatorConfiguration.LDAP_URI_PROP, properties.getProperty(LDAP_URI_PROP)); + properties.setProperty(LdapAuthenticatorConfiguration.CASSANDRA_ELIGIBILITY_CHECK_ACCESS_COLUMN, + properties.getProperty(CASSANDRA_ELIGIBILITY_CHECK_ACCESS_COLUMN, DEFAULT_CASSANDRA_ELIGIBILITY_CHECK_ACCESS_COLUMN)); return properties; } diff --git a/base/src/main/java/com/instaclustr/cassandra/ldap/utils/ServiceUtils.java b/base/src/main/java/com/instaclustr/cassandra/ldap/utils/ServiceUtils.java index b2dbb37..eb07522 100644 --- a/base/src/main/java/com/instaclustr/cassandra/ldap/utils/ServiceUtils.java +++ b/base/src/main/java/com/instaclustr/cassandra/ldap/utils/ServiceUtils.java @@ -34,6 +34,20 @@ public class ServiceUtils private static final Logger logger = LoggerFactory.getLogger(ServiceUtils.class); + public static T getServiceFromConfig(final Class clazz, + final String classNameFromConfig) + { + Class defaultImplClazz; + + try { + defaultImplClazz = Class.forName(classNameFromConfig); + } catch (final ClassNotFoundException e) { + throw new IllegalStateException(format("Could not find class %s", classNameFromConfig)); + } + + return getService(clazz, (Class) defaultImplClazz); + } + public static T getService(final Class clazz, final Class defaultImplClazz) { final ServiceLoader loader = ServiceLoader.load(clazz); diff --git a/base/src/main/java/org/apache/cassandra/auth/LDAPAuthenticator.java b/base/src/main/java/org/apache/cassandra/auth/LDAPAuthenticator.java index 56355c5..aa429a4 100644 --- a/base/src/main/java/org/apache/cassandra/auth/LDAPAuthenticator.java +++ b/base/src/main/java/org/apache/cassandra/auth/LDAPAuthenticator.java @@ -19,7 +19,9 @@ import static com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration.CASSANDRA_AUTH_CACHE_ENABLED_PROP; import static com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration.CASSANDRA_LDAP_ADMIN_USER; +import static com.instaclustr.cassandra.ldap.conf.LdapAuthenticatorConfiguration.ELIGIBILITY_CHECK_CLASS_NAME; import static com.instaclustr.cassandra.ldap.utils.ServiceUtils.getService; +import static com.instaclustr.cassandra.ldap.utils.ServiceUtils.getServiceFromConfig; import static java.lang.Boolean.parseBoolean; import static java.lang.String.format; @@ -29,6 +31,7 @@ import com.google.common.util.concurrent.UncheckedExecutionException; import com.google.common.util.concurrent.Uninterruptibles; import com.instaclustr.cassandra.ldap.AbstractLDAPAuthenticator; +import com.instaclustr.cassandra.ldap.auth.LoginEligibilityCheck; import com.instaclustr.cassandra.ldap.PlainTextSaslAuthenticator; import com.instaclustr.cassandra.ldap.User; import com.instaclustr.cassandra.ldap.auth.CassandraUserRetriever; @@ -64,6 +67,7 @@ public class LDAPAuthenticator extends AbstractLDAPAuthenticator private static final Logger logger = LoggerFactory.getLogger(AbstractLDAPAuthenticator.class); protected CacheDelegate cacheDelegate; + protected LoginEligibilityCheck loginEligibilityCheck; public void setup() { @@ -85,6 +89,9 @@ public void setup() cacheDelegate = getService(CacheDelegate.class, null); + loginEligibilityCheck = getServiceFromConfig(LoginEligibilityCheck.class, properties.getProperty(ELIGIBILITY_CHECK_CLASS_NAME)); + loginEligibilityCheck.init(clientState, properties); + final String adminRole = System.getProperty(CASSANDRA_LDAP_ADMIN_USER, "cassandra"); while (true) @@ -166,9 +173,16 @@ public AuthenticatedUser authenticate(String username, String password) throws A final String loginName = cachedUser.getLdapDN() == null ? cachedUser.getUsername() : cachedUser.getLdapDN(); - logger.debug("Going to log in with {}", loginName); - - return new AuthenticatedUser(loginName); + if (loginEligibilityCheck.isEligibleToLogin(cachedUser, loginName)) + { + logger.debug("Going to log in with {}", loginName); + return new AuthenticatedUser(loginName); + } + else + { + throw new AuthenticationException(String.format("User %s is authenticated against LDAP fine but it is not able " + + "to log in based on an eligibility check.", loginName)); + } } } catch (final UncheckedExecutionException ex) { diff --git a/cassandra-2.2/pom.xml b/cassandra-2.2/pom.xml index 4070851..fe4ba36 100644 --- a/cassandra-2.2/pom.xml +++ b/cassandra-2.2/pom.xml @@ -11,7 +11,7 @@ cassandra-ldap-2.2 - 1.1.1 + 1.2.0 Cassandra LDAP Authenticator for Cassandra 2.2 Pluggable LDAP authentication implementation for Apache Cassandra 2.2 @@ -50,7 +50,7 @@ com.instaclustr cassandra-ldap-base - 1.1.1 + 1.2.0 org.apache.cassandra @@ -93,7 +93,7 @@ cassandra-ldap-base tests test-jar - 1.1.1 + 1.2.0 test diff --git a/cassandra-2.2/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra22LDAPIntegrationTest.java b/cassandra-2.2/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra22LDAPIntegrationTest.java index 59736e0..3a40248 100644 --- a/cassandra-2.2/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra22LDAPIntegrationTest.java +++ b/cassandra-2.2/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra22LDAPIntegrationTest.java @@ -41,7 +41,7 @@ public List createPluginJars() throws IOException { File[] singleFile = Maven.resolver() .loadPomFromFile("pom.xml") - .resolve("com.instaclustr:cassandra-ldap-2.2:1.1.1") + .resolve("com.instaclustr:cassandra-ldap-2.2:1.2.0") .withTransitivity() .asFile(); diff --git a/cassandra-3.0/pom.xml b/cassandra-3.0/pom.xml index 0df5675..87bc7b7 100644 --- a/cassandra-3.0/pom.xml +++ b/cassandra-3.0/pom.xml @@ -11,7 +11,7 @@ cassandra-ldap-3.0 - 1.1.1 + 1.2.0 Cassandra LDAP Authenticator for Cassandra 3.0 Pluggable LDAP authentication implementation for Apache Cassandra 3.0 @@ -50,7 +50,7 @@ com.instaclustr cassandra-ldap-base - 1.1.1 + 1.2.0 org.apache.cassandra @@ -81,7 +81,7 @@ cassandra-ldap-base tests test-jar - 1.1.1 + 1.2.0 test diff --git a/cassandra-3.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra30LDAPIntegrationTest.java b/cassandra-3.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra30LDAPIntegrationTest.java index 9529344..c4ac7ac 100644 --- a/cassandra-3.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra30LDAPIntegrationTest.java +++ b/cassandra-3.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra30LDAPIntegrationTest.java @@ -38,7 +38,7 @@ public List createPluginJars() throws IOException { File[] singleFile = Maven.resolver() .loadPomFromFile("pom.xml") - .resolve("com.instaclustr:cassandra-ldap-3.0:1.1.1") + .resolve("com.instaclustr:cassandra-ldap-3.0:1.2.0") .withTransitivity() .asFile(); diff --git a/cassandra-3.11/pom.xml b/cassandra-3.11/pom.xml index 715a63d..0c85982 100644 --- a/cassandra-3.11/pom.xml +++ b/cassandra-3.11/pom.xml @@ -11,7 +11,7 @@ cassandra-ldap-3.11 - 1.1.1 + 1.2.0 Cassandra LDAP Authenticator for Cassandra 3.11 Pluggable LDAP authentication implementation for Apache Cassandra 3.11 @@ -51,7 +51,7 @@ com.instaclustr cassandra-ldap-base - 1.1.1 + 1.2.0 org.apache.cassandra @@ -116,7 +116,7 @@ cassandra-ldap-base tests test-jar - 1.1.1 + 1.2.0 test diff --git a/cassandra-3.11/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra311LoginEligibilityCheck.java b/cassandra-3.11/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra311LoginEligibilityCheck.java new file mode 100644 index 0000000..ca1d2dd --- /dev/null +++ b/cassandra-3.11/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra311LoginEligibilityCheck.java @@ -0,0 +1,22 @@ +package com.instaclustr.cassandra.ldap.auth; + +import static java.util.Collections.singletonList; + +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.service.QueryState; +import org.apache.cassandra.transport.messages.ResultMessage.Rows; +import org.apache.cassandra.utils.ByteBufferUtil; + +public class Cassandra311LoginEligibilityCheck extends BaseCassandraLoginEligibilityCheck +{ + @Override + protected Rows getRows(final String loginName) + { + final SelectStatement selStmt = (SelectStatement) QueryProcessor.getStatement(selectStatement, clientState).statement; + return selStmt.execute(new QueryState(clientState), + QueryOptions.forInternalCalls(singletonList(ByteBufferUtil.bytes(loginName))), + System.nanoTime()); + } +} diff --git a/cassandra-3.11/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra311LDAPIntegrationTest.java b/cassandra-3.11/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra311LDAPIntegrationTest.java index 1762c83..d996e26 100644 --- a/cassandra-3.11/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra311LDAPIntegrationTest.java +++ b/cassandra-3.11/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra311LDAPIntegrationTest.java @@ -38,7 +38,7 @@ public List createPluginJars() throws IOException { File[] singleFile = Maven.resolver() .loadPomFromFile("pom.xml") - .resolve("com.instaclustr:cassandra-ldap-3.11:1.1.1") + .resolve("com.instaclustr:cassandra-ldap-3.11:1.2.0") .withTransitivity() .asFile(); diff --git a/cassandra-4.0/pom.xml b/cassandra-4.0/pom.xml index e24c1a9..f4e9884 100644 --- a/cassandra-4.0/pom.xml +++ b/cassandra-4.0/pom.xml @@ -11,7 +11,7 @@ cassandra-ldap-4.0 - 1.1.1 + 1.2.0 Cassandra LDAP Authenticator for Cassandra 4.0 Pluggable LDAP authentication implementation for Apache Cassandra 4.0 @@ -51,7 +51,7 @@ com.instaclustr cassandra-ldap-base - 1.1.1 + 1.2.0 org.apache.cassandra @@ -116,7 +116,7 @@ cassandra-ldap-base tests test-jar - 1.1.1 + 1.2.0 test diff --git a/cassandra-4.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra40LoginEligibilityCheck.java b/cassandra-4.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra40LoginEligibilityCheck.java new file mode 100644 index 0000000..cadec53 --- /dev/null +++ b/cassandra-4.0/src/main/java/com/instaclustr/cassandra/ldap/auth/Cassandra40LoginEligibilityCheck.java @@ -0,0 +1,22 @@ +package com.instaclustr.cassandra.ldap.auth; + +import static java.util.Collections.singletonList; + +import org.apache.cassandra.cql3.QueryOptions; +import org.apache.cassandra.cql3.QueryProcessor; +import org.apache.cassandra.cql3.statements.SelectStatement; +import org.apache.cassandra.service.QueryState; +import org.apache.cassandra.transport.messages.ResultMessage.Rows; +import org.apache.cassandra.utils.ByteBufferUtil; + +public class Cassandra40LoginEligibilityCheck extends BaseCassandraLoginEligibilityCheck +{ + @Override + protected Rows getRows(final String loginName) + { + final SelectStatement selStmt = (SelectStatement) QueryProcessor.getStatement(selectStatement, clientState); + return selStmt.execute(new QueryState(clientState), + QueryOptions.forInternalCalls(singletonList(ByteBufferUtil.bytes(loginName))), + System.nanoTime()); + } +} diff --git a/cassandra-4.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra40LDAPIntegrationTest.java b/cassandra-4.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra40LDAPIntegrationTest.java index 10ec919..c98d099 100644 --- a/cassandra-4.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra40LDAPIntegrationTest.java +++ b/cassandra-4.0/src/test/java/com/instaclustr/cassandra/ldap/auth/Cassandra40LDAPIntegrationTest.java @@ -38,7 +38,7 @@ public List createPluginJars() throws IOException { File[] singleFile = Maven.resolver() .loadPomFromFile("pom.xml") - .resolve("com.instaclustr:cassandra-ldap-4.0:1.1.1") + .resolve("com.instaclustr:cassandra-ldap-4.0:1.2.0") .withTransitivity() .asFile(); diff --git a/conf/eligibility_check.cql b/conf/eligibility_check.cql new file mode 100644 index 0000000..b96fd91 --- /dev/null +++ b/conf/eligibility_check.cql @@ -0,0 +1,4 @@ +-- change datacenter replication and strategy as you please to suite your needs + +CREATE KEYSPACE IF NOT EXISTS login_eligibility WITH replication = {'class': 'NetworkTopologyStrategy', 'datacenter1': 1}; +CREATE TABLE IF NOT EXISTS login_eligibility.login_eligibility (user text, has_access boolean, PRIMARY KEY (user)); \ No newline at end of file diff --git a/conf/ldap.properties b/conf/ldap.properties index 992845d..e9a0484 100644 --- a/conf/ldap.properties +++ b/conf/ldap.properties @@ -23,7 +23,16 @@ filter_template: (cn=%s) # if you set this property, Cassandra will internally consider 'dba` to be same as 'cassandra'. # so you might get rid of `cassandra` role (not recommended) or you might make it unable to log in at least. # You need to create this admin role beforehand, it has to be super user. -cassandra_ldap_admin_user=dba +#cassandra_ldap_admin_user=dba # consistency level to use for retrieval of a role to check if it can log in - defaults to LOCAL_ONE -#consistency_for_role: LOCAL_ONE \ No newline at end of file +#consistency_for_role: LOCAL_ONE + +# Property telling what implementation of eligibility check you want, defaults to NoOp check when not specified +#eligibility_class_name: com.instaclustr.cassandra.ldap.auth.NoOpLoginEligibilityCheck + +# Properties related to Cassandra's eligibility check implementation +#eligibility_cassandra_keyspace: login_eligibility +#eligibility_cassandra_table: login_eligibility +#eligibility_cassandra_user_column: user +#eligibility_cassandra_access_column: has_access \ No newline at end of file diff --git a/pom.xml b/pom.xml index 1bfdd50..6b14db8 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.instaclustr cassandra-ldap-parent - 1.1.1 + 1.2.0 pom