/*
* 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.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.nio.ByteBuffer;
import java.nio.CharBuffer;
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) {
return new PlainTextAuthenticator(getCredentials(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;
public Credentials(@NonNull char[] username, @NonNull char[] password) {
this.username = Objects.requireNonNull(username);
this.password = Objects.requireNonNull(password);
}
@NonNull
public char[] getUsername() {
return username;
}
@NonNull
public char[] getPassword() {
return password;
}
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);
}
}
protected static class PlainTextAuthenticator implements SyncAuthenticator {
private final ByteBuffer initialToken;
protected PlainTextAuthenticator(@NonNull Credentials credentials) {
Objects.requireNonNull(credentials);
ByteBuffer usernameBytes = toUtf8Bytes(credentials.getUsername());
ByteBuffer passwordBytes = toUtf8Bytes(credentials.getPassword());
credentials.clear();
this.initialToken =
ByteBuffer.allocate(usernameBytes.remaining() + passwordBytes.remaining() + 2);
initialToken.put((byte) 0);
initialToken.put(usernameBytes);
initialToken.put((byte) 0);
initialToken.put(passwordBytes);
initialToken.flip();
// Clear temporary buffers
usernameBytes.rewind();
while (usernameBytes.remaining() > 0) {
usernameBytes.put((byte) 0);
}
passwordBytes.rewind();
while (passwordBytes.remaining() > 0) {
passwordBytes.put((byte) 0);
}
}
private ByteBuffer toUtf8Bytes(char[] charArray) {
CharBuffer charBuffer = CharBuffer.wrap(charArray);
return Charsets.UTF_8.encode(charBuffer);
}
@Override
@Nullable
public ByteBuffer initialResponseSync() {
return initialToken;
}
@Override
@Nullable
public ByteBuffer evaluateChallengeSync(@Nullable ByteBuffer token) {
return null;
}
@Override
public void onAuthenticationSuccessSync(@Nullable ByteBuffer token) {
// no-op, the server should send nothing anyway
}
}
}