Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kerberos auth (http client) #1541

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*.war
*.ear
*.out
.java-version

# VSCode
.bloop
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 0.6.1

- Supporting Kerberos auth in HTTP client.

### Bug Fixes

## 0.6.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ public static Map<ClickHouseOption, Serializable> toClientOptions(Map<?, ?> prop
private final String keyStoreType;
private final String trustStore;
private final String trustStorePassword;
private final boolean gssEnabled;
private final String kerberosServerName;
private final int transactionTimeout;
private final boolean widenUnsignedTypes;
private final boolean useBinaryString;
Expand Down Expand Up @@ -352,6 +354,8 @@ public ClickHouseConfig(Map<ClickHouseOption, Serializable> options, ClickHouseC
this.keyStoreType = getStrOption(ClickHouseClientOption.KEY_STORE_TYPE);
this.trustStore = getStrOption(ClickHouseClientOption.TRUST_STORE);
this.trustStorePassword = getStrOption(ClickHouseClientOption.KEY_STORE_PASSWORD);
this.gssEnabled = getBoolOption(ClickHouseClientOption.GSS_ENABLED);
this.kerberosServerName = getStrOption(ClickHouseClientOption.KERBEROS_SERVER_NAME);
this.transactionTimeout = getIntOption(ClickHouseClientOption.TRANSACTION_TIMEOUT);
this.widenUnsignedTypes = getBoolOption(ClickHouseClientOption.WIDEN_UNSIGNED_TYPES);
this.useBinaryString = getBoolOption(ClickHouseClientOption.USE_BINARY_STRING);
Expand All @@ -368,8 +372,12 @@ public ClickHouseConfig(Map<ClickHouseOption, Serializable> options, ClickHouseC
this.timeZoneForDate = this.useServerTimeZoneForDates ? this.useTimeZone : null;

if (credentials == null) {
this.credentials = ClickHouseCredentials.fromUserAndPassword(getStrOption(ClickHouseDefaults.USER),
getStrOption(ClickHouseDefaults.PASSWORD));
String user = getStrOption(ClickHouseDefaults.USER);
if (this.gssEnabled) {
this.credentials = ClickHouseCredentials.withGss(user);
} else {
this.credentials = ClickHouseCredentials.fromUserAndPassword(user, getStrOption(ClickHouseDefaults.PASSWORD));
}
} else {
this.credentials = credentials;
}
Expand Down Expand Up @@ -642,6 +650,14 @@ public String getTrustStorePassword() {
return trustStorePassword;
}

public boolean isGssEnabled() {
return gssEnabled;
}

public String getKerberosServerName() {
return kerberosServerName;
}

public int getTransactionTimeout() {
return transactionTimeout < 1 ? sessionTimeout : transactionTimeout;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class ClickHouseCredentials implements Serializable {
private final String userName;
private final String password;
// TODO sslCert
private final boolean gssEnabled;

/**
* Create credentials from access token.
Expand All @@ -25,7 +26,7 @@ public class ClickHouseCredentials implements Serializable {
* @return credentials object for authentication
*/
public static ClickHouseCredentials fromAccessToken(String accessToken) {
return new ClickHouseCredentials(accessToken);
return new ClickHouseCredentials(null, null, accessToken, false);
}

/**
Expand All @@ -36,43 +37,49 @@ public static ClickHouseCredentials fromAccessToken(String accessToken) {
* @return credentials object for authentication
*/
public static ClickHouseCredentials fromUserAndPassword(String userName, String password) {
return new ClickHouseCredentials(userName, password);
return ClickHouseCredentials.fromUserAndPassword(userName, password, false);
}

/**
* Construct credentials object using access token.
*
* @param accessToken access token
*/
protected ClickHouseCredentials(String accessToken) {
this.accessToken = ClickHouseChecker.nonNull(accessToken, "accessToken");
this.userName = null;
this.password = null;
public static ClickHouseCredentials fromUserAndPassword(String userName, String password, boolean useGss) {
ClickHouseChecker.nonBlank(userName, "userName");
return new ClickHouseCredentials(userName, password != null ? password : "", null, useGss);
}

/**
* Construct credentials using user name and password.
*
* Create credentials for GSS authentication.
*
* @param userName user name
* @param password password
* @return credentials object for authentication
*/
protected ClickHouseCredentials(String userName, String password) {
this.accessToken = null;
public static ClickHouseCredentials withGss(String userName) {
ClickHouseChecker.nonBlank(userName, "userName");
return new ClickHouseCredentials(userName, null, null, true);
}

this.userName = ClickHouseChecker.nonBlank(userName, "userName");
this.password = password != null ? password : "";
private ClickHouseCredentials(String userName, String password, String accessToken, boolean gssEnabled) {
this.userName = userName;
this.password = password;
this.accessToken = accessToken;
this.gssEnabled = gssEnabled;
}

public boolean useAccessToken() {
return accessToken != null;
}

public boolean isGssEnabled() {
return gssEnabled;
}

/**
* Get access token.
*
* @return access token
*/
public String getAccessToken() {
if (isGssEnabled()) {
throw new IllegalStateException("Authentication with access token disabled. Use GSS auth instead.");
}
if (!useAccessToken()) {
throw new IllegalStateException("No access token specified, please use user name and password instead.");
}
Expand Down Expand Up @@ -100,12 +107,15 @@ public String getPassword() {
if (useAccessToken()) {
throw new IllegalStateException("No user name and password specified, please use access token instead.");
}
if (isGssEnabled()) {
throw new IllegalStateException("Password authentication disabled. Use GSS auth instead.");
}
return this.password;
}

@Override
public int hashCode() {
return Objects.hash(accessToken, userName, password);
return Objects.hash(accessToken, userName, password, gssEnabled);
}

@Override
Expand All @@ -119,7 +129,7 @@ public boolean equals(Object obj) {
}

ClickHouseCredentials c = (ClickHouseCredentials) obj;
return Objects.equals(accessToken, c.accessToken) && Objects.equals(userName, c.userName)
&& Objects.equals(password, c.password);
return Objects.equals(accessToken, c.accessToken) && Objects.equals(userName, c.userName)
&& Objects.equals(password, c.password) && Objects.equals(gssEnabled, c.gssEnabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,13 @@ static ClickHouseCredentials extract(String rawUserInfo, Map<String, String> par
ClickHouseCredentials credentials = defaultCredentials;
String user = "";
String passwd = "";
boolean gssEnabled = false;
if (credentials != null && !credentials.useAccessToken()) {
user = credentials.getUserName();
passwd = credentials.getPassword();
gssEnabled = credentials.isGssEnabled();
if (!gssEnabled) {
passwd = credentials.getPassword();
}
}

if (!ClickHouseChecker.isNullOrEmpty(rawUserInfo)) {
Expand All @@ -535,11 +539,16 @@ static ClickHouseCredentials extract(String rawUserInfo, Map<String, String> par
if (str != null) {
passwd = str;
}
str = params.remove(ClickHouseDefaults.GSS_ENABLED.getKey());
if (str != null) {
gssEnabled = Boolean.parseBoolean(str);
}

if (!ClickHouseChecker.isNullOrEmpty(user)) {
credentials = ClickHouseCredentials.fromUserAndPassword(user, passwd);
credentials = ClickHouseCredentials.fromUserAndPassword(user, passwd, gssEnabled);
} else if (!ClickHouseChecker.isNullOrEmpty(passwd)) {
credentials = ClickHouseCredentials
.fromUserAndPassword((String) ClickHouseDefaults.USER.getEffectiveDefaultValue(), passwd);
.fromUserAndPassword((String) ClickHouseDefaults.USER.getEffectiveDefaultValue(), passwd, gssEnabled);
}
return credentials;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,14 @@ public enum ClickHouseClientOption implements ClickHouseOption {
* Trust Store password.
*/
KEY_STORE_PASSWORD("key_store_password", "", "Password needed to access the keystore file specified in the keystore config", true),
/**
* Enable GSS authorization.
*/
GSS_ENABLED("gss_enabled", false, "Enable Authorization via GSS. Supported only for http protocol"),
/**
* Kerberos server name. Used by GSS authorization.
*/
KERBEROS_SERVER_NAME("kerberos_server_name", "clickhouse", "Kerberos server name for GSS authorization"),
/**
* Transaction timeout in seconds.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@ public enum ClickHouseDefaults implements ClickHouseOption {
* {@link com.clickhouse.client.naming.SrvResolver}(e.g. resolve SRV record to
* extract both host and port from a given name).
*/
SRV_RESOLVE("srv_resolve", false, "Whether to resolve DNS SRV name.");
SRV_RESOLVE("srv_resolve", false, "Whether to resolve DNS SRV name."),
/**
* Enable GSS authentication.
*/
GSS_ENABLED("gss_enabled", false, "Enable GSS authentication. Supported only by HTTP protocol.");

private final String key;
private final Serializable defaultValue;
Expand Down
Loading