diff --git a/changelog/README.md b/changelog/README.md index b90979e2e71..8b1dad39bcc 100644 --- a/changelog/README.md +++ b/changelog/README.md @@ -23,6 +23,7 @@ under the License. ### 4.19.2 +- [improvement] Remove unused DSE authentication and proxy execution support (#1030) - [bug] CASSJAVA-116: Retry or Speculative Execution with RequestIdGenerator throws "Duplicate Key" ### 4.19.1 diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/BaseDseAuthenticator.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/BaseDseAuthenticator.java deleted file mode 100644 index abd68b530b6..00000000000 --- a/core/src/main/java/com/datastax/dse/driver/api/core/auth/BaseDseAuthenticator.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import com.datastax.oss.driver.api.core.auth.SyncAuthenticator; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.nio.ByteBuffer; -import net.jcip.annotations.ThreadSafe; - -/** - * Base class for {@link SyncAuthenticator} implementations that want to make use of the - * authentication scheme negotiation in DseAuthenticator. - */ -@ThreadSafe -public abstract class BaseDseAuthenticator implements SyncAuthenticator { - - private static final String DSE_AUTHENTICATOR = - "com.datastax.bdp.cassandra.auth.DseAuthenticator"; - - private final String serverAuthenticator; - - protected BaseDseAuthenticator(@NonNull String serverAuthenticator) { - this.serverAuthenticator = serverAuthenticator; - } - - /** - * Return a byte buffer containing the required SASL mechanism. - * - *

This should be one of: - * - *

- * - * This must be either a {@linkplain ByteBuffer#asReadOnlyBuffer() read-only} buffer, or a new - * instance every time. - */ - @NonNull - protected abstract ByteBuffer getMechanism(); - - /** - * Return a byte buffer containing the expected successful server challenge. - * - *

This should be one of: - * - *

- * - * This must be either a {@linkplain ByteBuffer#asReadOnlyBuffer() read-only} buffer, or a new - * instance every time. - */ - @NonNull - protected abstract ByteBuffer getInitialServerChallenge(); - - @Nullable - @Override - public ByteBuffer initialResponseSync() { - // DseAuthenticator communicates back the mechanism in response to server authenticate message. - // older authenticators simply expect the auth response with credentials. - if (isDseAuthenticator()) { - return getMechanism(); - } else { - return evaluateChallengeSync(getInitialServerChallenge()); - } - } - - @Override - public void onAuthenticationSuccessSync(@Nullable ByteBuffer token) {} - - private boolean isDseAuthenticator() { - return serverAuthenticator.equals(DSE_AUTHENTICATOR); - } -} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderBase.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderBase.java deleted file mode 100644 index 48a0e5b0ef3..00000000000 --- a/core/src/main/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderBase.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import com.datastax.oss.driver.api.core.auth.AuthProvider; -import com.datastax.oss.driver.api.core.auth.AuthenticationException; -import com.datastax.oss.driver.api.core.auth.Authenticator; -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import com.datastax.oss.driver.api.core.session.Session; -import com.datastax.oss.driver.shaded.guava.common.base.Charsets; -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; -import com.datastax.oss.protocol.internal.util.Bytes; -import edu.umd.cs.findbugs.annotations.NonNull; -import edu.umd.cs.findbugs.annotations.Nullable; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import javax.security.auth.Subject; -import javax.security.auth.login.AppConfigurationEntry; -import javax.security.auth.login.Configuration; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; -import javax.security.sasl.Sasl; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; -import net.jcip.annotations.Immutable; -import net.jcip.annotations.NotThreadSafe; -import net.jcip.annotations.ThreadSafe; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@ThreadSafe -public abstract class DseGssApiAuthProviderBase implements AuthProvider { - - /** The default SASL service name used by this auth provider. */ - public static final String DEFAULT_SASL_SERVICE_NAME = "dse"; - - /** The name of the system property to use to specify the SASL service name. */ - public static final String SASL_SERVICE_NAME_PROPERTY = "dse.sasl.service"; - - /** - * Legacy system property for SASL protocol name. Clients should migrate to - * SASL_SERVICE_NAME_PROPERTY above. - */ - private static final String LEGACY_SASL_PROTOCOL_PROPERTY = "dse.sasl.protocol"; - - private static final Logger LOG = LoggerFactory.getLogger(DseGssApiAuthProviderBase.class); - - private final String logPrefix; - - /** - * @param logPrefix a string that will get prepended to the logs (this is used for discrimination - * when you have multiple driver instances executing in the same JVM). Config-based - * implementations fill this with {@link Session#getName()}. - */ - protected DseGssApiAuthProviderBase(@NonNull String logPrefix) { - this.logPrefix = Objects.requireNonNull(logPrefix); - } - - @NonNull - protected abstract GssApiOptions getOptions( - @NonNull EndPoint endPoint, @NonNull String serverAuthenticator); - - @NonNull - @Override - public Authenticator newAuthenticator( - @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) - throws AuthenticationException { - return new GssApiAuthenticator( - getOptions(endPoint, serverAuthenticator), endPoint, serverAuthenticator); - } - - @Override - public void onMissingChallenge(@NonNull EndPoint endPoint) { - LOG.warn( - "[{}] {} did not send an authentication challenge; " - + "This is suspicious because the driver expects authentication", - logPrefix, - endPoint); - } - - @Override - public void close() { - // nothing to do - } - - /** - * The options to initialize a new authenticator. - * - *

Use {@link #builder()} to create an instance. - */ - @Immutable - public static class GssApiOptions { - - @NonNull - public static Builder builder() { - return new Builder(); - } - - private final Configuration loginConfiguration; - private final Subject subject; - private final String saslProtocol; - private final String authorizationId; - private final Map saslProperties; - - private GssApiOptions( - @Nullable Configuration loginConfiguration, - @Nullable Subject subject, - @Nullable String saslProtocol, - @Nullable String authorizationId, - @NonNull Map saslProperties) { - this.loginConfiguration = loginConfiguration; - this.subject = subject; - this.saslProtocol = saslProtocol; - this.authorizationId = authorizationId; - this.saslProperties = saslProperties; - } - - @Nullable - public Configuration getLoginConfiguration() { - return loginConfiguration; - } - - @Nullable - public Subject getSubject() { - return subject; - } - - @Nullable - public String getSaslProtocol() { - return saslProtocol; - } - - @Nullable - public String getAuthorizationId() { - return authorizationId; - } - - @NonNull - public Map getSaslProperties() { - return saslProperties; - } - - @NotThreadSafe - public static class Builder { - - private Configuration loginConfiguration; - private Subject subject; - private String saslProtocol; - private String authorizationId; - private final Map saslProperties = new HashMap<>(); - - public Builder() { - saslProperties.put(Sasl.SERVER_AUTH, "true"); - saslProperties.put(Sasl.QOP, "auth"); - } - - /** - * Sets a login configuration that will be used to create a {@link LoginContext}. - * - *

You MUST call either a withLoginConfiguration method or {@link #withSubject(Subject)}; - * if both are called, the subject takes precedence, and the login configuration will be - * ignored. - * - * @see #withLoginConfiguration(Map) - */ - @NonNull - public Builder withLoginConfiguration(@Nullable Configuration loginConfiguration) { - this.loginConfiguration = loginConfiguration; - return this; - } - /** - * Sets a login configuration that will be used to create a {@link LoginContext}. - * - *

This is an alternative to {@link #withLoginConfiguration(Configuration)}, that builds - * the configuration from {@code Krb5LoginModule} with the given options. - * - *

You MUST call either a withLoginConfiguration method or {@link #withSubject(Subject)}; - * if both are called, the subject takes precedence, and the login configuration will be - * ignored. - */ - @NonNull - public Builder withLoginConfiguration(@Nullable Map loginConfiguration) { - this.loginConfiguration = fetchLoginConfiguration(loginConfiguration); - return this; - } - - /** - * Sets a previously authenticated subject to reuse. - * - *

You MUST call either this method or {@link #withLoginConfiguration(Configuration)}; if - * both are called, the subject takes precedence, and the login configuration will be ignored. - */ - @NonNull - public Builder withSubject(@Nullable Subject subject) { - this.subject = subject; - return this; - } - - /** - * Sets the SASL protocol name to use; should match the username of the Kerberos service - * principal used by the DSE server. - */ - @NonNull - public Builder withSaslProtocol(@Nullable String saslProtocol) { - this.saslProtocol = saslProtocol; - return this; - } - - /** Sets the authorization ID (allows proxy authentication). */ - @NonNull - public Builder withAuthorizationId(@Nullable String authorizationId) { - this.authorizationId = authorizationId; - return this; - } - - /** - * Add a SASL property to use when creating the SASL client. - * - *

Note that this builder pre-initializes these two default properties: - * - *

-       * javax.security.sasl.server.authentication = true
-       * javax.security.sasl.qop = auth
-       * 
- */ - @NonNull - public Builder addSaslProperty(@NonNull String name, @NonNull String value) { - this.saslProperties.put(Objects.requireNonNull(name), Objects.requireNonNull(value)); - return this; - } - - @NonNull - public GssApiOptions build() { - return new GssApiOptions( - loginConfiguration, - subject, - saslProtocol, - authorizationId, - ImmutableMap.copyOf(saslProperties)); - } - - public static Configuration fetchLoginConfiguration(Map options) { - return new Configuration() { - - @Override - public AppConfigurationEntry[] getAppConfigurationEntry(String name) { - return new AppConfigurationEntry[] { - new AppConfigurationEntry( - "com.sun.security.auth.module.Krb5LoginModule", - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, - options) - }; - } - }; - } - } - } - - protected static class GssApiAuthenticator extends BaseDseAuthenticator { - - private static final ByteBuffer MECHANISM = - ByteBuffer.wrap("GSSAPI".getBytes(Charsets.UTF_8)).asReadOnlyBuffer(); - private static final ByteBuffer SERVER_INITIAL_CHALLENGE = - ByteBuffer.wrap("GSSAPI-START".getBytes(Charsets.UTF_8)).asReadOnlyBuffer(); - private static final ByteBuffer EMPTY_BYTE_ARRAY = - ByteBuffer.wrap(new byte[0]).asReadOnlyBuffer(); - private static final String JAAS_CONFIG_ENTRY = "DseClient"; - private static final String[] SUPPORTED_MECHANISMS = new String[] {"GSSAPI"}; - - private Subject subject; - private SaslClient saslClient; - private EndPoint endPoint; - - protected GssApiAuthenticator( - GssApiOptions options, EndPoint endPoint, String serverAuthenticator) { - super(serverAuthenticator); - - try { - if (options.getSubject() != null) { - this.subject = options.getSubject(); - } else { - Configuration loginConfiguration = options.getLoginConfiguration(); - if (loginConfiguration == null) { - throw new IllegalArgumentException("Must provide one of subject or loginConfiguration"); - } - LoginContext login = new LoginContext(JAAS_CONFIG_ENTRY, null, null, loginConfiguration); - login.login(); - this.subject = login.getSubject(); - } - String protocol = options.getSaslProtocol(); - if (protocol == null) { - protocol = - System.getProperty( - SASL_SERVICE_NAME_PROPERTY, - System.getProperty(LEGACY_SASL_PROTOCOL_PROPERTY, DEFAULT_SASL_SERVICE_NAME)); - } - this.saslClient = - Sasl.createSaslClient( - SUPPORTED_MECHANISMS, - options.getAuthorizationId(), - protocol, - ((InetSocketAddress) endPoint.resolve()).getAddress().getCanonicalHostName(), - options.getSaslProperties(), - null); - } catch (LoginException | SaslException e) { - throw new AuthenticationException(endPoint, e.getMessage()); - } - this.endPoint = endPoint; - } - - @NonNull - @Override - protected ByteBuffer getMechanism() { - return MECHANISM; - } - - @NonNull - @Override - protected ByteBuffer getInitialServerChallenge() { - return SERVER_INITIAL_CHALLENGE; - } - - @Nullable - @Override - public ByteBuffer evaluateChallengeSync(@Nullable ByteBuffer challenge) { - - byte[] challengeBytes; - if (SERVER_INITIAL_CHALLENGE.equals(challenge)) { - if (!saslClient.hasInitialResponse()) { - return EMPTY_BYTE_ARRAY; - } - challengeBytes = new byte[0]; - } else { - // The native protocol spec says the incoming challenge can be null depending on the - // implementation. But saslClient.evaluateChallenge clearly documents that the byte array - // can't be null, which probably means that a SASL authenticator never sends back null. - if (challenge == null) { - throw new AuthenticationException(this.endPoint, "Unexpected null challenge from server"); - } - challengeBytes = Bytes.getArray(challenge); - } - try { - - return ByteBuffer.wrap( - Subject.doAs( - subject, - new PrivilegedExceptionAction() { - @Override - public byte[] run() throws SaslException { - return saslClient.evaluateChallenge(challengeBytes); - } - })); - } catch (PrivilegedActionException e) { - throw new AuthenticationException(this.endPoint, e.getMessage(), e.getException()); - } - } - } -} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderBase.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderBase.java deleted file mode 100644 index 7c5ee23bd6c..00000000000 --- a/core/src/main/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderBase.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import com.datastax.oss.driver.api.core.auth.PlainTextAuthProviderBase; -import edu.umd.cs.findbugs.annotations.NonNull; -import net.jcip.annotations.ThreadSafe; - -/** - * @deprecated The driver's default plain text providers now support both Apache Cassandra and DSE. - * This type was preserved for backward compatibility, but implementors should now extend {@link - * PlainTextAuthProviderBase} instead. - */ -@ThreadSafe -@Deprecated -public abstract class DsePlainTextAuthProviderBase extends PlainTextAuthProviderBase { - - protected DsePlainTextAuthProviderBase(@NonNull String logPrefix) { - super(logPrefix); - } -} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProgrammaticDseGssApiAuthProvider.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProgrammaticDseGssApiAuthProvider.java deleted file mode 100644 index 64ee5265b5a..00000000000 --- a/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProgrammaticDseGssApiAuthProvider.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import com.datastax.oss.driver.api.core.auth.AuthProvider; -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import edu.umd.cs.findbugs.annotations.NonNull; - -/** - * {@link AuthProvider} that provides GSSAPI authenticator instances for clients to connect to DSE - * clusters secured with {@code DseAuthenticator}, in a programmatic way. - * - *

To use this provider the corresponding GssApiOptions must be passed into the provider - * directly, for example: - * - *

- *     DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
- *         DseGssApiAuthProviderBase.GssApiOptions.builder();
- *     Map<String, String> loginConfig =
- *         ImmutableMap.of(
- *             "principal",
- *             "user principal here ex cassandra@DATASTAX.COM",
- *             "useKeyTab",
- *             "true",
- *             "refreshKrb5Config",
- *             "true",
- *             "keyTab",
- *             "Path to keytab file here");
- *
- *     builder.withLoginConfiguration(loginConfig);
- *
- *     CqlSession session =
- *         CqlSession.builder()
- *             .withAuthProvider(new ProgrammaticDseGssApiAuthProvider(builder.build()))
- *             .build();
- * 
- * - * or alternatively - * - *
- *     DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
- *         DseGssApiAuthProviderBase.GssApiOptions.builder().withSubject(subject);
- *     CqlSession session =
- *         CqlSession.builder()
- *             .withAuthProvider(new ProgrammaticDseGssApiAuthProvider(builder.build()))
- *             .build();
- * 
- * - *

Kerberos Authentication

- * - * Keytab and ticket cache settings are specified using a standard JAAS configuration file. The - * location of the file can be set using the java.security.auth.login.config system - * property or by adding a login.config.url.n entry in the java.security - * properties file. Alternatively a login-configuration, or subject can be provided to the provider - * via the GssApiOptions (see above). - * - *

See the following documents for further details: - * - *

    - *
  1. JAAS - * Login Configuration File; - *
  2. Krb5LoginModule - * options; - *
  3. JAAS - * Authentication Tutorial for more on JAAS in general. - *
- * - *

Authentication using ticket cache

- * - * Run kinit to obtain a ticket and populate the cache before connecting. JAAS config: - * - *
- * DseClient {
- *   com.sun.security.auth.module.Krb5LoginModule required
- *     useTicketCache=true
- *     renewTGT=true;
- * };
- * 
- * - *

Authentication using a keytab file

- * - * To enable authentication using a keytab file, specify its location on disk. If your keytab - * contains more than one principal key, you should also specify which one to select. This - * information can also be specified in the driver config, under the login-configuration section. - * - *
- * DseClient {
- *     com.sun.security.auth.module.Krb5LoginModule required
- *       useKeyTab=true
- *       keyTab="/path/to/file.keytab"
- *       principal="user@MYDOMAIN.COM";
- * };
- * 
- * - *

Specifying SASL protocol name

- * - * The SASL protocol name used by this auth provider defaults to " - * {@value #DEFAULT_SASL_SERVICE_NAME}". - * - *

Important: the SASL protocol name should match the username of the Kerberos - * service principal used by the DSE server. This information is specified in the dse.yaml file by - * the {@code service_principal} option under the kerberos_options - * section, and may vary from one DSE installation to another – especially if you installed - * DSE with an automated package installer. - * - *

For example, if your dse.yaml file contains the following: - * - *

{@code
- * kerberos_options:
- *     ...
- *     service_principal: cassandra/my.host.com@MY.REALM.COM
- * }
- * - * The correct SASL protocol name to use when authenticating against this DSE server is "{@code - * cassandra}". - * - *

Should you need to change the SASL protocol name specify it in the GssApiOptions, use the - * method below: - * - *

- *     DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
- *         DseGssApiAuthProviderBase.GssApiOptions.builder();
- *     builder.withSaslProtocol("alternate");
- *     DseGssApiAuthProviderBase.GssApiOptions options = builder.build();
- * 
- * - *

Should internal sasl properties need to be set such as qop. This can also be accomplished by - * setting it in the GssApiOptions: - * - *

- *   DseGssApiAuthProviderBase.GssApiOptions.Builder builder =
- *         DseGssApiAuthProviderBase.GssApiOptions.builder();
- *     builder.addSaslProperty("javax.security.sasl.qop", "auth-conf");
- *     DseGssApiAuthProviderBase.GssApiOptions options = builder.build();
- * 
- * - * @see Authenticating - * a DSE cluster with Kerberos - */ -public class ProgrammaticDseGssApiAuthProvider extends DseGssApiAuthProviderBase { - private final GssApiOptions options; - - public ProgrammaticDseGssApiAuthProvider(GssApiOptions options) { - super("Programmatic-Kerberos"); - this.options = options; - } - - @NonNull - @Override - protected GssApiOptions getOptions( - @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) { - return options; - } -} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProxyAuthentication.java b/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProxyAuthentication.java deleted file mode 100644 index a3624ba736d..00000000000 --- a/core/src/main/java/com/datastax/dse/driver/api/core/auth/ProxyAuthentication.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import com.datastax.dse.driver.api.core.graph.GraphStatement; -import com.datastax.oss.driver.api.core.cql.Statement; -import com.datastax.oss.driver.shaded.guava.common.base.Charsets; -import com.datastax.oss.protocol.internal.util.collection.NullAllowingImmutableMap; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.nio.ByteBuffer; -import java.util.Map; - -public class ProxyAuthentication { - private static final String PROXY_EXECUTE = "ProxyExecute"; - - /** - * Adds proxy authentication information to a CQL statement. - * - *

This allows executing a statement as another role than the one the session is currently - * authenticated as. - * - * @param userOrRole the role to use for execution. If the statement was already configured with - * another role, it will get replaced by this one. - * @param statement the statement to modify. - * @return a statement that will run the same CQL query as {@code statement}, but acting as the - * provided role. Note: with the driver's default implementations, this will always be a copy; - * but if you use a custom implementation, it might return the same instance (depending on the - * behavior of {@link Statement#setCustomPayload(Map) statement.setCustomPayload()}). - * @see Setting - * up roles for applications (DSE 6.0 admin guide) - */ - @NonNull - public static > StatementT executeAs( - @NonNull String userOrRole, @NonNull StatementT statement) { - return statement.setCustomPayload( - addProxyExecuteEntry(statement.getCustomPayload(), userOrRole)); - } - - /** - * Adds proxy authentication information to a graph statement. - * - * @see #executeAs(String, Statement) - */ - @NonNull - public static > StatementT executeAs( - @NonNull String userOrRole, @NonNull StatementT statement) { - return statement.setCustomPayload( - addProxyExecuteEntry(statement.getCustomPayload(), userOrRole)); - } - - private static Map addProxyExecuteEntry( - Map currentPayload, @NonNull String userOrRole) { - NullAllowingImmutableMap.Builder builder = - NullAllowingImmutableMap.builder(); - builder.put(PROXY_EXECUTE, ByteBuffer.wrap(userOrRole.getBytes(Charsets.UTF_8))); - if (!currentPayload.isEmpty()) { - for (Map.Entry entry : currentPayload.entrySet()) { - String key = entry.getKey(); - if (!key.equals(PROXY_EXECUTE)) { - builder.put(key, entry.getValue()); - } - } - } - return builder.build(); - } -} diff --git a/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverOption.java b/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverOption.java index 4d10501f6d2..f3fe6f31529 100644 --- a/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverOption.java +++ b/core/src/main/java/com/datastax/dse/driver/api/core/config/DseDriverOption.java @@ -34,31 +34,6 @@ public enum DseDriverOption implements DriverOption { */ APPLICATION_VERSION("basic.application.version"), - /** - * Proxy authentication for GSSAPI authentication: allows to login as another user or role. - * - *

Value type: {@link String} - */ - AUTH_PROVIDER_AUTHORIZATION_ID("advanced.auth-provider.authorization-id"), - /** - * Service name for GSSAPI authentication. - * - *

Value type: {@link String} - */ - AUTH_PROVIDER_SERVICE("advanced.auth-provider.service"), - /** - * Login configuration for GSSAPI authentication. - * - *

Value type: {@link java.util.Map Map}<{@link String},{@link String}> - */ - AUTH_PROVIDER_LOGIN_CONFIGURATION("advanced.auth-provider.login-configuration"), - /** - * Internal SASL properties, if any, such as QOP, for GSSAPI authentication. - * - *

Value type: {@link java.util.Map Map}<{@link String},{@link String}> - */ - AUTH_PROVIDER_SASL_PROPERTIES("advanced.auth-provider.sasl-properties"), - /** * The page size for continuous paging. * diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DseGssApiAuthProvider.java b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DseGssApiAuthProvider.java deleted file mode 100644 index 6ef6596a870..00000000000 --- a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DseGssApiAuthProvider.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.internal.core.auth; - -import com.datastax.dse.driver.api.core.auth.DseGssApiAuthProviderBase; -import com.datastax.dse.driver.api.core.config.DseDriverOption; -import com.datastax.oss.driver.api.core.auth.AuthProvider; -import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; -import com.datastax.oss.driver.api.core.context.DriverContext; -import com.datastax.oss.driver.api.core.metadata.EndPoint; -import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.Map; -import net.jcip.annotations.ThreadSafe; - -/** - * {@link AuthProvider} that provides GSSAPI authenticator instances for clients to connect to DSE - * clusters secured with {@code DseAuthenticator}. - * - *

To activate this provider an {@code auth-provider} section must be included in the driver - * configuration, for example: - * - *

- * dse-java-driver {
- *  auth-provider {
- *      class = com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider
- *      login-configuration {
- *          principal = "user principal here ex cassandra@DATASTAX.COM"
- *          useKeyTab = "true"
- *          refreshKrb5Config = "true"
- *          keyTab = "Path to keytab file here"
- *      }
- *   }
- * }
- * 
- * - *

Kerberos Authentication

- * - * Keytab and ticket cache settings are specified using a standard JAAS configuration file. The - * location of the file can be set using the java.security.auth.login.config system - * property or by adding a login.config.url.n entry in the java.security - * properties file. Alternatively a login-configuration section can be included in the driver - * configuration. - * - *

See the following documents for further details: - * - *

    - *
  1. JAAS - * Login Configuration File; - *
  2. Krb5LoginModule - * options; - *
  3. JAAS - * Authentication Tutorial for more on JAAS in general. - *
- * - *

Authentication using ticket cache

- * - * Run kinit to obtain a ticket and populate the cache before connecting. JAAS config: - * - *
- * DseClient {
- *   com.sun.security.auth.module.Krb5LoginModule required
- *     useTicketCache=true
- *     renewTGT=true;
- * };
- * 
- * - *

Authentication using a keytab file

- * - * To enable authentication using a keytab file, specify its location on disk. If your keytab - * contains more than one principal key, you should also specify which one to select. This - * information can also be specified in the driver config, under the login-configuration section. - * - *
- * DseClient {
- *     com.sun.security.auth.module.Krb5LoginModule required
- *       useKeyTab=true
- *       keyTab="/path/to/file.keytab"
- *       principal="user@MYDOMAIN.COM";
- * };
- * 
- * - *

Specifying SASL protocol name

- * - * The SASL protocol name used by this auth provider defaults to " - * {@value #DEFAULT_SASL_SERVICE_NAME}". - * - *

Important: the SASL protocol name should match the username of the Kerberos - * service principal used by the DSE server. This information is specified in the dse.yaml file by - * the {@code service_principal} option under the kerberos_options - * section, and may vary from one DSE installation to another – especially if you installed - * DSE with an automated package installer. - * - *

For example, if your dse.yaml file contains the following: - * - *

{@code
- * kerberos_options:
- *     ...
- *     service_principal: cassandra/my.host.com@MY.REALM.COM
- * }
- * - * The correct SASL protocol name to use when authenticating against this DSE server is "{@code - * cassandra}". - * - *

Should you need to change the SASL protocol name, use one of the methods below: - * - *

    - *
  1. Specify the service name in the driver config. - *
    - * dse-java-driver {
    - *   auth-provider {
    - *     class = com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider
    - *     service = "alternate"
    - *   }
    - * }
    - * 
    - *
  2. Specify the service name with the {@code dse.sasl.service} system property when starting - * your application, e.g. {@code -Ddse.sasl.service=cassandra}. - *
- * - * If a non-null SASL service name is provided to the aforementioned config, that name takes - * precedence over the contents of the {@code dse.sasl.service} system property. - * - *

Should internal sasl properties need to be set such as qop. This can be accomplished by - * including a sasl-properties in the driver config, for example: - * - *

- * dse-java-driver {
- *   auth-provider {
- *     class = com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider
- *     sasl-properties {
- *       javax.security.sasl.qop = "auth-conf"
- *     }
- *   }
- * }
- * 
- */ -@ThreadSafe -public class DseGssApiAuthProvider extends DseGssApiAuthProviderBase { - - private final DriverExecutionProfile config; - - public DseGssApiAuthProvider(DriverContext context) { - super(context.getSessionName()); - - this.config = context.getConfig().getDefaultProfile(); - } - - @NonNull - @Override - protected GssApiOptions getOptions( - @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) { - // A login configuration is always necessary, throw an exception if that option is missing. - AuthUtils.validateConfigPresent( - config, - DseGssApiAuthProvider.class.getName(), - endPoint, - DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION); - - GssApiOptions.Builder optionsBuilder = GssApiOptions.builder(); - - if (config.isDefined(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID)) { - optionsBuilder.withAuthorizationId( - config.getString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID)); - } - if (config.isDefined(DseDriverOption.AUTH_PROVIDER_SERVICE)) { - optionsBuilder.withSaslProtocol(config.getString(DseDriverOption.AUTH_PROVIDER_SERVICE)); - } - if (config.isDefined(DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES)) { - for (Map.Entry entry : - config.getStringMap(DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES).entrySet()) { - optionsBuilder.addSaslProperty(entry.getKey(), entry.getValue()); - } - } - Map loginConfigurationMap = - config.getStringMap(DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION); - optionsBuilder.withLoginConfiguration(loginConfigurationMap); - return optionsBuilder.build(); - } -} diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DsePlainTextAuthProvider.java b/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DsePlainTextAuthProvider.java deleted file mode 100644 index 6cf82aef03e..00000000000 --- a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/DsePlainTextAuthProvider.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.internal.core.auth; - -import com.datastax.oss.driver.api.core.context.DriverContext; -import com.datastax.oss.driver.internal.core.auth.PlainTextAuthProvider; -import net.jcip.annotations.ThreadSafe; - -/** - * @deprecated The driver's default plain text providers now support both Apache Cassandra and DSE. - * This type was preserved for backward compatibility, but {@link PlainTextAuthProvider} should - * be used instead. - */ -@ThreadSafe -@Deprecated -public class DsePlainTextAuthProvider extends PlainTextAuthProvider { - - public DsePlainTextAuthProvider(DriverContext context) { - super(context); - } -} diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.java index fb85797af9e..9f31ac6c7b0 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.java @@ -17,17 +17,13 @@ */ package com.datastax.oss.driver.api.core.auth; -import com.datastax.dse.driver.api.core.auth.BaseDseAuthenticator; import com.datastax.oss.driver.api.core.metadata.EndPoint; import com.datastax.oss.driver.api.core.session.Session; import com.datastax.oss.driver.shaded.guava.common.base.Charsets; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; -import java.net.InetSocketAddress; -import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Objects; import net.jcip.annotations.ThreadSafe; @@ -74,8 +70,7 @@ protected abstract Credentials getCredentials( public Authenticator newAuthenticator( @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) throws AuthenticationException { - return new PlainTextAuthenticator( - getCredentials(endPoint, serverAuthenticator), endPoint, serverAuthenticator); + return new PlainTextAuthenticator(getCredentials(endPoint, serverAuthenticator), endPoint); } @Override @@ -96,25 +91,10 @@ public static class Credentials { private final char[] username; private final char[] password; - private final char[] authorizationId; - - /** - * Builds an instance for username/password authentication, and proxy authentication with the - * given authorizationId. - * - *

This feature is only available with DataStax Enterprise. If the target server is Apache - * Cassandra, the authorizationId will be ignored. - */ - public Credentials( - @NonNull char[] username, @NonNull char[] password, @NonNull char[] authorizationId) { + /** Builds an instance for username/password authentication. */ + public Credentials(@NonNull char[] username, @NonNull char[] password) { this.username = Objects.requireNonNull(username); this.password = Objects.requireNonNull(password); - this.authorizationId = Objects.requireNonNull(authorizationId); - } - - /** Builds an instance for simple username/password authentication. */ - public Credentials(@NonNull char[] username, @NonNull char[] password) { - this(username, password, new char[0]); } @NonNull @@ -137,11 +117,6 @@ public char[] getPassword() { return password; } - @NonNull - public char[] getAuthorizationId() { - return authorizationId; - } - /** Clears the credentials from memory when they're no longer needed. */ protected void clear() { // Note: this is a bit irrelevant with the built-in provider, because the config already @@ -149,85 +124,35 @@ protected void clear() { // retrieves the credentials from a different source. Arrays.fill(getUsername(), (char) 0); Arrays.fill(getPassword(), (char) 0); - Arrays.fill(getAuthorizationId(), (char) 0); } } - // Implementation note: BaseDseAuthenticator is backward compatible with Cassandra authenticators. - // This will work with both Cassandra (as long as no authorizationId is set) and DSE. - protected static class PlainTextAuthenticator extends BaseDseAuthenticator { - - private static final ByteBuffer MECHANISM = - ByteBuffer.wrap("PLAIN".getBytes(StandardCharsets.UTF_8)).asReadOnlyBuffer(); - - private static final ByteBuffer SERVER_INITIAL_CHALLENGE = - ByteBuffer.wrap("PLAIN-START".getBytes(StandardCharsets.UTF_8)).asReadOnlyBuffer(); - - private static final EndPoint DUMMY_END_POINT = - new EndPoint() { - @NonNull - @Override - public SocketAddress resolve() { - return new InetSocketAddress("127.0.0.1", 9042); - } - - @NonNull - @Override - public String asMetricPrefix() { - return ""; // will never be used - } - }; + protected static class PlainTextAuthenticator implements SyncAuthenticator { private final ByteBuffer encodedCredentials; private final EndPoint endPoint; - protected PlainTextAuthenticator( - @NonNull Credentials credentials, - @NonNull EndPoint endPoint, - @NonNull String serverAuthenticator) { - super(serverAuthenticator); - + protected PlainTextAuthenticator(@NonNull Credentials credentials, @NonNull EndPoint endPoint) { Objects.requireNonNull(credentials); Objects.requireNonNull(endPoint); - ByteBuffer authorizationId = toUtf8Bytes(credentials.getAuthorizationId()); ByteBuffer username = toUtf8Bytes(credentials.getUsername()); ByteBuffer password = toUtf8Bytes(credentials.getPassword()); this.encodedCredentials = - ByteBuffer.allocate( - authorizationId.remaining() + username.remaining() + password.remaining() + 2); - encodedCredentials.put(authorizationId); + ByteBuffer.allocate(username.remaining() + password.remaining() + 2); encodedCredentials.put((byte) 0); encodedCredentials.put(username); encodedCredentials.put((byte) 0); encodedCredentials.put(password); encodedCredentials.flip(); - clear(authorizationId); clear(username); clear(password); this.endPoint = endPoint; } - /** - * @deprecated Preserved for backward compatibility, implementors should use the 3-arg - * constructor {@code PlainTextAuthenticator(Credentials, EndPoint, String)} instead. - */ - @Deprecated - protected PlainTextAuthenticator(@NonNull Credentials credentials) { - this( - credentials, - // It's unlikely that this class was ever extended by third parties, but if it was, assume - // that it was not written for DSE: - // - dummy end point because we should never need to build an auth exception - DUMMY_END_POINT, - // - default OSS authenticator name (the only thing that matters is how this string - // compares to "DseAuthenticator") - "org.apache.cassandra.auth.PasswordAuthenticator"); - } - private static ByteBuffer toUtf8Bytes(char[] charArray) { CharBuffer charBuffer = CharBuffer.wrap(charArray); return Charsets.UTF_8.encode(charBuffer); @@ -240,25 +165,19 @@ private static void clear(ByteBuffer buffer) { } } - @NonNull - @Override - public ByteBuffer getMechanism() { - return MECHANISM; - } - - @NonNull + @Nullable @Override - public ByteBuffer getInitialServerChallenge() { - return SERVER_INITIAL_CHALLENGE; + public ByteBuffer initialResponseSync() { + return encodedCredentials; } @Nullable @Override public ByteBuffer evaluateChallengeSync(@Nullable ByteBuffer challenge) { - if (SERVER_INITIAL_CHALLENGE.equals(challenge)) { - return encodedCredentials; - } throw new AuthenticationException(endPoint, "Incorrect challenge from server"); } + + @Override + public void onAuthenticationSuccessSync(@Nullable ByteBuffer token) {} } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.java b/core/src/main/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.java index d991f5c5cb5..14e1c8c6d94 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.java @@ -21,7 +21,6 @@ import com.datastax.oss.driver.api.core.session.SessionBuilder; import com.datastax.oss.driver.internal.core.util.Strings; import edu.umd.cs.findbugs.annotations.NonNull; -import java.util.Objects; import net.jcip.annotations.ThreadSafe; /** @@ -51,37 +50,20 @@ * * @see SessionBuilder#withAuthProvider(AuthProvider) * @see SessionBuilder#withAuthCredentials(String, String) - * @see SessionBuilder#withAuthCredentials(String, String, String) */ @ThreadSafe public class ProgrammaticPlainTextAuthProvider extends PlainTextAuthProviderBase { private volatile char[] username; private volatile char[] password; - private volatile char[] authorizationId; /** Builds an instance for simple username/password authentication. */ public ProgrammaticPlainTextAuthProvider(@NonNull String username, @NonNull String password) { - this(username, password, ""); - } - - /** - * Builds an instance for username/password authentication, and proxy authentication with the - * given authorizationId. - * - *

This feature is only available with DataStax Enterprise. If the target server is Apache - * Cassandra, use {@link #ProgrammaticPlainTextAuthProvider(String, String)} instead, or set the - * authorizationId to an empty string. - */ - public ProgrammaticPlainTextAuthProvider( - @NonNull String username, @NonNull String password, @NonNull String authorizationId) { // This will typically be built before the session so we don't know the log prefix yet. Pass an // empty string, it's only used in one log message. super(""); this.username = Strings.requireNotEmpty(username, "username").toCharArray(); this.password = Strings.requireNotEmpty(password, "password").toCharArray(); - this.authorizationId = - Objects.requireNonNull(authorizationId, "authorizationId cannot be null").toCharArray(); } /** @@ -106,21 +88,6 @@ public void setPassword(@NonNull String password) { this.password = Strings.requireNotEmpty(password, "password").toCharArray(); } - /** - * Changes the authorization id. - * - *

The new credentials will be used for all connections initiated after this method was called. - * - *

This feature is only available with DataStax Enterprise. If the target server is Apache - * Cassandra, this method should not be used. - * - * @param authorizationId the new authorization id. - */ - public void setAuthorizationId(@NonNull String authorizationId) { - this.authorizationId = - Objects.requireNonNull(authorizationId, "authorizationId cannot be null").toCharArray(); - } - /** * {@inheritDoc} * @@ -131,6 +98,6 @@ public void setAuthorizationId(@NonNull String authorizationId) { @Override protected Credentials getCredentials( @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) { - return new Credentials(username.clone(), password.clone(), authorizationId.clone()); + return new Credentials(username.clone(), password.clone()); } } diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java b/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java index af93e734ef1..031173d432e 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/config/TypedDriverOption.java @@ -708,21 +708,6 @@ public String toString() { /** The version of the application using the session. */ public static final TypedDriverOption APPLICATION_VERSION = new TypedDriverOption<>(DseDriverOption.APPLICATION_VERSION, GenericType.STRING); - /** Proxy authentication for GSSAPI authentication: allows to login as another user or role. */ - public static final TypedDriverOption AUTH_PROVIDER_AUTHORIZATION_ID = - new TypedDriverOption<>(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, GenericType.STRING); - /** Service name for GSSAPI authentication. */ - public static final TypedDriverOption AUTH_PROVIDER_SERVICE = - new TypedDriverOption<>(DseDriverOption.AUTH_PROVIDER_SERVICE, GenericType.STRING); - /** Login configuration for GSSAPI authentication. */ - public static final TypedDriverOption AUTH_PROVIDER_LOGIN_CONFIGURATION = - new TypedDriverOption<>( - DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION, GenericType.STRING); - /** Internal SASL properties, if any, such as QOP, for GSSAPI authentication. */ - public static final TypedDriverOption> AUTH_PROVIDER_SASL_PROPERTIES = - new TypedDriverOption<>( - DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES, - GenericType.mapOf(GenericType.STRING, GenericType.STRING)); /** The page size for continuous paging. */ public static final TypedDriverOption CONTINUOUS_PAGING_PAGE_SIZE = new TypedDriverOption<>(DseDriverOption.CONTINUOUS_PAGING_PAGE_SIZE, GenericType.INTEGER); diff --git a/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java b/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java index 8375f0ef30b..c29032f2cfb 100644 --- a/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java +++ b/core/src/main/java/com/datastax/oss/driver/api/core/session/SessionBuilder.java @@ -370,32 +370,6 @@ public SelfT withAuthCredentials(@NonNull String username, @NonNull String passw return withAuthProvider(new ProgrammaticPlainTextAuthProvider(username, password)); } - /** - * Configures the session to use DSE plaintext authentication with the given username and - * password, and perform proxy authentication with the given authorization id. - * - *

This feature is only available in DataStax Enterprise. If connecting to Apache Cassandra, - * the authorization id will be ignored; it is recommended to use {@link - * #withAuthCredentials(String, String)} instead. - * - *

This methods calls {@link #withAuthProvider(AuthProvider)} to register a special provider - * implementation. Therefore calling it overrides the configuration (that is, the {@code - * advanced.auth-provider.class} option will be ignored). - * - *

Note that this approach holds the credentials in clear text in memory, which makes them - * vulnerable to an attacker who is able to perform memory dumps. If this is not acceptable for - * you, consider writing your own {@link AuthProvider} implementation (the internal class {@code - * PlainTextAuthProviderBase} is a good starting point), and providing it either with {@link - * #withAuthProvider(AuthProvider)} or via the configuration ({@code - * advanced.auth-provider.class}). - */ - @NonNull - public SelfT withAuthCredentials( - @NonNull String username, @NonNull String password, @NonNull String authorizationId) { - return withAuthProvider( - new ProgrammaticPlainTextAuthProvider(username, password, authorizationId)); - } - /** * @deprecated this method only exists to ease the transition from driver 3, it is an alias for * {@link #withAuthCredentials(String, String)}. @@ -406,17 +380,6 @@ public SelfT withCredentials(@NonNull String username, @NonNull String password) return withAuthCredentials(username, password); } - /** - * @deprecated this method only exists to ease the transition from driver 3, it is an alias for - * {@link #withAuthCredentials(String, String,String)}. - */ - @Deprecated - @NonNull - public SelfT withCredentials( - @NonNull String username, @NonNull String password, @NonNull String authorizationId) { - return withAuthCredentials(username, password, authorizationId); - } - /** * Registers an SSL engine factory for the session. * diff --git a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/AuthUtils.java b/core/src/main/java/com/datastax/oss/driver/internal/core/auth/AuthUtils.java similarity index 57% rename from core/src/main/java/com/datastax/dse/driver/internal/core/auth/AuthUtils.java rename to core/src/main/java/com/datastax/oss/driver/internal/core/auth/AuthUtils.java index 38f1644bcb7..c6a25c63517 100644 --- a/core/src/main/java/com/datastax/dse/driver/internal/core/auth/AuthUtils.java +++ b/core/src/main/java/com/datastax/oss/driver/internal/core/auth/AuthUtils.java @@ -1,11 +1,11 @@ /* * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file + * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file + * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at + * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.datastax.dse.driver.internal.core.auth; +package com.datastax.oss.driver.internal.core.auth; import com.datastax.oss.driver.api.core.auth.AuthenticationException; import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; @@ -25,15 +25,7 @@ import java.util.List; public class AuthUtils { - /** - * Utility function that checks for the existence of settings and throws an exception if they - * aren't present - * - * @param config Current working driver configuration - * @param authenticatorName name of authenticator for logging purposes - * @param endPoint the host we are attempting to authenticate to - * @param options a list of DriverOptions to check to see if they are present - */ + public static void validateConfigPresent( DriverExecutionProfile config, String authenticatorName, @@ -41,18 +33,19 @@ public static void validateConfigPresent( DriverOption... options) { List missingOptions = new ArrayList<>(); for (DriverOption option : options) { - if (!config.isDefined(option)) { missingOptions.add(option); } - if (missingOptions.size() > 0) { - String message = - "Missing required configuration options for authenticator " + authenticatorName + ":"; - for (DriverOption missingOption : missingOptions) { - message = message + " " + missingOption.getPath(); - } - throw new AuthenticationException(endPoint, message); + } + if (!missingOptions.isEmpty()) { + StringBuilder message = + new StringBuilder("Missing required configuration options for authenticator ") + .append(authenticatorName) + .append(':'); + for (DriverOption missingOption : missingOptions) { + message.append(' ').append(missingOption.getPath()); } + throw new AuthenticationException(endPoint, message.toString()); } } } diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/auth/PlainTextAuthProvider.java b/core/src/main/java/com/datastax/oss/driver/internal/core/auth/PlainTextAuthProvider.java index f2dfdf14171..3a6d42410e0 100644 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/auth/PlainTextAuthProvider.java +++ b/core/src/main/java/com/datastax/oss/driver/internal/core/auth/PlainTextAuthProvider.java @@ -17,8 +17,6 @@ */ package com.datastax.oss.driver.internal.core.auth; -import com.datastax.dse.driver.api.core.config.DseDriverOption; -import com.datastax.dse.driver.internal.core.auth.AuthUtils; import com.datastax.oss.driver.api.core.auth.PlainTextAuthProviderBase; import com.datastax.oss.driver.api.core.config.DefaultDriverOption; import com.datastax.oss.driver.api.core.config.DriverExecutionProfile; @@ -40,10 +38,6 @@ * class = com.datastax.driver.api.core.auth.PlainTextAuthProvider * username = cassandra * password = cassandra - * - * // If connecting to DataStax Enterprise, this additional option allows proxy authentication - * // (login as another user or role) - * authorization-id = userOrRole * } * } * @@ -77,11 +71,8 @@ protected Credentials getCredentials( DefaultDriverOption.AUTH_PROVIDER_USER_NAME, DefaultDriverOption.AUTH_PROVIDER_PASSWORD); - String authorizationId = config.getString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, ""); - assert authorizationId != null; // per the default above return new Credentials( config.getString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME).toCharArray(), - config.getString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD).toCharArray(), - authorizationId.toCharArray()); + config.getString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD).toCharArray()); } } diff --git a/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java b/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java index e6323b6b84c..135035b1866 100644 --- a/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java +++ b/core/src/main/java/com/datastax/oss/driver/internal/core/context/DefaultDriverContext.java @@ -955,8 +955,7 @@ protected Optional buildAuthProvider(AuthProvider authProviderFrom this, DefaultDriverOption.AUTH_PROVIDER_CLASS, AuthProvider.class, - "com.datastax.oss.driver.internal.core.auth", - "com.datastax.dse.driver.internal.core.auth"); + "com.datastax.oss.driver.internal.core.auth"); } protected List buildLifecycleListeners() { diff --git a/core/src/main/resources/META-INF/native-image/com.datastax.oss/java-driver-core/reflection.json b/core/src/main/resources/META-INF/native-image/com.datastax.oss/java-driver-core/reflection.json index 6082b853611..ba13d84a782 100644 --- a/core/src/main/resources/META-INF/native-image/com.datastax.oss/java-driver-core/reflection.json +++ b/core/src/main/resources/META-INF/native-image/com.datastax.oss/java-driver-core/reflection.json @@ -87,10 +87,6 @@ "name": "com.datastax.oss.driver.internal.core.auth.PlainTextAuthProvider", "methods": [ { "name": "", "parameterTypes": [ "com.datastax.oss.driver.api.core.context.DriverContext" ] } ] }, - { - "name": "com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider", - "methods": [ { "name": "", "parameterTypes": [ "com.datastax.oss.driver.api.core.context.DriverContext" ] } ] - }, { "name": "com.datastax.oss.driver.internal.core.ssl.DefaultSslEngineFactory", "methods": [ { "name": "", "parameterTypes": [ "com.datastax.oss.driver.api.core.context.DriverContext" ] } ] diff --git a/core/src/main/resources/reference.conf b/core/src/main/resources/reference.conf index 590784b70c5..705677ff929 100644 --- a/core/src/main/resources/reference.conf +++ b/core/src/main/resources/reference.conf @@ -778,62 +778,24 @@ datastax-java-driver { # SessionBuilder.withAuthProvider or SessionBuilder.withAuthCredentials. advanced.auth-provider { # The class of the provider. If it is not qualified, the driver assumes that it resides in one - # of the following packages: + # in the following package: # - com.datastax.oss.driver.internal.core.auth - # - com.datastax.dse.driver.internal.core.auth # - # The driver provides two implementations: + # The driver provides one implementation: # - PlainTextAuthProvider: uses plain-text credentials. It requires the `username` and - # `password` options below. When connecting to DataStax Enterprise, an optional - # `authorization-id` can also be specified. - # For backward compatibility with previous driver versions, you can also use the class name - # "DsePlainTextAuthProvider" for this provider. - # - DseGssApiAuthProvider: provides GSSAPI authentication for DSE clusters secured with - # DseAuthenticator. See the example below and refer to the manual for detailed instructions. + # `password` options below. # # You can also specify a custom class that implements AuthProvider and has a public constructor - # with a DriverContext argument (to simplify this, the driver provides two abstract classes that - # can be extended: PlainTextAuthProviderBase and DseGssApiAuthProviderBase). + # with a DriverContext argument. PlainTextAuthProviderBase can be extended to simplify custom + # providers that use plain-text credentials. # # Finally, you can configure a provider instance programmatically with - # DseSessionBuilder#withAuthProvider. In that case, it will take precedence over the - # configuration. + # SessionBuilder#withAuthProvider. In that case, it will take precedence over the configuration. // class = PlainTextAuthProvider # # Sample configuration for plain-text authentication providers: // username = cassandra // password = cassandra - # - # Proxy authentication: allows to login as another user or role (valid for both - # PlainTextAuthProvider and DseGssApiAuthProvider): - // authorization-id = userOrRole - # - # The settings below are only applicable to DseGssApiAuthProvider: - # - # Service name. For example, if in your dse.yaml configuration file the - # "kerberos_options/service_principal" setting is "cassandra/my.host.com@MY.REALM.COM", then set - # this option to "cassandra". If this value is not explicitly set via configuration (in an - # application.conf or programmatically), the driver will attempt to set it via a System - # property. The property should be "dse.sasl.service". For backwards compatibility with 1.x - # versions of the driver, if "dse.sasl.service" is not set as a System property, the driver will - # attempt to use "dse.sasl.protocol" as a fallback (which is the property for the 1.x driver). - //service = "cassandra" - # - # Login configuration. It is also possible to provide login configuration through a standard - # JAAS configuration file. The below configuration is just an example, see all possible options - # here: - # https://docs.oracle.com/javase/6/docs/jre/api/security/jaas/spec/com/sun/security/auth/module/Krb5LoginModule.html - // login-configuration { - // principal = "cassandra@DATASTAX.COM" - // useKeyTab = "true" - // refreshKrb5Config = "true" - // keyTab = "/path/to/keytab/file" - // } - # - # Internal SASL properties, if any, such as QOP. - // sasl-properties { - // javax.security.sasl.qop = "auth-conf" - // } } # The SSL engine factory that will initialize an SSL engine for each new connection to a server. diff --git a/core/src/test/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProviderTest.java b/core/src/test/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProviderTest.java index 44d2acfbb2e..04a87f7c44b 100644 --- a/core/src/test/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProviderTest.java +++ b/core/src/test/java/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProviderTest.java @@ -21,6 +21,7 @@ import com.datastax.oss.driver.api.core.auth.PlainTextAuthProviderBase.Credentials; import com.datastax.oss.driver.api.core.metadata.EndPoint; +import java.nio.ByteBuffer; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -32,29 +33,31 @@ public class ProgrammaticPlainTextAuthProviderTest { @Mock private EndPoint endpoint; @Test - public void should_return_correct_credentials_without_authorization_id() { - // given + public void should_encode_standard_sasl_plain_response() { ProgrammaticPlainTextAuthProvider provider = new ProgrammaticPlainTextAuthProvider("user", "pass"); - // when - Credentials credentials = provider.getCredentials(endpoint, "irrelevant"); - // then - assertThat(credentials.getUsername()).isEqualTo("user".toCharArray()); - assertThat(credentials.getPassword()).isEqualTo("pass".toCharArray()); - assertThat(credentials.getAuthorizationId()).isEqualTo(new char[0]); + + ByteBuffer response = + provider + .newAuthenticator(endpoint, "org.apache.cassandra.auth.PasswordAuthenticator") + .initialResponse() + .toCompletableFuture() + .join(); + + assertThat(response) + .isEqualTo(ByteBuffer.wrap(new byte[] {0, 'u', 's', 'e', 'r', 0, 'p', 'a', 's', 's'})); } @Test - public void should_return_correct_credentials_with_authorization_id() { + public void should_return_correct_credentials() { // given ProgrammaticPlainTextAuthProvider provider = - new ProgrammaticPlainTextAuthProvider("user", "pass", "proxy"); + new ProgrammaticPlainTextAuthProvider("user", "pass"); // when Credentials credentials = provider.getCredentials(endpoint, "irrelevant"); // then assertThat(credentials.getUsername()).isEqualTo("user".toCharArray()); assertThat(credentials.getPassword()).isEqualTo("pass".toCharArray()); - assertThat(credentials.getAuthorizationId()).isEqualTo("proxy".toCharArray()); } @Test @@ -68,7 +71,6 @@ public void should_change_username() { // then assertThat(credentials.getUsername()).isEqualTo("user2".toCharArray()); assertThat(credentials.getPassword()).isEqualTo("pass".toCharArray()); - assertThat(credentials.getAuthorizationId()).isEqualTo(new char[0]); } @Test @@ -82,20 +84,5 @@ public void should_change_password() { // then assertThat(credentials.getUsername()).isEqualTo("user".toCharArray()); assertThat(credentials.getPassword()).isEqualTo("pass2".toCharArray()); - assertThat(credentials.getAuthorizationId()).isEqualTo(new char[0]); - } - - @Test - public void should_change_authorization_id() { - // given - ProgrammaticPlainTextAuthProvider provider = - new ProgrammaticPlainTextAuthProvider("user", "pass", "proxy"); - // when - provider.setAuthorizationId("proxy2"); - Credentials credentials = provider.getCredentials(endpoint, "irrelevant"); - // then - assertThat(credentials.getUsername()).isEqualTo("user".toCharArray()); - assertThat(credentials.getPassword()).isEqualTo("pass".toCharArray()); - assertThat(credentials.getAuthorizationId()).isEqualTo("proxy2".toCharArray()); } } diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 7f481242fce..66393bf34e1 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -143,41 +143,6 @@ tinkergraph-gremlin test - - org.apache.directory.server - apacheds-core - test - - - org.apache.directory.server - apacheds-protocol-kerberos - test - - - org.apache.directory.server - apacheds-interceptor-kerberos - test - - - org.apache.directory.server - apacheds-protocol-ldap - test - - - org.apache.directory.server - apacheds-ldif-partition - test - - - org.apache.directory.server - apacheds-jdbm-partition - test - - - org.apache.directory.api - api-ldap-codec-standalone - test - com.github.tomakehurst wiremock diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderAlternateIT.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderAlternateIT.java deleted file mode 100644 index 77edb6c7c3e..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderAlternateIT.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.datastax.dse.driver.api.core.config.DseDriverOption; -import com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.cql.Row; -import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; -import com.datastax.oss.driver.api.testinfra.requirement.BackendType; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; -import com.tngtech.java.junit.dataprovider.DataProvider; -import com.tngtech.java.junit.dataprovider.DataProviderRunner; -import com.tngtech.java.junit.dataprovider.UseDataProvider; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@BackendRequirement( - type = BackendType.DSE, - minInclusive = "5.0", - description = "Required for DseAuthenticator") -@RunWith(DataProviderRunner.class) -public class DseGssApiAuthProviderAlternateIT { - @ClassRule public static EmbeddedAdsRule ads = new EmbeddedAdsRule(true); - - @DataProvider - public static Object[][] saslSystemProperties() { - return new Object[][] {{"dse.sasl.service"}, {"dse.sasl.protocol"}}; - } - - @Test - @UseDataProvider("saslSystemProperties") - public void - should_authenticate_using_kerberos_with_keytab_and_alternate_service_principal_using_system_property( - String saslSystemProperty) { - System.setProperty(saslSystemProperty, "alternate"); - try (CqlSession session = - SessionUtils.newSession( - ads.getCcm(), - SessionUtils.configLoaderBuilder() - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, DseGssApiAuthProvider.class) - .withStringMap( - DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES, - ImmutableMap.of("javax.security.sasl.qop", "auth-conf")) - .withStringMap( - DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION, - ImmutableMap.of( - "principal", - ads.getUserPrincipal(), - "useKeyTab", - "true", - "refreshKrb5Config", - "true", - "keyTab", - ads.getUserKeytab().getAbsolutePath())) - .build())) { - Row row = session.execute("select * from system.local where key='local'").one(); - assertThat(row).isNotNull(); - } finally { - System.clearProperty(saslSystemProperty); - } - } - - @Test - public void should_authenticate_using_kerberos_with_keytab_and_alternate_service_principal() { - try (CqlSession session = - SessionUtils.newSession( - ads.getCcm(), - SessionUtils.configLoaderBuilder() - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, DseGssApiAuthProvider.class) - .withString(DseDriverOption.AUTH_PROVIDER_SERVICE, "alternate") - .withStringMap( - DseDriverOption.AUTH_PROVIDER_SASL_PROPERTIES, - ImmutableMap.of("javax.security.sasl.qop", "auth-conf")) - .withStringMap( - DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION, - ImmutableMap.of( - "principal", - ads.getUserPrincipal(), - "useKeyTab", - "true", - "refreshKrb5Config", - "true", - "keyTab", - ads.getUserKeytab().getAbsolutePath())) - .build())) { - Row row = session.execute("select * from system.local where key='local'").one(); - assertThat(row).isNotNull(); - } - } -} diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderIT.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderIT.java deleted file mode 100644 index 01e1e704198..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderIT.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import static com.datastax.dse.driver.api.core.auth.KerberosUtils.acquireTicket; -import static com.datastax.dse.driver.api.core.auth.KerberosUtils.destroyTicket; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -import com.datastax.oss.driver.api.core.AllNodesFailedException; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.auth.AuthenticationException; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; -import com.datastax.oss.driver.api.testinfra.requirement.BackendType; -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; -import java.util.List; -import java.util.Map; -import org.junit.Assume; -import org.junit.ClassRule; -import org.junit.Test; - -@BackendRequirement( - type = BackendType.DSE, - minInclusive = "5.0", - description = "Required for DseAuthenticator") -public class DseGssApiAuthProviderIT { - - @ClassRule public static EmbeddedAdsRule ads = new EmbeddedAdsRule(); - - /** - * Ensures that a Session can be established to a DSE server secured with Kerberos and that simple - * queries can be made using a client configuration that provides a keytab file. - */ - @Test - public void should_authenticate_using_kerberos_with_keytab() { - try (CqlSession session = ads.newKeyTabSession()) { - ResultSet set = session.execute("select * from system.local where key='local'"); - assertThat(set).isNotNull(); - } - } - - /** - * Ensures that a Session can be established to a DSE server secured with Kerberos and that simple - * queries can be made using a client configuration that uses the ticket cache. This test will - * only run on unix platforms since it uses kinit to acquire tickets and kdestroy to destroy them. - */ - @Test - public void should_authenticate_using_kerberos_with_ticket() throws Exception { - String osName = System.getProperty("os.name", "").toLowerCase(); - boolean isUnix = osName.contains("mac") || osName.contains("darwin") || osName.contains("nux"); - Assume.assumeTrue(isUnix); - acquireTicket(ads.getUserPrincipal(), ads.getUserKeytab(), ads.getAdsServer()); - try (CqlSession session = ads.newTicketSession()) { - ResultSet set = session.execute("select * from system.local where key='local'"); - assertThat(set).isNotNull(); - } finally { - destroyTicket(ads); - } - } - - /** - * Validates that an AllNodesFailedException is thrown when using a ticket-based configuration and - * no such ticket exists in the user's cache. This is expected because we shouldn't be able to - * establish connection to a cassandra node if we cannot authenticate. - * - * @test_category dse:authentication - */ - @SuppressWarnings("unused") - @Test - public void should_not_authenticate_if_no_ticket_in_cache() { - try (CqlSession session = ads.newTicketSession()) { - fail("Expected an AllNodesFailedException"); - } catch (AllNodesFailedException e) { - verifyException(e); - } - } - - /** - * Validates that an AllNodesFailedException is thrown when using a keytab-based configuration and - * no such user exists for the given principal. This is expected because we shouldn't be able to - * establish connection to a cassandra node if we cannot authenticate. - * - * @test_category dse:authentication - */ - @SuppressWarnings("unused") - @Test - public void should_not_authenticate_if_keytab_does_not_map_to_valid_principal() { - try (CqlSession session = - ads.newKeyTabSession(ads.getUnknownPrincipal(), ads.getUnknownKeytab().getAbsolutePath())) { - fail("Expected an AllNodesFailedException"); - } catch (AllNodesFailedException e) { - verifyException(e); - } - } - /** - * Ensures that a Session can be established to a DSE server secured with Kerberos and that simple - * queries can be made using a client configuration that is provided via programatic interface - */ - @Test - public void should_authenticate_using_kerberos_with_keytab_programmatically() { - DseGssApiAuthProviderBase.GssApiOptions.Builder builder = - DseGssApiAuthProviderBase.GssApiOptions.builder(); - Map loginConfig = - ImmutableMap.of( - "principal", - ads.getUserPrincipal(), - "useKeyTab", - "true", - "refreshKrb5Config", - "true", - "keyTab", - ads.getUserKeytab().getAbsolutePath()); - - builder.withLoginConfiguration(loginConfig); - try (CqlSession session = - CqlSession.builder() - .withAuthProvider(new ProgrammaticDseGssApiAuthProvider(builder.build())) - .build()) { - - ResultSet set = session.execute("select * from system.local where key='local'"); - assertThat(set).isNotNull(); - } - } - - private void verifyException(AllNodesFailedException anfe) { - assertThat(anfe.getAllErrors()).hasSize(1); - List errors = anfe.getAllErrors().values().iterator().next(); - assertThat(errors).hasSize(1); - Throwable firstError = errors.get(0); - assertThat(firstError) - .isInstanceOf(AuthenticationException.class) - .hasMessageContaining("Authentication error on node /127.0.0.1:9042"); - } -} diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderIT.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderIT.java deleted file mode 100644 index af785b4b372..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DsePlainTextAuthProviderIT.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Fail.fail; - -import com.datastax.dse.driver.api.core.config.DseDriverOption; -import com.datastax.oss.driver.api.core.AllNodesFailedException; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.Version; -import com.datastax.oss.driver.api.core.auth.AuthenticationException; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.testinfra.ccm.CustomCcmRule; -import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; -import com.datastax.oss.driver.api.testinfra.requirement.BackendType; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.internal.core.auth.PlainTextAuthProvider; -import com.datastax.oss.driver.shaded.guava.common.util.concurrent.Uninterruptibles; -import java.util.List; -import java.util.concurrent.TimeUnit; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; - -@BackendRequirement( - type = BackendType.DSE, - minInclusive = "5.0", - description = "Required for DseAuthenticator") -public class DsePlainTextAuthProviderIT { - - @ClassRule - public static CustomCcmRule ccm = - CustomCcmRule.builder() - .withCassandraConfiguration( - "authenticator", "com.datastax.bdp.cassandra.auth.DseAuthenticator") - .withDseConfiguration("authentication_options.enabled", true) - .withDseConfiguration("authentication_options.default_scheme", "internal") - .withJvmArgs("-Dcassandra.superuser_setup_delay_ms=0") - .build(); - - @BeforeClass - public static void sleepForAuth() { - if (ccm.getCassandraVersion().compareTo(Version.V2_2_0) < 0) { - // Sleep for 1 second to allow C* auth to do its work. This is only needed for 2.1 - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - } - } - - @Test - public void should_connect_dse_plaintext_auth() { - try (CqlSession session = - SessionUtils.newSession( - ccm, - SessionUtils.configLoaderBuilder() - .withString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, "") - .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, "cassandra") - .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, "cassandra") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build())) { - session.execute("select * from system.local where key='local'"); - } - } - - @Test - public void should_connect_dse_plaintext_auth_programmatically() { - try (CqlSession session = - CqlSession.builder() - .addContactEndPoints(ccm.getContactPoints()) - .withAuthCredentials("cassandra", "cassandra") - .build()) { - session.execute("select * from system.local where key='local'"); - } - } - - @SuppressWarnings("unused") - @Test - public void should_not_connect_with_invalid_credentials() { - try (CqlSession session = - SessionUtils.newSession( - ccm, - SessionUtils.configLoaderBuilder() - .withString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, "") - .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, "cassandra") - .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, "NotARealPassword") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build())) { - fail("Expected an AllNodesFailedException"); - } catch (AllNodesFailedException e) { - verifyException(e); - } - } - - @SuppressWarnings("unused") - @Test - public void should_not_connect_without_credentials() { - try (CqlSession session = - SessionUtils.newSession( - ccm, - SessionUtils.configLoaderBuilder() - .withString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, "") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build())) { - fail("Expected AllNodesFailedException"); - } catch (AllNodesFailedException e) { - verifyException(e); - } - } - - private void verifyException(AllNodesFailedException anfe) { - assertThat(anfe.getAllErrors()).hasSize(1); - List errors = anfe.getAllErrors().values().iterator().next(); - assertThat(errors).hasSize(1); - Throwable firstError = errors.get(0); - assertThat(firstError) - .isInstanceOf(AuthenticationException.class) - .hasMessageContaining("Authentication error on node /127.0.0.1:9042"); - } -} diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseProxyAuthenticationIT.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseProxyAuthenticationIT.java deleted file mode 100644 index 4d43c75ed28..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/DseProxyAuthenticationIT.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.fail; - -import com.datastax.dse.driver.api.core.config.DseDriverOption; -import com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider; -import com.datastax.oss.driver.api.core.AllNodesFailedException; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.auth.AuthenticationException; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.cql.ResultSet; -import com.datastax.oss.driver.api.core.cql.SimpleStatement; -import com.datastax.oss.driver.api.core.servererrors.UnauthorizedException; -import com.datastax.oss.driver.api.testinfra.ccm.SchemaChangeSynchronizer; -import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; -import com.datastax.oss.driver.api.testinfra.requirement.BackendType; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.internal.core.auth.PlainTextAuthProvider; -import java.util.List; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; - -@BackendRequirement( - type = BackendType.DSE, - minInclusive = "5.1", - description = "Required for DseAuthenticator with proxy") -public class DseProxyAuthenticationIT { - private static String bobPrincipal; - private static String charliePrincipal; - @ClassRule public static EmbeddedAdsRule ads = new EmbeddedAdsRule(); - - @BeforeClass - public static void addUsers() { - bobPrincipal = ads.addUserAndCreateKeyTab("bob", "fakePasswordForBob"); - charliePrincipal = ads.addUserAndCreateKeyTab("charlie", "fakePasswordForCharlie"); - } - - @Before - public void setupRoles() { - - SchemaChangeSynchronizer.withLock( - () -> { - try (CqlSession session = ads.newKeyTabSession()) { - session.execute( - "CREATE ROLE IF NOT EXISTS alice WITH PASSWORD = 'fakePasswordForAlice' AND LOGIN = FALSE"); - session.execute( - "CREATE ROLE IF NOT EXISTS ben WITH PASSWORD = 'fakePasswordForBen' AND LOGIN = TRUE"); - session.execute("CREATE ROLE IF NOT EXISTS 'bob@DATASTAX.COM' WITH LOGIN = TRUE"); - session.execute( - "CREATE ROLE IF NOT EXISTS 'charlie@DATASTAX.COM' WITH PASSWORD = 'fakePasswordForCharlie' AND LOGIN = TRUE"); - session.execute( - "CREATE ROLE IF NOT EXISTS steve WITH PASSWORD = 'fakePasswordForSteve' AND LOGIN = TRUE"); - session.execute( - "CREATE KEYSPACE IF NOT EXISTS aliceks WITH REPLICATION = {'class':'NetworkTopologyStrategy', 'dc1':'1'}"); - session.execute( - "CREATE TABLE IF NOT EXISTS aliceks.alicetable (key text PRIMARY KEY, value text)"); - session.execute( - "INSERT INTO aliceks.alicetable (key, value) VALUES ('hello', 'world')"); - session.execute("GRANT ALL ON KEYSPACE aliceks TO alice"); - session.execute("GRANT EXECUTE ON ALL AUTHENTICATION SCHEMES TO 'ben'"); - session.execute("GRANT EXECUTE ON ALL AUTHENTICATION SCHEMES TO 'bob@DATASTAX.COM'"); - session.execute("GRANT EXECUTE ON ALL AUTHENTICATION SCHEMES TO 'steve'"); - session.execute( - "GRANT EXECUTE ON ALL AUTHENTICATION SCHEMES TO 'charlie@DATASTAX.COM'"); - session.execute("GRANT PROXY.LOGIN ON ROLE 'alice' TO 'ben'"); - session.execute("GRANT PROXY.LOGIN ON ROLE 'alice' TO 'bob@DATASTAX.COM'"); - session.execute("GRANT PROXY.EXECUTE ON ROLE 'alice' TO 'steve'"); - session.execute("GRANT PROXY.EXECUTE ON ROLE 'alice' TO 'charlie@DATASTAX.COM'"); - // ben and bob are allowed to login as alice, but not execute as alice. - // charlie and steve are allowed to execute as alice, but not login as alice. - } - }); - } - /** - * Validates that a connection may be successfully made as user 'alice' using the credentials of a - * user 'ben' using {@link PlainTextAuthProvider} assuming ben has PROXY.LOGIN authorization on - * alice. - */ - @Test - public void should_allow_plain_text_authorized_user_to_login_as() { - try (CqlSession session = - SessionUtils.newSession( - ads.ccm, - SessionUtils.configLoaderBuilder() - .withString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, "alice") - .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, "ben") - .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, "fakePasswordForBen") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build())) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - ResultSet set = session.execute(select); - assertThat(set).isNotNull(); - } - } - - @Test - public void should_allow_plain_text_authorized_user_to_login_as_programmatically() { - try (CqlSession session = - CqlSession.builder() - .addContactEndPoints(ads.ccm.getContactPoints()) - .withAuthCredentials("ben", "fakePasswordForBen", "alice") - .build()) { - session.execute("select * from system.local where key='local'"); - } - } - - /** - * Validates that a connection may successfully made as user 'alice' using the credentials of a - * principal 'bob@DATASTAX.COM' using {@link DseGssApiAuthProvider} assuming 'bob@DATASTAX.COM' - * has PROXY.LOGIN authorization on alice. - */ - @Test - public void should_allow_kerberos_authorized_user_to_login_as() { - try (CqlSession session = - ads.newKeyTabSession( - bobPrincipal, ads.getKeytabForPrincipal(bobPrincipal).getAbsolutePath(), "alice")) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - ResultSet set = session.execute(select); - assertThat(set).isNotNull(); - } - } - - /** - * Validates that a connection does not succeed as user 'alice' using the credentials of a user - * 'steve' assuming 'steve' does not have PROXY.LOGIN authorization on alice. - */ - @Test - public void should_not_allow_plain_text_unauthorized_user_to_login_as() { - try (CqlSession session = - SessionUtils.newSession( - ads.ccm, - SessionUtils.configLoaderBuilder() - .withString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, "alice") - .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, "steve") - .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, "fakePasswordForSteve") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build())) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - session.execute(select); - fail("Should have thrown AllNodesFailedException on login."); - } catch (AllNodesFailedException anfe) { - verifyException(anfe); - } - } - /** - * Validates that a connection does not succeed as user 'alice' using the credentials of a - * principal 'charlie@DATASTAX.COM' assuming 'charlie@DATASTAX.COM' does not have PROXY.LOGIN - * authorization on alice. - */ - @Test - public void should_not_allow_kerberos_unauthorized_user_to_login_as() throws Exception { - try (CqlSession session = - ads.newKeyTabSession( - charliePrincipal, - ads.getKeytabForPrincipal(charliePrincipal).getAbsolutePath(), - "alice")) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - session.execute(select); - fail("Should have thrown AllNodesFailedException on login."); - } catch (AllNodesFailedException anfe) { - verifyException(anfe); - } - } - /** - * Validates that a query may be successfully made as user 'alice' using a {@link CqlSession} that - * is authenticated to user 'steve' using {@link PlainTextAuthProvider} assuming steve has - * PROXY.EXECUTE authorization on alice. - */ - @Test - public void should_allow_plain_text_authorized_user_to_execute_as() { - try (CqlSession session = - SessionUtils.newSession( - ads.ccm, - SessionUtils.configLoaderBuilder() - .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, "steve") - .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, "fakePasswordForSteve") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build())) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - SimpleStatement statementAsAlice = ProxyAuthentication.executeAs("alice", select); - ResultSet set = session.execute(statementAsAlice); - assertThat(set).isNotNull(); - } - } - /** - * Validates that a query may be successfully made as user 'alice' using a {@link CqlSession} that - * is authenticated to principal 'charlie@DATASTAX.COM' using {@link DseGssApiAuthProvider} - * assuming charlie@DATASTAX.COM has PROXY.EXECUTE authorization on alice. - */ - @Test - public void should_allow_kerberos_authorized_user_to_execute_as() { - try (CqlSession session = - ads.newKeyTabSession( - charliePrincipal, ads.getKeytabForPrincipal(charliePrincipal).getAbsolutePath())) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - SimpleStatement statementAsAlice = ProxyAuthentication.executeAs("alice", select); - ResultSet set = session.execute(statementAsAlice); - assertThat(set).isNotNull(); - } - } - /** - * Validates that a query may not be made as user 'alice' using a {@link CqlSession} that is - * authenticated to user 'ben' if ben does not have PROXY.EXECUTE authorization on alice. - */ - @Test - public void should_not_allow_plain_text_unauthorized_user_to_execute_as() { - try (CqlSession session = - SessionUtils.newSession( - ads.ccm, - SessionUtils.configLoaderBuilder() - .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, "ben") - .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, "fakePasswordForBen") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build())) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - SimpleStatement statementAsAlice = ProxyAuthentication.executeAs("alice", select); - session.execute(statementAsAlice); - fail("Should have thrown UnauthorizedException on executeAs."); - } catch (UnauthorizedException ue) { - verifyException(ue, "ben"); - } - } - /** - * Validates that a query may not be made as user 'alice' using a {@link CqlSession} that is - * authenticated to principal 'bob@DATASTAX.COM' using {@link DseGssApiAuthProvider} if - * bob@DATASTAX.COM does not have PROXY.EXECUTE authorization on alice. - */ - @Test - public void should_not_allow_kerberos_unauthorized_user_to_execute_as() { - try (CqlSession session = - ads.newKeyTabSession( - bobPrincipal, ads.getKeytabForPrincipal(bobPrincipal).getAbsolutePath())) { - SimpleStatement select = SimpleStatement.builder("select * from aliceks.alicetable").build(); - SimpleStatement statementAsAlice = ProxyAuthentication.executeAs("alice", select); - session.execute(statementAsAlice); - fail("Should have thrown UnauthorizedException on executeAs."); - } catch (UnauthorizedException ue) { - verifyException(ue, "bob@DATASTAX.COM"); - } - } - - private void verifyException(AllNodesFailedException anfe) { - assertThat(anfe.getAllErrors()).hasSize(1); - List errors = anfe.getAllErrors().values().iterator().next(); - assertThat(errors).hasSize(1); - Throwable firstError = errors.get(0); - assertThat(firstError) - .isInstanceOf(AuthenticationException.class) - .hasMessageContaining( - "Authentication error on node /127.0.0.1:9042: " - + "server replied with 'Failed to login. Please re-try.' to AuthResponse request"); - } - - private void verifyException(UnauthorizedException ue, String user) { - assertThat(ue.getMessage()) - .contains( - String.format( - "Either '%s' does not have permission to execute queries as 'alice' " - + "or that role does not exist.", - user)); - } -} diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/EmbeddedAds.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/EmbeddedAds.java deleted file mode 100644 index 5ca751e9151..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/EmbeddedAds.java +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import com.datastax.oss.driver.shaded.guava.common.base.Preconditions; -import com.datastax.oss.driver.shaded.guava.common.collect.Maps; -import com.datastax.oss.driver.shaded.guava.common.collect.Sets; -import com.datastax.oss.driver.shaded.guava.common.io.Files; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.UnknownHostException; -import java.nio.charset.Charset; -import java.util.Collections; -import java.util.Map; -import java.util.UUID; -import org.apache.directory.api.ldap.model.constants.SchemaConstants; -import org.apache.directory.api.ldap.model.constants.SupportedSaslMechanisms; -import org.apache.directory.api.ldap.model.csn.CsnFactory; -import org.apache.directory.api.ldap.model.entry.Entry; -import org.apache.directory.api.ldap.model.exception.LdapException; -import org.apache.directory.api.ldap.model.exception.LdapInvalidDnException; -import org.apache.directory.api.ldap.model.name.Dn; -import org.apache.directory.api.ldap.model.schema.SchemaManager; -import org.apache.directory.api.ldap.schemamanager.impl.DefaultSchemaManager; -import org.apache.directory.server.constants.ServerDNConstants; -import org.apache.directory.server.core.DefaultDirectoryService; -import org.apache.directory.server.core.api.CacheService; -import org.apache.directory.server.core.api.DirectoryService; -import org.apache.directory.server.core.api.DnFactory; -import org.apache.directory.server.core.api.InstanceLayout; -import org.apache.directory.server.core.api.schema.SchemaPartition; -import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor; -import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition; -import org.apache.directory.server.core.partition.ldif.LdifPartition; -import org.apache.directory.server.core.shared.DefaultDnFactory; -import org.apache.directory.server.kerberos.KerberosConfig; -import org.apache.directory.server.kerberos.kdc.KdcServer; -import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory; -import org.apache.directory.server.kerberos.shared.keytab.Keytab; -import org.apache.directory.server.kerberos.shared.keytab.KeytabEntry; -import org.apache.directory.server.ldap.LdapServer; -import org.apache.directory.server.ldap.handlers.sasl.MechanismHandler; -import org.apache.directory.server.ldap.handlers.sasl.cramMD5.CramMd5MechanismHandler; -import org.apache.directory.server.ldap.handlers.sasl.digestMD5.DigestMd5MechanismHandler; -import org.apache.directory.server.ldap.handlers.sasl.gssapi.GssapiMechanismHandler; -import org.apache.directory.server.ldap.handlers.sasl.plain.PlainMechanismHandler; -import org.apache.directory.server.protocol.shared.transport.TcpTransport; -import org.apache.directory.server.protocol.shared.transport.UdpTransport; -import org.apache.directory.shared.kerberos.KerberosTime; -import org.apache.directory.shared.kerberos.codec.types.EncryptionType; -import org.apache.directory.shared.kerberos.components.EncryptionKey; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A convenience utility for running an Embedded Apache Directory Service with LDAP and optionally a - * Kerberos Key Distribution Server. By default listens for LDAP on 10389 and Kerberos on 60088. You - * can use something like Apache Directory Studio - * to verify the server is configured and running correctly by connecting to localhost:10389 with - * username 'uid=admin,ou=system' and password 'secret'. - * - *

Note: This should only be used for development and testing purposes. - */ -public class EmbeddedAds { - - private static final Logger LOG = LoggerFactory.getLogger(EmbeddedAds.class); - - private final String dn; - - private final String realm; - - private int kdcPort; - - private int ldapPort; - - private final boolean kerberos; - - private InetAddress address; - - private String hostname; - - private File confDir; - - private volatile boolean isInit = false; - - private DirectoryService service; - - private LdapServer ldapServer; - - private KdcServer kdcServer; - - private Dn usersDN; - - private File krb5Conf; - - private EmbeddedAds( - String dn, - String realm, - String address, - int ldapPort, - boolean kerberos, - int kdcPort, - File confDir) { - this.dn = dn; - this.realm = realm; - try { - this.address = InetAddress.getByName(address); - } catch (UnknownHostException e) { - LOG.error("Failure resolving address '{}', falling back to loopback.", address, e); - this.address = InetAddress.getLoopbackAddress(); - } - this.hostname = this.address.getHostName().toLowerCase(); - this.ldapPort = ldapPort; - this.kerberos = kerberos; - this.kdcPort = kdcPort; - this.confDir = confDir; - } - - public void start() throws Exception { - if (isInit) { - return; - } - isInit = true; - File workDir = Files.createTempDir(); - // Set confDir = workDir if not defined. - if (confDir == null) { - confDir = workDir; - } - - if (kerberos) { - kdcPort = kdcPort != -1 ? kdcPort : findAvailablePort(60088); - - // Set system properties required for kerberos auth to work. Unfortunately admin_server - // cannot be expressed via System properties (like realm and kdc can), thus we must create a - // config file. - krb5Conf = createKrb5Conf(); - - System.setProperty("java.security.krb5.conf", krb5Conf.getAbsolutePath()); - // Useful options for debugging. - // System.setProperty("sun.security.krb5.debug", "true"); - // System.setProperty("java.security.debug", "configfile,configparser,gssloginconfig"); - } - - // Initialize service and set its filesystem layout. - service = new DefaultDirectoryService(); - InstanceLayout layout = new InstanceLayout(workDir); - service.setInstanceLayout(layout); - - // Disable ChangeLog as we don't need change tracking. - service.getChangeLog().setEnabled(false); - // Denormalizes attribute DNs to be human readable, i.e uid=admin,ou=system instead of - // 0.9.2.3=admin,2.5=system) - service.setDenormalizeOpAttrsEnabled(true); - - // Create and init cache service which will be used for caching DNs, among other things. - CacheService cacheService = new CacheService(); - cacheService.initialize(layout); - - // Create and load SchemaManager which will create the default schema partition. - SchemaManager schemaManager = new DefaultSchemaManager(); - service.setSchemaManager(schemaManager); - schemaManager.loadAllEnabled(); - - // Create SchemaPartition from schema manager and load ldif from schema directory. - SchemaPartition schemaPartition = new SchemaPartition(schemaManager); - LdifPartition ldifPartition = new LdifPartition(schemaManager, service.getDnFactory()); - ldifPartition.setPartitionPath(new File(layout.getPartitionsDirectory(), "schema").toURI()); - schemaPartition.setWrappedPartition(ldifPartition); - service.setSchemaPartition(schemaPartition); - - // Create a DN factory which can be used to create and cache DNs. - DnFactory dnFactory = new DefaultDnFactory(schemaManager, cacheService.getCache("dnCache")); - service.setDnFactory(dnFactory); - - // Create mandatory system partition. This is used for storing server configuration. - JdbmPartition systemPartition = - createPartition("system", dnFactory.create(ServerDNConstants.SYSTEM_DN)); - service.setSystemPartition(systemPartition); - - // Now that we have a schema and system partition, start up the directory service. - service.startup(); - - // Create partition where user, tgt and ldap principals will live. - Dn partitionDn = dnFactory.create(dn); - String dnName = partitionDn.getRdn().getValue().getString(); - JdbmPartition partition = createPartition(dnName, partitionDn); - - // Add a context entry so the partition can be referenced by entries. - Entry context = service.newEntry(partitionDn); - context.add("objectClass", "top", "domain", "extensibleObject"); - context.add(partitionDn.getRdn().getType(), dnName); - partition.setContextEntry(context); - service.addPartition(partition); - - // Create users domain. - usersDN = partitionDn.add(dnFactory.create("ou=users")); - Entry usersEntry = service.newEntry(usersDN); - usersEntry.add("objectClass", "organizationalUnit", "top"); - usersEntry.add("ou", "users"); - if (kerberos) { - usersEntry = kerberize(usersEntry); - } - service.getAdminSession().add(usersEntry); - - // Uncomment to allow to connect to ldap server without credentials for convenience. - // service.setAllowAnonymousAccess(true); - - startLdap(); - - // Create sasl and krbtgt principals and start KDC if kerberos is enabled. - if (kerberos) { - // Ticket Granting Ticket entry. - Dn tgtDN = usersDN.add(dnFactory.create("uid=krbtgt")); - String servicePrincipal = "krbtgt/" + realm + "@" + realm; - Entry tgtEntry = service.newEntry(tgtDN); - tgtEntry.add( - "objectClass", - "person", - "inetOrgPerson", - "top", - "krb5KDCEntry", - "uidObject", - "krb5Principal"); - tgtEntry.add("krb5KeyVersionNumber", "0"); - tgtEntry.add("krb5PrincipalName", servicePrincipal); - tgtEntry.add("uid", "krbtgt"); - tgtEntry.add("userPassword", "secret"); - tgtEntry.add("sn", "Service"); - tgtEntry.add("cn", "KDC Service"); - service.getAdminSession().add(kerberize(tgtEntry)); - - // LDAP SASL principal. - String saslPrincipal = "ldap/" + hostname + "@" + realm; - ldapServer.setSaslPrincipal(saslPrincipal); - Dn ldapDN = usersDN.add(dnFactory.create("uid=ldap")); - Entry ldapEntry = service.newEntry(ldapDN); - ldapEntry.add( - "objectClass", - "top", - "person", - "inetOrgPerson", - "krb5KDCEntry", - "uidObject", - "krb5Principal"); - ldapEntry.add("krb5KeyVersionNumber", "0"); - ldapEntry.add("krb5PrincipalName", saslPrincipal); - ldapEntry.add("uid", "ldap"); - ldapEntry.add("userPassword", "secret"); - ldapEntry.add("sn", "Service"); - ldapEntry.add("cn", "LDAP Service"); - service.getAdminSession().add(kerberize(ldapEntry)); - - startKDC(servicePrincipal); - } - } - - public boolean isStarted() { - return this.isInit; - } - - private File createKrb5Conf() throws IOException { - File krb5Conf = new File(confDir, "krb5.conf"); - String config = - String.format( - "[libdefaults]%n" - + "default_realm = %s%n" - + "default_tgs_enctypes = aes128-cts-hmac-sha1-96 aes256-cts-hmac-sha1-96%n%n" - + "[realms]%n" - + "%s = {%n" - + " kdc = %s:%d%n" - + " admin_server = %s:%d%n" - + "}%n", - realm, realm, hostname, kdcPort, hostname, kdcPort); - - try (FileOutputStream fios = new FileOutputStream(krb5Conf)) { - PrintWriter pw = - new PrintWriter( - new BufferedWriter(new OutputStreamWriter(fios, Charset.defaultCharset()))); - pw.write(config); - pw.close(); - } - return krb5Conf; - } - - /** - * @return A specialized krb5.conf file that defines and defaults to the domain expressed by this - * server. - */ - public File getKrb5Conf() { - return krb5Conf; - } - - /** - * Adds a user with the given password and principal name and creates a keytab file for - * authenticating with that user's principal. - * - * @param user Username to login with (i.e. cassandra). - * @param password Password to authenticate with. - * @param principal Principal representing the server (i.e. cassandra@DATASTAX.COM). - * @return Generated keytab file for this user. - */ - public File addUserAndCreateKeytab(String user, String password, String principal) - throws IOException, LdapException { - addUser(user, password, principal); - return createKeytab(user, password, principal); - } - - /** - * Creates a keytab file for authenticating with a given principal. - * - * @param user Username to login with (i.e. cassandra). - * @param password Password to authenticate with. - * @param principal Principal representing the server (i.e. cassandra@DATASTAX.COM). - * @return Generated keytab file for this user. - */ - public File createKeytab(String user, String password, String principal) throws IOException { - File keytabFile = new File(confDir, user + ".keytab"); - Keytab keytab = Keytab.getInstance(); - - KerberosTime timeStamp = new KerberosTime(System.currentTimeMillis()); - - Map keys = - KerberosKeyFactory.getKerberosKeys(principal, password); - - KeytabEntry keytabEntry = - new KeytabEntry( - principal, 0, timeStamp, (byte) 0, keys.get(EncryptionType.AES128_CTS_HMAC_SHA1_96)); - - keytab.setEntries(Collections.singletonList(keytabEntry)); - keytab.write(keytabFile); - return keytabFile; - } - - /** - * Adds a user with the given password, does not create necessary kerberos attributes. - * - * @param user Username to login with (i.e. cassandra). - * @param password Password to authenticate with. - */ - public void addUser(String user, String password) throws LdapException { - addUser(user, password, null); - } - - /** - * Adds a user with the given password and principal. If principal is specified and kerberos is - * enabled, user is created with the necessary attributes to authenticate with kerberos (entryCsn, - * entryUuid, etc.). - * - * @param user Username to login with (i.e. cassandra). - * @param password Password to authenticate with. - * @param principal Principal representing the server (i.e. cassandra@DATASTAX.COM). - */ - public void addUser(String user, String password, String principal) throws LdapException { - Preconditions.checkState(isInit); - Dn userDN = usersDN.add("uid=" + user); - Entry userEntry = service.newEntry(userDN); - if (kerberos && principal != null) { - userEntry.add( - "objectClass", - "organizationalPerson", - "person", - "extensibleObject", - "inetOrgPerson", - "top", - "krb5KDCEntry", - "uidObject", - "krb5Principal"); - userEntry.add("krb5KeyVersionNumber", "0"); - userEntry.add("krb5PrincipalName", principal); - userEntry = kerberize(userEntry); - } else { - userEntry.add( - "objectClass", - "organizationalPerson", - "person", - "extensibleObject", - "inetOrgPerson", - "top", - "uidObject"); - } - userEntry.add("uid", user); - userEntry.add("sn", user); - userEntry.add("cn", user); - userEntry.add("userPassword", password); - service.getAdminSession().add(userEntry); - } - - /** Stops the server(s) if running. */ - public void stop() { - if (ldapServer != null) { - ldapServer.stop(); - } - if (kdcServer != null) { - kdcServer.stop(); - } - } - - /** @return The evaluated hostname that the server is listening with. */ - public String getHostname() { - return this.hostname; - } - - /** - * Adds attributes to the given Entry which will enable krb5key attributes to be added to them. - * - * @param entry Entry to add attributes to. - * @return The provided entry. - */ - private Entry kerberize(Entry entry) throws LdapException { - // Add csn and uuids for kerberos, this is needed to generate krb5keys. - entry.add(SchemaConstants.ENTRY_CSN_AT, new CsnFactory(0).newInstance().toString()); - entry.add(SchemaConstants.ENTRY_UUID_AT, UUID.randomUUID().toString()); - return entry; - } - - /** - * Creates a {@link JdbmPartition} with the given id and DN. - * - * @param id Id to create partition with. - * @param dn Distinguished Name to use to create partition. - * @return Created partition. - */ - private JdbmPartition createPartition(String id, Dn dn) throws LdapInvalidDnException { - JdbmPartition partition = new JdbmPartition(service.getSchemaManager(), service.getDnFactory()); - partition.setId(id); - partition.setPartitionPath( - new File(service.getInstanceLayout().getPartitionsDirectory(), id).toURI()); - partition.setSuffixDn(dn); - partition.setSchemaManager(service.getSchemaManager()); - return partition; - } - - /** Starts the LDAP Server with SASL enabled. */ - private void startLdap() throws Exception { - // Create and start LDAP server. - ldapServer = new LdapServer(); - - // Enable SASL layer, this is useful with or without kerberos. - Map mechanismHandlerMap = Maps.newHashMap(); - mechanismHandlerMap.put(SupportedSaslMechanisms.PLAIN, new PlainMechanismHandler()); - mechanismHandlerMap.put(SupportedSaslMechanisms.CRAM_MD5, new CramMd5MechanismHandler()); - mechanismHandlerMap.put(SupportedSaslMechanisms.DIGEST_MD5, new DigestMd5MechanismHandler()); - // GSSAPI is required for kerberos. - mechanismHandlerMap.put(SupportedSaslMechanisms.GSSAPI, new GssapiMechanismHandler()); - ldapServer.setSaslMechanismHandlers(mechanismHandlerMap); - ldapServer.setSaslHost(hostname); - // Realms only used by DIGEST_MD5 and GSSAPI. - ldapServer.setSaslRealms(Collections.singletonList(realm)); - ldapServer.setSearchBaseDn(dn); - - ldapPort = ldapPort != -1 ? ldapPort : findAvailablePort(10389); - ldapServer.setTransports(new TcpTransport(address.getHostAddress(), ldapPort)); - ldapServer.setDirectoryService(service); - if (kerberos) { - // Add an interceptor to attach krb5keys to created principals. - KeyDerivationInterceptor interceptor = new KeyDerivationInterceptor(); - interceptor.init(service); - service.addLast(interceptor); - } - ldapServer.start(); - } - - /** - * Starts the Kerberos Key Distribution Server supporting AES128 using the given principal for the - * Ticket-granting ticket. - * - * @param servicePrincipal TGT principcal service. - */ - private void startKDC(String servicePrincipal) throws Exception { - KerberosConfig config = new KerberosConfig(); - // We choose AES128_CTS_HMAC_SHA1_96 for our generated keytabs so we don't need JCE. - config.setEncryptionTypes(Sets.newHashSet(EncryptionType.AES128_CTS_HMAC_SHA1_96)); - config.setSearchBaseDn(dn); - config.setServicePrincipal(servicePrincipal); - - kdcServer = new KdcServer(config); - kdcServer.setDirectoryService(service); - - kdcServer.setTransports( - new TcpTransport(address.getHostAddress(), kdcPort), - new UdpTransport(address.getHostAddress(), kdcPort)); - kdcServer.start(); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private String dn = "dc=datastax,dc=com"; - - private String realm = "DATASTAX.COM"; - - private boolean kerberos = false; - - private int kdcPort = -1; - - private int ldapPort = -1; - - private String address = "127.0.0.1"; - - private File confDir = null; - - private Builder() {} - - public EmbeddedAds build() { - return new EmbeddedAds(dn, realm, address, ldapPort, kerberos, kdcPort, confDir); - } - - /** - * Configures the base DN to create users under. Defaults to dc=datastax,dc=com. - */ - public Builder withBaseDn(String dn) { - this.dn = dn; - return this; - } - - /** Configures the realm to use for SASL and Kerberos. Defaults to DATASTAX.COM. */ - public Builder withRealm(String realm) { - this.realm = realm; - return this; - } - - /** - * Sets the directory where krb5.conf and generated keytabs are created. Defaults to current - * directory. - */ - public Builder withConfDir(File confDir) { - this.confDir = confDir; - return this; - } - - /** - * Configures the port to use for LDAP. Defaults to the first available port from 10389+. Must - * be greater than 0. - */ - public Builder withLdapPort(int port) { - Preconditions.checkArgument(port > 0); - this.ldapPort = port; - return this; - } - - /** - * Configures the port to use for Kerberos KDC. Defaults to the first available port for 60088+. - * Must be greater than 0. - */ - public Builder withKerberos(int port) { - Preconditions.checkArgument(port > 0); - this.kdcPort = port; - return withKerberos(); - } - - /** - * Configures the server to run with a Kerberos KDC using the first available port for 60088+. - */ - public Builder withKerberos() { - this.kerberos = true; - return this; - } - - /** - * Configures the server to be configured to listen with the given address. Defaults to - * 127.0.0.1. You shouldn't need to change this. - */ - public Builder withAddress(String address) { - this.address = address; - return this; - } - } - - private static int findAvailablePort(int startingWith) { - IOException last = null; - for (int port = startingWith; port < startingWith + 100; port++) { - try { - ServerSocket s = new ServerSocket(port); - s.close(); - return port; - } catch (IOException e) { - last = e; - } - } - // If for whatever reason a port could not be acquired throw the last encountered exception. - throw new RuntimeException("Could not acquire an available port", last); - } -} diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/EmbeddedAdsRule.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/EmbeddedAdsRule.java deleted file mode 100644 index a57e349a51b..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/EmbeddedAdsRule.java +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import com.datastax.dse.driver.api.core.config.DseDriverOption; -import com.datastax.dse.driver.internal.core.auth.DseGssApiAuthProvider; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.testinfra.ccm.CustomCcmRule; -import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirementRule; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; -import java.io.File; -import java.util.HashMap; -import java.util.Map; -import org.junit.AssumptionViolatedException; -import org.junit.rules.ExternalResource; -import org.junit.runner.Description; -import org.junit.runners.model.Statement; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A testing rule that wraps the EmbeddedAds server, and ccmRule into one rule This is needed - * because ccm needs to be aware of the kerberos server settings prior to it's initialization. - */ -public class EmbeddedAdsRule extends ExternalResource { - - private static final Logger LOG = LoggerFactory.getLogger(EmbeddedAdsRule.class); - - public CustomCcmRule ccm; - // Realm for the KDC. - private final String realm = "DATASTAX.COM"; - private final String address = "127.0.0.1"; - - private final EmbeddedAds adsServer = - EmbeddedAds.builder().withKerberos().withRealm(realm).withAddress(address).build(); - - // Principal for DSE service ( = kerberos_options.service_principal) - private final String servicePrincipal = "dse/" + adsServer.getHostname() + "@" + realm; - - // A non-standard principal for DSE service, to test SASL protocol names - private final String alternateServicePrincipal = - "alternate/" + adsServer.getHostname() + "@" + realm; - - // Principal for the default cassandra user. - private final String userPrincipal = "cassandra@" + realm; - - // Principal for a user that doesn't exist. - private final String unknownPrincipal = "unknown@" + realm; - - // Keytabs to use for auth. - private static File userKeytab; - private static File unknownKeytab; - private static File dseKeytab; - private static File alternateKeytab; - private static Map customKeytabs = new HashMap<>(); - - private boolean alternate = false; - - public EmbeddedAdsRule(boolean alternate) { - this.alternate = alternate; - } - - public EmbeddedAdsRule() { - this(false); - } - - @Override - protected void before() { - try { - if (adsServer.isStarted()) { - return; - } - // Start ldap/kdc server. - adsServer.start(); - - // Create users and keytabs for the DSE principal and cassandra user. - dseKeytab = adsServer.addUserAndCreateKeytab("dse", "fakePasswordForTests", servicePrincipal); - alternateKeytab = - adsServer.addUserAndCreateKeytab( - "alternate", "fakePasswordForTests", alternateServicePrincipal); - userKeytab = - adsServer.addUserAndCreateKeytab("cassandra", "fakePasswordForTests", userPrincipal); - unknownKeytab = adsServer.createKeytab("unknown", "fakePasswordForTests", unknownPrincipal); - - String authenticationOptions = - "" - + "authentication_options:\n" - + " enabled: true\n" - + " default_scheme: kerberos\n" - + " other_schemes:\n" - + " - internal"; - - if (alternate) { - ccm = - CustomCcmRule.builder() - .withCassandraConfiguration( - "authorizer", "com.datastax.bdp.cassandra.auth.DseAuthorizer") - .withCassandraConfiguration( - "authenticator", "com.datastax.bdp.cassandra.auth.DseAuthenticator") - .withDseConfiguration("authorization_options.enabled", true) - .withDseConfiguration(authenticationOptions) - .withDseConfiguration("kerberos_options.qop", "auth-conf") - .withDseConfiguration( - "kerberos_options.keytab", getAlternateKeytab().getAbsolutePath()) - .withDseConfiguration( - "kerberos_options.service_principal", "alternate/_HOST@" + getRealm()) - .withJvmArgs( - "-Dcassandra.superuser_setup_delay_ms=0", - "-Djava.security.krb5.conf=" + getAdsServer().getKrb5Conf().getAbsolutePath()) - .build(); - } else { - ccm = - CustomCcmRule.builder() - .withCassandraConfiguration( - "authorizer", "com.datastax.bdp.cassandra.auth.DseAuthorizer") - .withCassandraConfiguration( - "authenticator", "com.datastax.bdp.cassandra.auth.DseAuthenticator") - .withDseConfiguration("authorization_options.enabled", true) - .withDseConfiguration(authenticationOptions) - .withDseConfiguration("kerberos_options.qop", "auth") - .withDseConfiguration("kerberos_options.keytab", getDseKeytab().getAbsolutePath()) - .withDseConfiguration( - "kerberos_options.service_principal", "dse/_HOST@" + getRealm()) - .withJvmArgs( - "-Dcassandra.superuser_setup_delay_ms=0", - "-Djava.security.krb5.conf=" + getAdsServer().getKrb5Conf().getAbsolutePath()) - .build(); - } - ccm.getCcmBridge().create(); - ccm.getCcmBridge().start(); - - } catch (Exception e) { - LOG.error("Unable to start ads server ", e); - } - } - - @Override - public Statement apply(Statement base, Description description) { - if (BackendRequirementRule.meetsDescriptionRequirements(description)) { - return super.apply(base, description); - } else { - // requirements not met, throw reasoning assumption to skip test - return new Statement() { - @Override - public void evaluate() { - throw new AssumptionViolatedException( - BackendRequirementRule.buildReasonString(description)); - } - }; - } - } - - @Override - protected void after() { - adsServer.stop(); - ccm.getCcmBridge().stop(); - } - - public CqlSession newKeyTabSession(String userPrincipal, String keytabPath) { - return SessionUtils.newSession( - getCcm(), - SessionUtils.configLoaderBuilder() - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, DseGssApiAuthProvider.class) - .withStringMap( - DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION, - ImmutableMap.of( - "principal", - userPrincipal, - "useKeyTab", - "true", - "refreshKrb5Config", - "true", - "keyTab", - keytabPath)) - .build()); - } - - public CqlSession newKeyTabSession(String userPrincipal, String keytabPath, String authId) { - return SessionUtils.newSession( - getCcm(), - SessionUtils.configLoaderBuilder() - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, DseGssApiAuthProvider.class) - .withStringMap( - DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION, - ImmutableMap.of( - "principal", - userPrincipal, - "useKeyTab", - "true", - "refreshKrb5Config", - "true", - "keyTab", - keytabPath)) - .withString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, authId) - .build()); - } - - public CqlSession newKeyTabSession() { - return newKeyTabSession(getUserPrincipal(), getUserKeytab().getAbsolutePath()); - } - - public CqlSession newTicketSession() { - return SessionUtils.newSession( - getCcm(), - SessionUtils.configLoaderBuilder() - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, DseGssApiAuthProvider.class) - .withStringMap( - DseDriverOption.AUTH_PROVIDER_LOGIN_CONFIGURATION, - ImmutableMap.of( - "principal", - userPrincipal, - "useTicketCache", - "true", - "refreshKrb5Config", - "true", - "renewTGT", - "true")) - .build()); - } - - public CustomCcmRule getCcm() { - return ccm; - } - - public String getRealm() { - return realm; - } - - public String getAddress() { - return address; - } - - public EmbeddedAds getAdsServer() { - return adsServer; - } - - public String getServicePrincipal() { - return servicePrincipal; - } - - public String getAlternateServicePrincipal() { - return alternateServicePrincipal; - } - - public String getUserPrincipal() { - return userPrincipal; - } - - public String getUnknownPrincipal() { - return unknownPrincipal; - } - - public File getUserKeytab() { - return userKeytab; - } - - public File getUnknownKeytab() { - return unknownKeytab; - } - - public File getDseKeytab() { - return dseKeytab; - } - - public File getAlternateKeytab() { - return alternateKeytab; - } - - public String addUserAndCreateKeyTab(String user, String password) { - String principal = user + "@" + realm; - try { - File keytabFile = adsServer.addUserAndCreateKeytab(user, password, principal); - customKeytabs.put(principal, keytabFile); - } catch (Exception e) { - LOG.error("Unable to add user and create keytab for " + user + " ", e); - } - return principal; - } - - public File getKeytabForPrincipal(String prinicipal) { - return customKeytabs.get(prinicipal); - } -} diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/KerberosUtils.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/KerberosUtils.java deleted file mode 100644 index 5d385b51c92..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/auth/KerberosUtils.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.auth; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap; -import java.io.File; -import java.io.IOException; -import java.util.Map; -import org.apache.commons.exec.CommandLine; -import org.apache.commons.exec.DefaultExecutor; -import org.apache.commons.exec.Executor; - -public class KerberosUtils { - /** - * Executes the given command with KRB5_CONFIG environment variable pointing to the specialized - * config file for the embedded KDC server. - */ - public static void executeCommand(String command, EmbeddedAds adsServer) throws IOException { - Map environmentMap = - ImmutableMap.builder() - .put("KRB5_CONFIG", adsServer.getKrb5Conf().getAbsolutePath()) - .build(); - CommandLine cli = CommandLine.parse(command); - Executor executor = new DefaultExecutor(); - int retValue = executor.execute(cli, environmentMap); - assertThat(retValue).isZero(); - } - - /** - * Acquires a ticket into the cache with the tgt using kinit command with the given principal and - * keytab file. - */ - public static void acquireTicket(String principal, File keytab, EmbeddedAds adsServer) - throws IOException { - executeCommand( - String.format("kinit -t %s -k %s", keytab.getAbsolutePath(), principal), adsServer); - } - - /** Destroys all tickets in the cache with given principal. */ - public static void destroyTicket(EmbeddedAdsRule ads) throws IOException { - executeCommand("kdestroy", ads.getAdsServer()); - } -} diff --git a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/graph/GraphAuthenticationIT.java b/integration-tests/src/test/java/com/datastax/dse/driver/api/core/graph/GraphAuthenticationIT.java deleted file mode 100644 index de1c23fd661..00000000000 --- a/integration-tests/src/test/java/com/datastax/dse/driver/api/core/graph/GraphAuthenticationIT.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License 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.datastax.dse.driver.api.core.graph; - -import static com.datastax.dse.driver.api.core.graph.TinkerGraphAssertions.assertThat; - -import com.datastax.dse.driver.api.core.config.DseDriverOption; -import com.datastax.oss.driver.api.core.CqlSession; -import com.datastax.oss.driver.api.core.Version; -import com.datastax.oss.driver.api.core.config.DefaultDriverOption; -import com.datastax.oss.driver.api.core.config.DriverConfigLoader; -import com.datastax.oss.driver.api.testinfra.ccm.CustomCcmRule; -import com.datastax.oss.driver.api.testinfra.requirement.BackendRequirement; -import com.datastax.oss.driver.api.testinfra.requirement.BackendType; -import com.datastax.oss.driver.api.testinfra.session.SessionUtils; -import com.datastax.oss.driver.internal.core.auth.PlainTextAuthProvider; -import com.datastax.oss.driver.shaded.guava.common.util.concurrent.Uninterruptibles; -import java.util.concurrent.TimeUnit; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; - -@BackendRequirement( - type = BackendType.DSE, - minInclusive = "5.0.0", - description = "DSE 5 required for Graph") -public class GraphAuthenticationIT { - - @ClassRule - public static CustomCcmRule ccm = - CustomCcmRule.builder() - .withDseConfiguration("authentication_options.enabled", true) - .withJvmArgs("-Dcassandra.superuser_setup_delay_ms=0") - .withDseWorkloads("graph") - .build(); - - @BeforeClass - public static void sleepForAuth() { - if (ccm.getCassandraVersion().compareTo(Version.V2_2_0) < 0) { - // Sleep for 1 second to allow C* auth to do its work. This is only needed for 2.1 - Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); - } - } - - @Test - public void should_execute_graph_query_on_authenticated_connection() { - CqlSession dseSession = - SessionUtils.newSession( - ccm, - DriverConfigLoader.programmaticBuilder() - .withString(DseDriverOption.AUTH_PROVIDER_AUTHORIZATION_ID, "") - .withString(DefaultDriverOption.AUTH_PROVIDER_USER_NAME, "cassandra") - .withString(DefaultDriverOption.AUTH_PROVIDER_PASSWORD, "cassandra") - .withClass(DefaultDriverOption.AUTH_PROVIDER_CLASS, PlainTextAuthProvider.class) - .build()); - - GraphNode gn = - dseSession.execute(ScriptGraphStatement.newInstance("1+1").setSystemQuery(true)).one(); - assertThat(gn).isNotNull(); - assertThat(gn.asInt()).isEqualTo(2); - } -} diff --git a/integration-tests/src/test/resources/logback-test.xml b/integration-tests/src/test/resources/logback-test.xml index a2179e4357b..48f53406353 100644 --- a/integration-tests/src/test/resources/logback-test.xml +++ b/integration-tests/src/test/resources/logback-test.xml @@ -27,7 +27,6 @@ - diff --git a/manual/core/authentication/README.md b/manual/core/authentication/README.md index 516e47f558f..49ff6b3e6ac 100644 --- a/manual/core/authentication/README.md +++ b/manual/core/authentication/README.md @@ -22,7 +22,7 @@ under the License. ### Quick overview * `advanced.auth-provider` in the configuration. -* disabled by default. Also available: plain-text credentials, GSSAPI (DSE only), or write your own. +* disabled by default. Use plain-text credentials or write a custom provider. * can also be defined programmatically: [CqlSession.builder().withAuthCredentials][SessionBuilder.withAuthCredentials] or [CqlSession.builder().withAuthProvider][SessionBuilder.withAuthProvider]. @@ -64,44 +64,6 @@ datastax-java-driver { } ``` -When connecting to DSE, an optional `authorization-id` can also be specified. It will be used for -proxy authentication (logging in as another user or role). If you try to use this feature with an -authenticator that doesn't support it, the authorization id will be ignored. - -``` -datastax-java-driver { - advanced.auth-provider { - class = PlainTextAuthProvider - username = user - password = pass - authorization-id = otherUserOrRole - } -} -``` - -Note that, for backward compatibility with previous driver versions, you can also use the class name -`DsePlainTextAuthProvider` to enable this provider. - -#### GSSAPI (DSE only) - -`DseGssApiAuthProvider` supports GSSAPI authentication against a DSE cluster secured with Kerberos: - -``` -datastax-java-driver { - advanced.auth-provider { - class = DseGssApiAuthProvider - login-configuration { - principal = "user principal here ex cassandra@DATASTAX.COM" - useKeyTab = "true" - refreshKrb5Config = "true" - keyTab = "Path to keytab file here" - } - } - } -``` - -See the comments in [reference.conf] for more details. - #### Custom You can also write your own provider; it must implement [AuthProvider] and declare a public @@ -140,19 +102,13 @@ CqlSession session = .build(); ``` -For convenience, there are shortcuts that take the credentials directly: +For convenience, the credentials can be passed directly: ```java CqlSession session = CqlSession.builder() .withAuthCredentials("user", "pass") .build(); - -// With proxy authentication (DSE only) -CqlSession session = - CqlSession.builder() - .withAuthCredentials("user", "pass", "otherUserOrRole") - .build(); ``` One downside of the driver's built-in authentication providers is that the credentials are stored in @@ -160,99 +116,12 @@ clear text in memory; this means they are vulnerable to an attacker who is able dumps. If this is not acceptable for you, consider writing your own [AuthProvider] implementation; [PlainTextAuthProviderBase] is a good starting point. -Similarly, [ProgrammaticDseGssApiAuthProvider] lets you configure GSSAPI programmatically: - -```java -import com.datastax.dse.driver.api.core.auth.DseGssApiAuthProviderBase.GssApiOptions; - -javax.security.auth.Subject subject = ...; // do your Kerberos configuration here - -GssApiOptions options = GssApiOptions.builder().withSubject(subject).build(); -CqlSession session = CqlSession.builder() - .withAuthProvider(new ProgrammaticDseGssApiAuthProvider(options)) - .build(); -``` - -For more complex needs (e.g. if building the options once and reusing them doesn't work for you), -you can subclass [DseGssApiAuthProviderBase]. - -### Proxy authentication - -DSE allows a user to connect as another user or role: - -``` --- Allow bob to connect as alice: -GRANT PROXY.LOGIN ON ROLE 'alice' TO 'bob' -``` - -Once connected, all authorization checks will be performed against the proxy role (alice in this -example). - -To use proxy authentication with the driver, you need to provide the **authorization-id**, in other -words the name of the role you want to connect as. - -Example for plain text authentication: - -``` -datastax-java-driver { - advanced.auth-provider { - class = PlainTextAuthProvider - username = bob - password = bob's password - authorization-id = alice - } - } -``` - -With the GSSAPI (Kerberos) provider: - -``` -datastax-java-driver { - advanced.auth-provider { - class = DseGssApiAuthProvider - authorization-id = alice - login-configuration { - principal = "user principal here ex bob@DATASTAX.COM" - useKeyTab = "true" - refreshKrb5Config = "true" - keyTab = "Path to keytab file here" - } - } - } -``` - -### Proxy execution - -Proxy execution is similar to proxy authentication, but it applies to a single query, not the whole -session. - -``` --- Allow bob to execute queries as alice: -GRANT PROXY.EXECUTE ON ROLE 'alice' TO 'bob' -``` - -For this scenario, you would **not** add the `authorization-id = alice` to your configuration. -Instead, use [ProxyAuthentication.executeAs] to wrap your query with the correct authorization for -the execution: - -```java -import com.datastax.dse.driver.api.core.auth.ProxyAuthentication; - -SimpleStatement statement = SimpleStatement.newInstance("some query"); -// executeAs returns a new instance, you need to re-assign -statement = ProxyAuthentication.executeAs("alice", statement); -session.execute(statement); -``` - [SASL]: https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer [AuthProvider]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/auth/AuthProvider.html [DriverContext]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/context/DriverContext.html [PlainTextAuthProviderBase]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/auth/PlainTextAuthProviderBase.html [ProgrammaticPlainTextAuthProvider]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/auth/ProgrammaticPlainTextAuthProvider.html -[DseGssApiAuthProviderBase]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/dse/driver/api/core/auth/DseGssApiAuthProviderBase.html -[ProgrammaticDseGssApiAuthProvider]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/dse/driver/api/core/auth/ProgrammaticDseGssApiAuthProvider.html -[ProxyAuthentication.executeAs]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/dse/driver/api/core/auth/ProxyAuthentication.html#executeAs-java.lang.String-StatementT- [SessionBuilder.withAuthCredentials]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/session/SessionBuilder.html#withAuthCredentials-java.lang.String-java.lang.String- [SessionBuilder.withAuthProvider]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/core/session/SessionBuilder.html#withAuthProvider-com.datastax.oss.driver.api.core.auth.AuthProvider- [reference.conf]: ../configuration/reference/ diff --git a/pom.xml b/pom.xml index 76caea447cc..0605fb259e1 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,6 @@ 2.2.21 4.3.0 1.5.9 - 2.0.0-M19 3.5.5 22.0.0.2 false @@ -446,47 +445,6 @@ testng 7.12.0 - - org.apache.directory.server - apacheds-core - ${apacheds.version} - - - org.slf4j - slf4j-log4j12 - - - - - org.apache.directory.server - apacheds-protocol-kerberos - ${apacheds.version} - - - org.apache.directory.server - apacheds-interceptor-kerberos - ${apacheds.version} - - - org.apache.directory.server - apacheds-protocol-ldap - ${apacheds.version} - - - org.apache.directory.server - apacheds-ldif-partition - ${apacheds.version} - - - org.apache.directory.server - apacheds-jdbm-partition - ${apacheds.version} - - - org.apache.directory.api - api-ldap-codec-standalone - 2.1.7 - com.github.tomakehurst wiremock diff --git a/upgrade_guide/README.md b/upgrade_guide/README.md index 83963f94775..d67848d2465 100644 --- a/upgrade_guide/README.md +++ b/upgrade_guide/README.md @@ -605,10 +605,6 @@ changes right away; but you will get deprecation warnings: methods in this class now redirect to `DriverConfigLoader`. On that note, `dse-reference.conf` does not exist anymore, all the driver defaults are now in [reference.conf](../manual/core/configuration/reference/). -* plain-text authentication: there is now a single implementation that works with both Cassandra and - DSE. If you used `DseProgrammaticPlainTextAuthProvider`, replace it by - `PlainTextProgrammaticAuthProvider`. Similarly, if you wrote a custom implementation by - subclassing `DsePlainTextAuthProviderBase`, extend `PlainTextAuthProviderBase` instead. * `DseLoadBalancingPolicy`: DSE-specific features (the slow replica avoidance mechanism) have been merged into `DefaultLoadBalancingPolicy`. `DseLoadBalancingPolicy` still exists for backward compatibility, but it is now identical to the default policy.