/*
 * Copyright DataStax, Inc.
 *
 * Licensed 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.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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Common infrastructure for plain text auth providers.

This can be reused to write an implementation that retrieves the credentials from another source than the configuration.

/** * Common infrastructure for plain text auth providers. * * <p>This can be reused to write an implementation that retrieves the credentials from another * source than the configuration. */
@ThreadSafe public abstract class PlainTextAuthProviderBase implements AuthProvider { private static final Logger LOG = LoggerFactory.getLogger(PlainTextAuthProviderBase.class); private final String logPrefix;
Params:
  • 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). Built-in implementations fill this with Session.getName().
/** * @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). Built-in * implementations fill this with {@link Session#getName()}. */
protected PlainTextAuthProviderBase(@NonNull String logPrefix) { this.logPrefix = Objects.requireNonNull(logPrefix); }
Retrieves the credentials from the underlying source.

This is invoked every time the driver opens a new connection.

/** * Retrieves the credentials from the underlying source. * * <p>This is invoked every time the driver opens a new connection. */
@NonNull protected abstract Credentials getCredentials( @NonNull EndPoint endPoint, @NonNull String serverAuthenticator); @NonNull @Override public Authenticator newAuthenticator( @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) throws AuthenticationException { return new PlainTextAuthenticator( getCredentials(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 } 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.

/** * Builds an instance for username/password authentication, and proxy authentication with the * given authorizationId. * * <p>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) { this.username = Objects.requireNonNull(username); this.password = Objects.requireNonNull(password); this.authorizationId = Objects.requireNonNull(authorizationId); }
Builds an instance for simple username/password authentication.
/** Builds an instance for simple username/password authentication. */
public Credentials(@NonNull char[] username, @NonNull char[] password) { this(username, password, new char[0]); } @NonNull public char[] getUsername() { return username; }
Deprecated:this method only exists for backward compatibility. It is a synonym for getUsername(), which should be used instead.
/** * @deprecated this method only exists for backward compatibility. It is a synonym for {@link * #getUsername()}, which should be used instead. */
@Deprecated @NonNull public char[] getAuthenticationId() { return username; } @NonNull public char[] getPassword() { return password; } @NonNull public char[] getAuthorizationId() { return authorizationId; }
Clears the credentials from memory when they're no longer needed.
/** 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 // caches the credentials in memory. But it might be useful for a custom implementation that // 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 } }; private final ByteBuffer encodedCredentials; private final EndPoint endPoint; protected PlainTextAuthenticator( @NonNull Credentials credentials, @NonNull EndPoint endPoint, @NonNull String serverAuthenticator) { super(serverAuthenticator); 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); 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 PlainTextAuthenticator(Credentials, EndPoint, String) instead.
/** * @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); } private static void clear(ByteBuffer buffer) { buffer.rewind(); while (buffer.remaining() > 0) { buffer.put((byte) 0); } } @NonNull @Override public ByteBuffer getMechanism() { return MECHANISM; } @NonNull @Override public ByteBuffer getInitialServerChallenge() { return SERVER_INITIAL_CHALLENGE; } @Nullable @Override public ByteBuffer evaluateChallengeSync(@Nullable ByteBuffer challenge) { if (SERVER_INITIAL_CHALLENGE.equals(challenge)) { return encodedCredentials; } throw new AuthenticationException(endPoint, "Incorrect challenge from server"); } } }