This is an automated email from the ASF dual-hosted git repository.
RaigorJiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git
The following commit(s) were added to refs/heads/master by this push:
new d75a844d8d2 Add connection attributes for ConnectionSession (#39028)
d75a844d8d2 is described below
commit d75a844d8d28882aeb85f6b8ade5e66da65b814a
Author: jiangML <[email protected]>
AuthorDate: Wed Jul 8 10:29:54 2026 +0800
Add connection attributes for ConnectionSession (#39028)
* Add connection attributes for ConnectionSession
* Add test for MySQLAuthenticationEngine
* Improve MySQLHandshakeResponse41Packet
---
.../mysql/constant/MySQLCapabilityFlag.java | 2 +-
.../protocol/mysql/constant/MySQLConstants.java | 3 ++
.../handshake/MySQLHandshakeResponse41Packet.java | 53 +++++++++++++++++++++-
.../mysql/constant/MySQLCapabilityFlagTest.java | 2 +-
.../MySQLHandshakeResponse41PacketTest.java | 41 +++++++++++++++++
.../result/AuthenticationResult.java | 4 ++
.../result/AuthenticationResultBuilder.java | 15 ++++--
.../result/AuthenticationResultBuilderTest.java | 6 ++-
.../proxy/backend/session/ConnectionSession.java | 4 ++
.../netty/FrontendChannelInboundHandler.java | 1 +
.../netty/FrontendChannelInboundHandlerTest.java | 5 +-
.../FirebirdAuthenticationEngine.java | 5 +-
.../FirebirdAuthenticationEngineTest.java | 6 +--
.../authentication/MySQLAuthenticationEngine.java | 13 ++++--
.../MySQLAuthenticationEngineTest.java | 22 ++++++---
.../OpenGaussAuthenticationEngine.java | 5 +-
.../PostgreSQLAuthenticationEngine.java | 5 +-
17 files changed, 163 insertions(+), 29 deletions(-)
diff --git
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlag.java
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlag.java
index 424db2c6b87..6507ba53038 100644
---
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlag.java
+++
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlag.java
@@ -97,7 +97,7 @@ public enum MySQLCapabilityFlag {
* @return handshake capability flags upper bit
*/
public static int calculateHandshakeCapabilityFlagsUpper() {
- return calculateCapabilityFlags(CLIENT_MULTI_STATEMENTS,
CLIENT_PLUGIN_AUTH, CLIENT_MULTI_RESULTS, CLIENT_PS_MULTI_RESULTS) >> 16;
+ return calculateCapabilityFlags(CLIENT_MULTI_STATEMENTS,
CLIENT_PLUGIN_AUTH, CLIENT_MULTI_RESULTS, CLIENT_PS_MULTI_RESULTS,
CLIENT_CONNECT_ATTRS) >> 16;
}
/**
diff --git
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLConstants.java
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLConstants.java
index 4d2a18582ff..eabf7c40731 100644
---
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLConstants.java
+++
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLConstants.java
@@ -21,6 +21,7 @@ import io.netty.util.AttributeKey;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -35,6 +36,8 @@ public final class MySQLConstants {
public static final AttributeKey<Integer>
OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY =
AttributeKey.valueOf("MYSQL_OPTION_MULTI_STATEMENTS");
+ public static final AttributeKey<Map<String, String>>
CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY =
AttributeKey.valueOf("MYSQL_CONNECTION_ATTRIBUTES");
+
/**
* Protocol version is always 0x0A.
*/
diff --git
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41Packet.java
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41Packet.java
index f9713d0f02a..38aa111442b 100644
---
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41Packet.java
+++
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41Packet.java
@@ -26,6 +26,9 @@ import
org.apache.shardingsphere.database.protocol.mysql.packet.MySQLPacket;
import
org.apache.shardingsphere.database.protocol.mysql.packet.command.admin.MySQLComSetOptionPacket;
import
org.apache.shardingsphere.database.protocol.mysql.payload.MySQLPacketPayload;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* Handshake response above MySQL 4.1 packet protocol.
*
@@ -52,6 +55,8 @@ public final class MySQLHandshakeResponse41Packet extends
MySQLPacket {
private int multiStatementsOption;
+ private Map<String, String> connectionAttributes;
+
public MySQLHandshakeResponse41Packet(final MySQLPacketPayload payload) {
capabilityFlags = payload.readInt4();
multiStatementsOption = readMultiStatementsOption(capabilityFlags);
@@ -62,6 +67,7 @@ public final class MySQLHandshakeResponse41Packet extends
MySQLPacket {
authResponse = readAuthResponse(payload);
database = readDatabase(payload);
authPluginName = readAuthPluginName(payload);
+ connectionAttributes = readConnectionAttributes(payload);
}
private int readMultiStatementsOption(final int capabilityFlags) {
@@ -85,7 +91,52 @@ public final class MySQLHandshakeResponse41Packet extends
MySQLPacket {
}
private String readAuthPluginName(final MySQLPacketPayload payload) {
- return 0 == (capabilityFlags &
MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH.getValue()) ? null :
payload.readStringNul();
+ if (0 == (capabilityFlags &
MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH.getValue())) {
+ return null;
+ }
+ String result = payload.readStringNul();
+ return result.isEmpty() && isKnownAuthPluginName(payload) ?
payload.readStringNul() : result;
+ }
+
+ private boolean isKnownAuthPluginName(final MySQLPacketPayload payload) {
+ for (MySQLAuthenticationMethod each :
MySQLAuthenticationMethod.values()) {
+ if (isAuthPluginName(payload, each.getMethodName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isAuthPluginName(final MySQLPacketPayload payload, final
String authPluginName) {
+ byte[] authPluginNameBytes =
authPluginName.getBytes(payload.getCharset());
+ if (payload.getByteBuf().readableBytes() <=
authPluginNameBytes.length) {
+ return false;
+ }
+ int readerIndex = payload.getByteBuf().readerIndex();
+ for (int i = 0; i < authPluginNameBytes.length; i++) {
+ if (authPluginNameBytes[i] !=
payload.getByteBuf().getByte(readerIndex + i)) {
+ return false;
+ }
+ }
+ return 0 == payload.getByteBuf().getByte(readerIndex +
authPluginNameBytes.length);
+ }
+
+ private Map<String, String> readConnectionAttributes(final
MySQLPacketPayload payload) {
+ if (0 == (capabilityFlags &
MySQLCapabilityFlag.CLIENT_CONNECT_ATTRS.getValue()) ||
!payload.getByteBuf().isReadable()) {
+ return new HashMap<>();
+ }
+ int attributeLength = (int) payload.readIntLenenc();
+ if (attributeLength > payload.getByteBuf().readableBytes()) {
+ return new HashMap<>();
+ }
+ Map<String, String> result = new HashMap<>();
+ int endIndex = payload.getByteBuf().readerIndex() + attributeLength;
+ while (payload.getByteBuf().readerIndex() < endIndex) {
+ String key = new String(payload.readStringLenencByBytes(),
payload.getCharset());
+ String value = new String(payload.readStringLenencByBytes(),
payload.getCharset());
+ result.put(key, value);
+ }
+ return result;
}
/**
diff --git
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlagTest.java
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlagTest.java
index b8fe2fc143c..af25b442f9c 100644
---
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlagTest.java
+++
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCapabilityFlagTest.java
@@ -36,6 +36,6 @@ class MySQLCapabilityFlagTest {
@Test
void assertCalculateHandshakeCapabilityFlagsUpper() {
-
assertThat(MySQLCapabilityFlag.calculateHandshakeCapabilityFlagsUpper(),
is(0x000f));
+
assertThat(MySQLCapabilityFlag.calculateHandshakeCapabilityFlagsUpper(),
is(0x001f));
}
}
diff --git
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41PacketTest.java
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41PacketTest.java
index 3e72b9ea303..abf0cb75b52 100644
---
a/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41PacketTest.java
+++
b/database/protocol/dialect/mysql/src/test/java/org/apache/shardingsphere/database/protocol/mysql/packet/handshake/MySQLHandshakeResponse41PacketTest.java
@@ -17,6 +17,7 @@
package org.apache.shardingsphere.database.protocol.mysql.packet.handshake;
+import io.netty.buffer.Unpooled;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLAuthenticationMethod;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCapabilityFlag;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
@@ -27,6 +28,9 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+
import static org.hamcrest.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNull;
@@ -125,6 +129,22 @@ class MySQLHandshakeResponse41PacketTest {
verify(payload).skipReserved(23);
}
+ @Test
+ void assertNewWithPayloadWithConnectionAttributes() {
+ MySQLPacketPayload payload = new MySQLPacketPayload(Unpooled.buffer(),
StandardCharsets.UTF_8);
+ payload.writeInt4(MySQLCapabilityFlag.CLIENT_CONNECT_ATTRS.getValue());
+ payload.writeInt4(1000);
+ payload.writeInt1(MySQLConstants.DEFAULT_CHARSET.getId());
+ payload.writeReserved(23);
+ payload.writeStringNul("root");
+ payload.writeStringNul("");
+ payload.writeIntLenenc("program_name".length() + "mysql".length() +
2L);
+ payload.writeStringLenenc("program_name");
+ payload.writeStringLenenc("mysql");
+ MySQLHandshakeResponse41Packet actual = new
MySQLHandshakeResponse41Packet(payload);
+ assertThat(actual.getConnectionAttributes(),
is(Collections.singletonMap("program_name", "mysql")));
+ }
+
@Test
void assertWriteWithDatabase() {
MySQLHandshakeResponse41Packet actual = new
MySQLHandshakeResponse41Packet(100, MySQLConstants.DEFAULT_CHARSET.getId(),
"root");
@@ -183,4 +203,25 @@ class MySQLHandshakeResponse41PacketTest {
verify(payload).writeInt1(1);
verify(payload).writeBytes(new byte[]{1});
}
+
+ @Test
+ void assertNewWithPayloadWithEmptyAuthResponseAndAuthPluginName() {
+ MySQLPacketPayload payload = new MySQLPacketPayload(Unpooled.buffer(),
StandardCharsets.UTF_8);
+
payload.writeInt4(MySQLCapabilityFlag.calculateCapabilityFlags(MySQLCapabilityFlag.CLIENT_SECURE_CONNECTION,
MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH,
+ MySQLCapabilityFlag.CLIENT_CONNECT_ATTRS,
MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA));
+ payload.writeInt4(1000);
+ payload.writeInt1(MySQLConstants.DEFAULT_CHARSET.getId());
+ payload.writeReserved(23);
+ payload.writeStringNul("root");
+ payload.writeInt1(0);
+ payload.writeInt1(0);
+
payload.writeStringNul(MySQLAuthenticationMethod.CACHING_SHA2_PASSWORD.getMethodName());
+ payload.writeIntLenenc("_client_name".length() + "MariaDB
Connector/J".length() + 2L);
+ payload.writeStringLenenc("_client_name");
+ payload.writeStringLenenc("MariaDB Connector/J");
+ MySQLHandshakeResponse41Packet actual = new
MySQLHandshakeResponse41Packet(payload);
+ assertThat(actual.getAuthResponse(), is(new byte[]{}));
+ assertThat(actual.getAuthPluginName(),
is(MySQLAuthenticationMethod.CACHING_SHA2_PASSWORD.getMethodName()));
+ assertThat(actual.getConnectionAttributes(),
is(Collections.singletonMap("_client_name", "MariaDB Connector/J")));
+ }
}
diff --git
a/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResult.java
b/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResult.java
index c860ef2ece0..ad43f2afd53 100644
---
a/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResult.java
+++
b/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResult.java
@@ -20,6 +20,8 @@ package org.apache.shardingsphere.authentication.result;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
+import java.util.Map;
+
/**
* Authentication result.
*/
@@ -34,4 +36,6 @@ public final class AuthenticationResult {
private final String database;
private final boolean finished;
+
+ private final Map<String, String> connectionAttributes;
}
diff --git
a/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilder.java
b/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilder.java
index 69d72cbff5a..204ec74f2dd 100644
---
a/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilder.java
+++
b/kernel/authority/core/src/main/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilder.java
@@ -20,6 +20,9 @@ package org.apache.shardingsphere.authentication.result;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
+import java.util.Collections;
+import java.util.Map;
+
/**
* Authentication result builder.
*/
@@ -32,10 +35,11 @@ public final class AuthenticationResultBuilder {
* @param username username
* @param hostname hostname
* @param database database
+ * @param attributes attributes
* @return finished authentication result
*/
- public static AuthenticationResult finished(final String username, final
String hostname, final String database) {
- return new AuthenticationResult(username, hostname, database, true);
+ public static AuthenticationResult finished(final String username, final
String hostname, final String database, final Map<String, String> attributes) {
+ return new AuthenticationResult(username, hostname, database, true,
attributes);
}
/**
@@ -44,7 +48,7 @@ public final class AuthenticationResultBuilder {
* @return continued authentication result
*/
public static AuthenticationResult continued() {
- return new AuthenticationResult(null, null, null, false);
+ return new AuthenticationResult(null, null, null, false,
Collections.emptyMap());
}
/**
@@ -53,9 +57,10 @@ public final class AuthenticationResultBuilder {
* @param username username
* @param hostname hostname
* @param database database
+ * @param attributes attributes
* @return continued authentication result
*/
- public static AuthenticationResult continued(final String username, final
String hostname, final String database) {
- return new AuthenticationResult(username, hostname, database, false);
+ public static AuthenticationResult continued(final String username, final
String hostname, final String database, final Map<String, String> attributes) {
+ return new AuthenticationResult(username, hostname, database, false,
attributes);
}
}
diff --git
a/kernel/authority/core/src/test/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilderTest.java
b/kernel/authority/core/src/test/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilderTest.java
index c5cca6d7206..d9805dffe44 100644
---
a/kernel/authority/core/src/test/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilderTest.java
+++
b/kernel/authority/core/src/test/java/org/apache/shardingsphere/authentication/result/AuthenticationResultBuilderTest.java
@@ -19,6 +19,8 @@ package org.apache.shardingsphere.authentication.result;
import org.junit.jupiter.api.Test;
+import java.util.Collections;
+
import static org.hamcrest.Matchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -37,7 +39,7 @@ class AuthenticationResultBuilderTest {
@Test
void assertContinued() {
- AuthenticationResult actual =
AuthenticationResultBuilder.continued("username", "127.0.0.1", "database");
+ AuthenticationResult actual =
AuthenticationResultBuilder.continued("username", "127.0.0.1", "database",
Collections.emptyMap());
assertThat(actual.getUsername(), is("username"));
assertThat(actual.getHostname(), is("127.0.0.1"));
assertThat(actual.getDatabase(), is("database"));
@@ -46,7 +48,7 @@ class AuthenticationResultBuilderTest {
@Test
void assertFinished() {
- AuthenticationResult actual =
AuthenticationResultBuilder.finished("username", "127.0.0.1", "database");
+ AuthenticationResult actual =
AuthenticationResultBuilder.finished("username", "127.0.0.1", "database",
Collections.emptyMap());
assertThat(actual.getUsername(), is("username"));
assertThat(actual.getHostname(), is("127.0.0.1"));
assertThat(actual.getDatabase(), is("database"));
diff --git
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
index cc273f83486..33b8dfe0ad0 100644
---
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
+++
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
@@ -31,6 +31,8 @@ import
org.apache.shardingsphere.proxy.backend.connector.jdbc.statement.JDBCBack
import
org.apache.shardingsphere.proxy.backend.session.transaction.TransactionStatus;
import
org.apache.shardingsphere.sql.parser.statement.core.enums.TransactionIsolationLevel;
+import java.util.Collections;
+import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
@@ -77,6 +79,8 @@ public final class ConnectionSession {
private volatile String processId;
+ private volatile Map<String, String> connectionAttributes =
Collections.emptyMap();
+
private QueryContext queryContext;
public ConnectionSession(final DatabaseType protocolType, final
AttributeMap attributeMap) {
diff --git
a/proxy/frontend/core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java
b/proxy/frontend/core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java
index 79e30f51cf5..3909913cc95 100644
---
a/proxy/frontend/core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java
+++
b/proxy/frontend/core/src/main/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandler.java
@@ -85,6 +85,7 @@ public final class FrontendChannelInboundHandler extends
ChannelInboundHandlerAd
connectionSession.setGrantee(new
Grantee(authResult.getUsername(), authResult.getHostname()));
connectionSession.setCurrentDatabaseName(authResult.getDatabase());
connectionSession.setProcessId(processEngine.connect(connectionSession.getUsedDatabaseName(),
connectionSession.getConnectionContext().getGrantee()));
+
connectionSession.setConnectionAttributes(authResult.getConnectionAttributes());
}
return authResult.isFinished();
// CHECKSTYLE:OFF
diff --git
a/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
b/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
index 87bca9ce096..d40f9e9dcf4 100644
---
a/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
+++
b/proxy/frontend/core/src/test/java/org/apache/shardingsphere/proxy/frontend/netty/FrontendChannelInboundHandlerTest.java
@@ -64,6 +64,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -129,11 +130,13 @@ class FrontendChannelInboundHandlerTest {
@Test
void assertChannelReadNotAuthenticated() throws Exception {
channel.register();
- AuthenticationResult authenticationResult =
AuthenticationResultBuilder.finished("username", "hostname", "database");
+ Map<String, String> connectionAttributes =
Collections.singletonMap("program_name", "mysql");
+ AuthenticationResult authenticationResult =
AuthenticationResultBuilder.finished("username", "hostname", "database",
connectionAttributes);
when(authenticationEngine.authenticate(any(ChannelHandlerContext.class),
any(PacketPayload.class))).thenReturn(authenticationResult);
channel.writeInbound(Unpooled.EMPTY_BUFFER);
assertThat(connectionSession.getConnectionContext().getGrantee(),
is(new Grantee("username", "hostname")));
assertThat(connectionSession.getUsedDatabaseName(), is("database"));
+ assertThat(connectionSession.getConnectionAttributes(),
is(connectionAttributes));
}
@Test
diff --git
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngine.java
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngine.java
index 0f34c239f88..c0b1250a960 100644
---
a/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngine.java
+++
b/proxy/frontend/dialect/firebird/src/main/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngine.java
@@ -55,6 +55,7 @@ import
org.apache.shardingsphere.proxy.frontend.firebird.command.query.statement
import
org.apache.shardingsphere.proxy.frontend.firebird.command.query.transaction.FirebirdTransactionIdGenerator;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Optional;
/**
@@ -104,7 +105,7 @@ public final class FirebirdAuthenticationEngine implements
AuthenticationEngine
context.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).set(FirebirdCharacterSets.findCharacterSet(attachPacket.getEncoding()));
login(currentAuthResult.getDatabase(),
currentAuthResult.getUsername(), attachPacket, rule);
context.writeAndFlush(new FirebirdGenericResponsePacket());
- return
AuthenticationResultBuilder.finished(currentAuthResult.getUsername(), "",
currentAuthResult.getDatabase());
+ return
AuthenticationResultBuilder.finished(currentAuthResult.getUsername(), "",
currentAuthResult.getDatabase(), currentAuthResult.getConnectionAttributes());
}
private void login(final String databaseName, final String username, final
FirebirdAttachPacket attachPacket, final AuthorityRule rule) {
@@ -137,7 +138,7 @@ public final class FirebirdAuthenticationEngine implements
AuthenticationEngine
acceptPacket.setAcceptDataPacket(authData.getSalt(),
authData.getPublicKeyHex(), plugin, 0, "");
}
context.writeAndFlush(acceptPacket);
- currentAuthResult = AuthenticationResultBuilder.continued(username,
connectPacket.getHost(), connectPacket.getDatabase());
+ currentAuthResult = AuthenticationResultBuilder.continued(username,
connectPacket.getHost(), connectPacket.getDatabase(), Collections.emptyMap());
return currentAuthResult;
}
diff --git
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngineTest.java
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngineTest.java
index 25356587f5b..bf79c1155fa 100644
---
a/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngineTest.java
+++
b/proxy/frontend/dialect/firebird/src/test/java/org/apache/shardingsphere/proxy/frontend/firebird/authentication/FirebirdAuthenticationEngineTest.java
@@ -227,7 +227,7 @@ class FirebirdAuthenticationEngineTest {
Attribute<Charset> charsetAttr = mock(Attribute.class);
when(context.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(charsetAttr);
Plugins.getMemberAccessor().set(FirebirdAuthenticationEngine.class.getDeclaredField("currentAuthResult"),
authenticationEngine,
- AuthenticationResultBuilder.continued(currentUsername, "",
currentDatabase));
+ AuthenticationResultBuilder.continued(currentUsername, "",
currentDatabase, Collections.emptyMap()));
FirebirdSRPAuthenticationData authData =
mock(FirebirdSRPAuthenticationData.class);
Plugins.getMemberAccessor().set(FirebirdAuthenticationEngine.class.getDeclaredField("authData"),
authenticationEngine, authData);
FirebirdPacketPayload payload =
mockFirebirdPayload(FirebirdCommandPacketType.ATTACH);
@@ -299,7 +299,7 @@ class FirebirdAuthenticationEngineTest {
Attribute<Charset> charsetAttr = mock(Attribute.class);
when(context.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(charsetAttr);
Plugins.getMemberAccessor().set(FirebirdAuthenticationEngine.class.getDeclaredField("currentAuthResult"),
authenticationEngine,
- AuthenticationResultBuilder.continued("absent", "", "db"));
+ AuthenticationResultBuilder.continued("absent", "", "db",
Collections.emptyMap()));
FirebirdPacketPayload payload =
mockFirebirdPayload(FirebirdCommandPacketType.ATTACH);
try (MockedConstruction<FirebirdAttachPacket> ignored =
mockConstruction(FirebirdAttachPacket.class, (attachPacket, construction) ->
when(attachPacket.getEncoding()).thenReturn("UTF8"))) {
assertThrows(AccessDeniedException.class, () ->
authenticationEngine.authenticate(context, payload));
@@ -317,7 +317,7 @@ class FirebirdAuthenticationEngineTest {
Attribute<Charset> charsetAttr = mock(Attribute.class);
when(context.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(charsetAttr);
Plugins.getMemberAccessor().set(FirebirdAuthenticationEngine.class.getDeclaredField("currentAuthResult"),
authenticationEngine,
- AuthenticationResultBuilder.continued("root", "", "db"));
+ AuthenticationResultBuilder.continued("root", "", "db",
Collections.emptyMap()));
FirebirdSRPAuthenticationData authData =
mock(FirebirdSRPAuthenticationData.class);
Plugins.getMemberAccessor().set(FirebirdAuthenticationEngine.class.getDeclaredField("authData"),
authenticationEngine, authData);
FirebirdAuthenticator authenticator =
mock(FirebirdAuthenticator.class);
diff --git
a/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
index 67f7c249e25..c8878003404 100644
---
a/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
+++
b/proxy/frontend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngine.java
@@ -59,6 +59,7 @@ import
org.apache.shardingsphere.proxy.frontend.ssl.ProxySSLContext;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
+import java.util.Map;
import java.util.Optional;
/**
@@ -104,7 +105,7 @@ public final class MySQLAuthenticationEngine implements
AuthenticationEngine {
ShardingSpherePreconditions.checkState(authorizeDatabase(rule,
grantee, currentAuthResult.getDatabase()),
() -> new
DatabaseAccessDeniedException(currentAuthResult.getUsername(),
grantee.getHostname(), currentAuthResult.getDatabase()));
writeOKPacket(context);
- return AuthenticationResultBuilder.finished(grantee.getUsername(),
grantee.getHostname(), currentAuthResult.getDatabase());
+ return AuthenticationResultBuilder.finished(grantee.getUsername(),
grantee.getHostname(), currentAuthResult.getDatabase(),
currentAuthResult.getConnectionAttributes());
}
private AuthenticationResult authenticatePhaseFastPath(final
ChannelHandlerContext context, final PacketPayload payload, final AuthorityRule
rule) {
@@ -120,6 +121,7 @@ public final class MySQLAuthenticationEngine implements
AuthenticationEngine {
authResponse = handshakeResponsePacket.getAuthResponse();
setMultiStatementsOption(context, handshakeResponsePacket);
setCharacterSet(context, handshakeResponsePacket);
+ setConnectionAttributes(context, handshakeResponsePacket);
String database = handshakeResponsePacket.getDatabase();
ShardingSpherePreconditions.checkState(Strings.isNullOrEmpty(database)
||
ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().containsDatabase(database),
() -> new UnknownDatabaseException(database));
@@ -127,12 +129,13 @@ public final class MySQLAuthenticationEngine implements
AuthenticationEngine {
String hostname = getHostAddress(context);
ShardingSphereUser user = rule.findUser(new Grantee(username,
hostname)).orElseGet(() -> new ShardingSphereUser(username, "", hostname));
Authenticator authenticator = new
AuthenticatorFactory<>(MySQLAuthenticatorType.class, rule).newInstance(user);
+ Map<String, String> connectionAttributes =
handshakeResponsePacket.getConnectionAttributes();
if (0 == authResponse.length ||
isClientPluginAuthenticate(handshakeResponsePacket) &&
!authenticator.getAuthenticationMethodName().equals(handshakeResponsePacket.getAuthPluginName()))
{
connectionPhase =
MySQLConnectionPhase.AUTHENTICATION_METHOD_MISMATCH;
context.writeAndFlush(new
MySQLAuthSwitchRequestPacket(authenticator.getAuthenticationMethodName(),
authPluginData));
- return AuthenticationResultBuilder.continued(username, hostname,
database);
+ return AuthenticationResultBuilder.continued(username, hostname,
database, connectionAttributes);
}
- return AuthenticationResultBuilder.finished(username, hostname,
database);
+ return AuthenticationResultBuilder.finished(username, hostname,
database, connectionAttributes);
}
private void setMultiStatementsOption(final ChannelHandlerContext context,
final MySQLHandshakeResponse41Packet handshakeResponsePacket) {
@@ -145,6 +148,10 @@ public final class MySQLAuthenticationEngine implements
AuthenticationEngine {
context.channel().attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).set(characterSet);
}
+ private void setConnectionAttributes(final ChannelHandlerContext context,
final MySQLHandshakeResponse41Packet handshakeResponsePacket) {
+
context.channel().attr(MySQLConstants.CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY).set(handshakeResponsePacket.getConnectionAttributes());
+ }
+
private boolean isClientPluginAuthenticate(final
MySQLHandshakeResponse41Packet packet) {
return 0 != (packet.getCapabilityFlags() &
MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH.getValue());
}
diff --git
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
index 01aca06a270..d6fa81aaf40 100644
---
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
+++
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
@@ -153,6 +153,7 @@ class MySQLAuthenticationEngineTest {
when(channel.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
when(channel.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
when(channel.attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
+
when(channel.attr(MySQLConstants.CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
when(channelHandlerContext.channel()).thenReturn(channel);
when(payload.readInt1()).thenReturn(1);
when(payload.readInt4()).thenReturn(MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH.getValue());
@@ -172,14 +173,20 @@ class MySQLAuthenticationEngineTest {
ContextManager contextManager = mockContextManager(rule);
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
ChannelHandlerContext context = mockChannelHandlerContext();
- MySQLPacketPayload payload = mock(MySQLPacketPayload.class);
- when(payload.readInt4()).thenReturn(0);
- when(payload.readInt1()).thenReturn(1);
- when(payload.readStringNul()).thenReturn("root");
- when(payload.readStringNulByBytes()).thenReturn(new byte[0]);
+ MySQLPacketPayload payload = new MySQLPacketPayload(Unpooled.buffer(),
StandardCharsets.UTF_8);
+ payload.writeInt4(MySQLCapabilityFlag.CLIENT_CONNECT_ATTRS.getValue());
+ payload.writeInt4(1000);
+ payload.writeInt1(MySQLConstants.DEFAULT_CHARSET.getId());
+ payload.writeReserved(23);
+ payload.writeStringNul("root");
+ payload.writeStringNul("");
+ payload.writeIntLenenc("program_name".length() + "mysql".length() +
2L);
+ payload.writeStringLenenc("program_name");
+ payload.writeStringLenenc("mysql");
AuthenticationResult actual =
authenticationEngine.authenticate(context, payload);
assertFalse(actual.isFinished());
assertThat(getConnectionPhase(),
is(MySQLConnectionPhase.AUTHENTICATION_METHOD_MISMATCH));
+ assertThat(actual.getConnectionAttributes(),
is(Collections.singletonMap("program_name", "mysql")));
}
@Test
@@ -213,7 +220,8 @@ class MySQLAuthenticationEngineTest {
@SneakyThrows(ReflectiveOperationException.class)
private void setAuthenticationResult() {
-
Plugins.getMemberAccessor().set(MySQLAuthenticationEngine.class.getDeclaredField("currentAuthResult"),
authenticationEngine, AuthenticationResultBuilder.continued("root", "",
"foo_db"));
+
Plugins.getMemberAccessor().set(MySQLAuthenticationEngine.class.getDeclaredField("currentAuthResult"),
authenticationEngine,
+ AuthenticationResultBuilder.continued("root", "", "foo_db",
Collections.emptyMap()));
}
private ShardingSpherePrivileges mockPrivileges() {
@@ -377,6 +385,7 @@ class MySQLAuthenticationEngineTest {
when(channel.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
when(channel.attr(MySQLConstants.SEQUENCE_ID_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
when(channel.attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
+
when(channel.attr(MySQLConstants.CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
doReturn(channel).when(result).channel();
return result;
}
@@ -389,6 +398,7 @@ class MySQLAuthenticationEngineTest {
when(result.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
when(result.attr(MySQLConstants.SEQUENCE_ID_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
when(result.attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
+
when(result.attr(MySQLConstants.CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
return result;
}
diff --git
a/proxy/frontend/dialect/opengauss/src/main/java/org/apache/shardingsphere/proxy/frontend/opengauss/authentication/OpenGaussAuthenticationEngine.java
b/proxy/frontend/dialect/opengauss/src/main/java/org/apache/shardingsphere/proxy/frontend/opengauss/authentication/OpenGaussAuthenticationEngine.java
index d91f2683343..2245d022d22 100644
---
a/proxy/frontend/dialect/opengauss/src/main/java/org/apache/shardingsphere/proxy/frontend/opengauss/authentication/OpenGaussAuthenticationEngine.java
+++
b/proxy/frontend/dialect/opengauss/src/main/java/org/apache/shardingsphere/proxy/frontend/opengauss/authentication/OpenGaussAuthenticationEngine.java
@@ -63,6 +63,7 @@ import
org.apache.shardingsphere.proxy.frontend.connection.ConnectionIdGenerator
import
org.apache.shardingsphere.proxy.frontend.opengauss.authentication.authenticator.OpenGaussAuthenticatorType;
import org.apache.shardingsphere.proxy.frontend.ssl.ProxySSLContext;
+import java.util.Collections;
import java.util.Optional;
/**
@@ -127,7 +128,7 @@ public final class OpenGaussAuthenticationEngine implements
AuthenticationEngine
context.write(new PostgreSQLParameterStatusPacket("server_encoding",
"UTF8"));
context.write(new PostgreSQLParameterStatusPacket("integer_datetimes",
"on"));
context.writeAndFlush(PostgreSQLReadyForQueryPacket.NOT_IN_TRANSACTION);
- return
AuthenticationResultBuilder.finished(currentAuthResult.getUsername(), "",
currentAuthResult.getDatabase());
+ return
AuthenticationResultBuilder.finished(currentAuthResult.getUsername(), "",
currentAuthResult.getDatabase(), currentAuthResult.getConnectionAttributes());
}
private void login(final AuthorityRule rule, final String digest) {
@@ -158,7 +159,7 @@ public final class OpenGaussAuthenticationEngine implements
AuthenticationEngine
String username = startupPacket.getUsername();
ShardingSpherePreconditions.checkNotEmpty(username,
EmptyUsernameException::new);
context.writeAndFlush(getIdentifierPacket(username, rule,
startupPacket.getVersion()));
- currentAuthResult = AuthenticationResultBuilder.continued(username,
"", startupPacket.getDatabase());
+ currentAuthResult = AuthenticationResultBuilder.continued(username,
"", startupPacket.getDatabase(), Collections.emptyMap());
return currentAuthResult;
}
diff --git
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
index 4cc200b501e..be7800c01e2 100644
---
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
+++
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
@@ -63,6 +63,7 @@ import
org.apache.shardingsphere.proxy.frontend.postgresql.authentication.authen
import org.apache.shardingsphere.proxy.frontend.ssl.ProxySSLContext;
import java.nio.charset.StandardCharsets;
+import java.util.Collections;
import java.util.Locale;
import java.util.Optional;
@@ -120,7 +121,7 @@ public final class PostgreSQLAuthenticationEngine
implements AuthenticationEngin
context.write(new PostgreSQLParameterStatusPacket("integer_datetimes",
"on"));
context.write(new
PostgreSQLParameterStatusPacket("standard_conforming_strings", "on"));
context.writeAndFlush(PostgreSQLReadyForQueryPacket.NOT_IN_TRANSACTION);
- return
AuthenticationResultBuilder.finished(currentAuthResult.getUsername(), "",
currentAuthResult.getDatabase());
+ return
AuthenticationResultBuilder.finished(currentAuthResult.getUsername(), "",
currentAuthResult.getDatabase(), currentAuthResult.getConnectionAttributes());
}
private void login(final String databaseName, final String username, final
byte[] md5Salt, final String digest, final AuthorityRule rule) {
@@ -142,7 +143,7 @@ public final class PostgreSQLAuthenticationEngine
implements AuthenticationEngin
ShardingSpherePreconditions.checkNotEmpty(username,
EmptyUsernameException::new);
startupMessageReceived = true;
context.writeAndFlush(getIdentifierPacket(username, rule));
- currentAuthResult = AuthenticationResultBuilder.continued(username,
"", startupPacket.getDatabase());
+ currentAuthResult = AuthenticationResultBuilder.continued(username,
"", startupPacket.getDatabase(), Collections.emptyMap());
return currentAuthResult;
}