Copilot commented on code in PR #6687:
URL: https://github.com/apache/hive/pull/6687#discussion_r3754287244
##########
service/src/java/org/apache/hive/service/cli/session/SessionManager.java:
##########
@@ -715,10 +757,59 @@ public void run() {
public HiveSession getSession(SessionHandle sessionHandle) throws
HiveSQLException {
HiveSession session = handleToSession.get(sessionHandle);
- if (session == null) {
+ if (session != null) {
+ if (fetchStrategy == FetchStrategy.ALWAYS && sessionStateStore != null) {
+ syncFromRemoteIfStale(session);
+ }
+ return session;
+ }
+ if (fetchStrategy == FetchStrategy.NEVER) {
throw new HiveSQLException("Invalid SessionHandle: " + sessionHandle);
}
- return session;
+ return recoverSession(sessionHandle);
+ }
+
+ private void syncFromRemoteIfStale(HiveSession session) {
+ try {
+ String handleId =
session.getSessionHandle().getHandleIdentifier().toString();
+ HiveSessionSnapshot remoteSnapshot =
sessionStateStore.getSnapshot(handleId);
+ if (remoteSnapshot == null) {
+ return;
+ }
+ if (remoteSnapshot.getLastAccessTime() > session.getLastAccessTime()) {
+ LOG.info("Remote snapshot is newer for session {}, re-hydrating",
session.getSessionHandle());
+ hydrateSession(session, remoteSnapshot);
+ }
+ } catch (Exception e) {
+ LOG.warn("Failed to sync session from remote store: {}",
session.getSessionHandle(), e);
+ }
+ }
+
+ private HiveSession recoverSession(SessionHandle sessionHandle) throws
HiveSQLException {
+ String handleId = sessionHandle.getHandleIdentifier().toString();
+ HiveSessionSnapshot snapshot = sessionStateStore.getSnapshot(handleId);
+ if (snapshot == null) {
+ throw new HiveSQLException("Invalid SessionHandle: " + sessionHandle);
+ }
Review Comment:
recoverSession() looks up the remote snapshot using
sessionHandle.getHandleIdentifier().toString(), which only includes the public
UUID (HandleIdentifier#toString()) and ignores the secret UUID. That means
anyone who learns a sessionId (public UUID is exposed via REST and logs) can
craft a SessionHandle with any secret and hijack / impersonate the recovered
session. The state store key should incorporate both public+secret (or store &
verify the secret in the snapshot) so the secret remains required to
recover/access a session.
##########
service/src/java/org/apache/hive/service/cli/session/SessionManager.java:
##########
@@ -577,6 +617,7 @@ public HiveSession createSession(SessionHandle
sessionHandle, TProtocolVersion p
throw new HiveSQLException(FAIL_CLOSE_ERROR_MESSAGE);
}
registerLlapTargetGaugeIfNeeded(session);
+ saveSessionSnapshot(session);
LOG.info("Session opened, " + session.getSessionHandle()
Review Comment:
createSession() unconditionally persists a snapshot immediately after
opening a session. When called from recoverSession() (or
CLIService#createSessionWithSessionHandle), this will overwrite the remote
snapshot with a mostly-empty snapshot before hydrateSession() restores state,
potentially breaking subsequent recovery attempts and/or racing other HS2
instances. It’s safer to only persist snapshots for genuinely new sessions
here, and persist after hydration during recovery.
##########
service/src/java/org/apache/hive/service/cli/session/SessionManager.java:
##########
@@ -154,10 +161,36 @@ public synchronized void init(HiveConf hiveConf) {
cleanupService = SyncCleanupService.INSTANCE;
}
cleanupService.start();
+ initSessionStateStore();
super.init(hiveConf);
}
- private void registerOpenSesssionMetrics(Metrics metrics) {
+ private void initSessionStateStore() {
+ String storeClassName =
hiveConf.getVar(ConfVars.HIVE_SERVER2_SESSION_STATE_STORE_CLASS);
+ String strategyStr =
hiveConf.getVar(ConfVars.HIVE_SERVER2_SESSION_STATE_STORE_FETCH_STRATEGY);
+ this.fetchStrategy = FetchStrategy.valueOf(strategyStr);
+ if (storeClassName == null || storeClassName.isEmpty()) {
+ LOG.info("Session state store not configured. Persistable sessions
disabled.");
+ this.sessionStateStore = null;
+ this.fetchStrategy = FetchStrategy.NEVER;
+ return;
+ }
+ try {
+ Class<?> storeClass = Class.forName(storeClassName);
+ this.sessionStateStore = (SessionStateStore)
storeClass.getDeclaredConstructor().newInstance();
+ this.sessionStateStore.init(hiveConf);
+ LOG.info("Initialized session state store: {}, fetch strategy: {}",
storeClassName, fetchStrategy);
+ } catch (ClassNotFoundException e) {
+ LOG.warn("Session state store class not found: {}. Persistable sessions
disabled.", storeClassName);
+ this.sessionStateStore = null;
+ this.fetchStrategy = FetchStrategy.NEVER;
+ } catch (Exception e) {
+ LOG.error("Failed to initialize session state store: {}",
storeClassName, e);
+ throw new RuntimeException("Failed to initialize session state store",
e);
+ }
+ }
Review Comment:
initSessionStateStore() calls FetchStrategy.valueOf(strategyStr) before
checking whether a store is configured. If the fetch-strategy config is invalid
(or set to a non-enum value), HS2 will fail to start even though persistable
sessions are effectively disabled by an empty store class. Consider parsing the
strategy only after confirming the store is enabled, and defaulting to NEVER on
invalid values.
##########
service/src/java/org/apache/hive/service/cli/session/PersistableSessionUtils.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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 org.apache.hive.service.cli.session;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.ql.exec.Utilities;
+import org.apache.hadoop.hive.ql.metadata.Table;
+import org.apache.hadoop.hive.ql.session.SessionState;
+import org.apache.hadoop.hive.metastore.api.FieldSchema;
+import org.apache.hive.service.cli.HiveSQLException;
+import org.apache.hive.service.cli.SessionHandle;
+import org.apache.hive.service.cli.session.store.HiveSessionSnapshot;
+import org.apache.hive.service.cli.session.store.SessionStateStore;
+import org.apache.hive.service.rpc.thrift.TProtocolVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for the Persistable Sessions feature.
+ * Extracted from SessionManager and HiveSessionImpl to keep
+ * the feature's logic isolated from existing core classes.
+ */
+public final class PersistableSessionUtils {
+
+ public enum FetchStrategy {
+ NEVER,
+ ALWAYS,
+ FETCH_WHEN_MISSING
+ }
+
+ private static final Logger LOG =
LoggerFactory.getLogger(PersistableSessionUtils.class);
+
+ private static final Pattern STATE_CHANGING_PATTERN = Pattern.compile(
+ "(?i)^\\s*(?:" +
+ "USE\\b|" +
+ "SET\\b|" +
+ "ADD\\s+(?:JAR|FILE)\\b|" +
+ "DELETE\\s+(?:JAR|FILE)\\b|" +
+ "(?:CREATE|DROP)\\s+TEMPORARY\\s+(?:TABLE|FUNCTION)\\b" +
+ ").*");
+
+ private PersistableSessionUtils() {
+ }
+
+ /**
+ * Determines whether a SQL statement changes session state that should
+ * be persisted (database, configs, JARs, temp tables, temp functions).
+ */
+ public static boolean isStateChangingCommand(String statement) {
+ if (statement == null) {
+ return false;
+ }
+ return STATE_CHANGING_PATTERN.matcher(statement).matches();
+ }
+
+ /**
+ * Captures the current session state into a snapshot DTO.
+ */
+ public static HiveSessionSnapshot captureSnapshot(SessionHandle
sessionHandle,
+ String username, String ipAddress, SessionState sessionState,
+ HiveConf sessionConf, TProtocolVersion protocol,
+ long creationTime, long lastAccessTime) {
+ List<String> jars = new ArrayList<>();
+ String addedJarsStr = Utilities.getResourceFiles(sessionConf,
SessionState.ResourceType.JAR);
+ if (StringUtils.isNotBlank(addedJarsStr)) {
+ Collections.addAll(jars, addedJarsStr.split(","));
+ }
+
+ Map<String, String> tempTableDefs = new HashMap<>();
+ if (sessionState != null && sessionState.getTempTables() != null) {
+ for (Map.Entry<String, Map<String, Table>> dbEntry :
+ sessionState.getTempTables().entrySet()) {
+ for (Map.Entry<String, Table> tableEntry :
dbEntry.getValue().entrySet()) {
+ String tableName = tableEntry.getKey();
+ Table table = tableEntry.getValue();
+ String ddl = generateTempTableDDL(tableName, table);
+ if (ddl != null) {
+ tempTableDefs.put(tableName, ddl);
+ }
Review Comment:
captureSnapshot() iterates SessionState#getTempTables() by database, but
stores DDLs keyed only by tableName. If multiple DBs have temp tables with the
same name, entries will clobber; and during hydration there is no way to
restore temp tables in non-current databases. The snapshot needs to preserve
the database context per temp table (e.g., dbName -> (tableName -> ddl) or a
composite key + USE during hydrate).
This issue also appears on line 185 of the same file.
##########
service/src/java/org/apache/hive/service/cli/session/SessionManager.java:
##########
@@ -715,10 +757,59 @@ public void run() {
public HiveSession getSession(SessionHandle sessionHandle) throws
HiveSQLException {
HiveSession session = handleToSession.get(sessionHandle);
- if (session == null) {
+ if (session != null) {
+ if (fetchStrategy == FetchStrategy.ALWAYS && sessionStateStore != null) {
+ syncFromRemoteIfStale(session);
+ }
+ return session;
+ }
+ if (fetchStrategy == FetchStrategy.NEVER) {
throw new HiveSQLException("Invalid SessionHandle: " + sessionHandle);
}
- return session;
+ return recoverSession(sessionHandle);
+ }
+
+ private void syncFromRemoteIfStale(HiveSession session) {
+ try {
+ String handleId =
session.getSessionHandle().getHandleIdentifier().toString();
+ HiveSessionSnapshot remoteSnapshot =
sessionStateStore.getSnapshot(handleId);
+ if (remoteSnapshot == null) {
+ return;
+ }
+ if (remoteSnapshot.getLastAccessTime() > session.getLastAccessTime()) {
+ LOG.info("Remote snapshot is newer for session {}, re-hydrating",
session.getSessionHandle());
+ hydrateSession(session, remoteSnapshot);
+ }
+ } catch (Exception e) {
+ LOG.warn("Failed to sync session from remote store: {}",
session.getSessionHandle(), e);
+ }
+ }
+
+ private HiveSession recoverSession(SessionHandle sessionHandle) throws
HiveSQLException {
+ String handleId = sessionHandle.getHandleIdentifier().toString();
+ HiveSessionSnapshot snapshot = sessionStateStore.getSnapshot(handleId);
+ if (snapshot == null) {
+ throw new HiveSQLException("Invalid SessionHandle: " + sessionHandle);
+ }
+ LOG.info("Recovering session from state store: {}", sessionHandle);
+ TProtocolVersion protocol =
TProtocolVersion.findByValue(snapshot.getProtocolVersion());
+ if (protocol == null) {
+ protocol = TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V1;
+ }
+ SessionHandle recoveredHandle = new SessionHandle(
+ sessionHandle.getHandleIdentifier(), protocol);
+ boolean withImpersonation =
hiveConf.getBoolVar(ConfVars.HIVE_SERVER2_ENABLE_DOAS)
+ && snapshot.getUsername() != null;
+ HiveSession recovered = createSession(recoveredHandle, protocol,
+ snapshot.getUsername(), null, snapshot.getIpAddress(),
+ null, withImpersonation, null);
+ hydrateSession(recovered, snapshot);
+ LOG.info("Successfully recovered session: {}", sessionHandle);
+ return recovered;
Review Comment:
After recoverSession() hydrates the recovered session, it never persists an
updated snapshot. Combined with the unconditional save during createSession(),
the store may end up with an incomplete snapshot (e.g., missing restored
configs/current DB/JARs). Persisting once after hydration ensures the store
reflects the recovered state and refreshes TTL.
##########
service/src/java/org/apache/hive/service/cli/session/HiveSessionImpl.java:
##########
@@ -523,24 +525,32 @@ public HiveConf getSessionConf() throws HiveSQLException {
@Override
public OperationHandle executeStatement(String statement, Map<String,
String> confOverlay) throws HiveSQLException {
- return executeStatementInternal(statement, confOverlay, false, 0);
+ OperationHandle handle = executeStatementInternal(statement, confOverlay,
false, 0);
+ notifyIfStateChanging(statement);
+ return handle;
}
@Override
public OperationHandle executeStatement(String statement, Map<String,
String> confOverlay,
long queryTimeout) throws HiveSQLException {
- return executeStatementInternal(statement, confOverlay, false,
queryTimeout);
+ OperationHandle handle = executeStatementInternal(statement, confOverlay,
false, queryTimeout);
+ notifyIfStateChanging(statement);
+ return handle;
}
@Override
public OperationHandle executeStatementAsync(String statement, Map<String,
String> confOverlay) throws HiveSQLException {
- return executeStatementInternal(statement, confOverlay, true, 0);
+ OperationHandle handle = executeStatementInternal(statement, confOverlay,
true, 0);
+ notifyIfStateChanging(statement);
+ return handle;
Review Comment:
notifyIfStateChanging(statement) is invoked immediately after
executeStatementInternal() returns, even for async operations. For SQLOperation
with runAsync=true, executeStatementInternal returns after submitting
background work, so the snapshot can be saved before the state-changing command
(e.g., CREATE/DROP TEMPORARY TABLE) actually completes, resulting in
stale/incorrect persisted state. Until there is an async-completion hook to
snapshot after success, it’s safer to avoid snapshotting from
executeStatementAsync methods.
##########
service/src/java/org/apache/hive/service/cli/session/PersistableSessionUtils.java:
##########
@@ -0,0 +1,251 @@
+/*
+ * 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 org.apache.hive.service.cli.session;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.ql.exec.Utilities;
+import org.apache.hadoop.hive.ql.metadata.Table;
+import org.apache.hadoop.hive.ql.session.SessionState;
+import org.apache.hadoop.hive.metastore.api.FieldSchema;
+import org.apache.hive.service.cli.HiveSQLException;
+import org.apache.hive.service.cli.SessionHandle;
+import org.apache.hive.service.cli.session.store.HiveSessionSnapshot;
+import org.apache.hive.service.cli.session.store.SessionStateStore;
+import org.apache.hive.service.rpc.thrift.TProtocolVersion;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility methods for the Persistable Sessions feature.
+ * Extracted from SessionManager and HiveSessionImpl to keep
+ * the feature's logic isolated from existing core classes.
+ */
+public final class PersistableSessionUtils {
+
+ public enum FetchStrategy {
+ NEVER,
+ ALWAYS,
+ FETCH_WHEN_MISSING
+ }
+
+ private static final Logger LOG =
LoggerFactory.getLogger(PersistableSessionUtils.class);
+
+ private static final Pattern STATE_CHANGING_PATTERN = Pattern.compile(
+ "(?i)^\\s*(?:" +
+ "USE\\b|" +
+ "SET\\b|" +
+ "ADD\\s+(?:JAR|FILE)\\b|" +
+ "DELETE\\s+(?:JAR|FILE)\\b|" +
+ "(?:CREATE|DROP)\\s+TEMPORARY\\s+(?:TABLE|FUNCTION)\\b" +
+ ").*");
+
+ private PersistableSessionUtils() {
+ }
+
+ /**
+ * Determines whether a SQL statement changes session state that should
+ * be persisted (database, configs, JARs, temp tables, temp functions).
+ */
+ public static boolean isStateChangingCommand(String statement) {
+ if (statement == null) {
+ return false;
+ }
+ return STATE_CHANGING_PATTERN.matcher(statement).matches();
+ }
+
+ /**
+ * Captures the current session state into a snapshot DTO.
+ */
+ public static HiveSessionSnapshot captureSnapshot(SessionHandle
sessionHandle,
+ String username, String ipAddress, SessionState sessionState,
+ HiveConf sessionConf, TProtocolVersion protocol,
+ long creationTime, long lastAccessTime) {
+ List<String> jars = new ArrayList<>();
+ String addedJarsStr = Utilities.getResourceFiles(sessionConf,
SessionState.ResourceType.JAR);
+ if (StringUtils.isNotBlank(addedJarsStr)) {
+ Collections.addAll(jars, addedJarsStr.split(","));
+ }
+
+ Map<String, String> tempTableDefs = new HashMap<>();
+ if (sessionState != null && sessionState.getTempTables() != null) {
+ for (Map.Entry<String, Map<String, Table>> dbEntry :
+ sessionState.getTempTables().entrySet()) {
+ for (Map.Entry<String, Table> tableEntry :
dbEntry.getValue().entrySet()) {
+ String tableName = tableEntry.getKey();
+ Table table = tableEntry.getValue();
+ String ddl = generateTempTableDDL(tableName, table);
+ if (ddl != null) {
+ tempTableDefs.put(tableName, ddl);
+ }
+ }
+ }
+ }
+
+ return HiveSessionSnapshot.builder()
+ .sessionHandleId(sessionHandle.getHandleIdentifier().toString())
+ .username(username)
+ .ipAddress(ipAddress)
+ .currentDatabase(sessionState != null ?
sessionState.getCurrentDatabase() : null)
+ .overriddenConfigurations(sessionState != null
+ ? new HashMap<>(sessionState.getOverriddenConfigurations()) : null)
+ .addedJars(jars)
+ .tempTableDefinitions(tempTableDefs)
+ .protocolVersion(protocol.getValue())
+ .creationTime(creationTime)
+ .lastAccessTime(lastAccessTime)
+ .build();
+ }
+
+ /**
+ * Generates the CREATE TEMPORARY TABLE DDL for a temp table,
+ * including LOCATION so data can be recovered on shared storage.
+ */
+ public static String generateTempTableDDL(String tableName, Table table) {
+ try {
+ StringBuilder sb = new StringBuilder("CREATE TEMPORARY TABLE ");
+ sb.append(tableName).append(" (");
+ List<FieldSchema> cols = table.getCols();
+ for (int i = 0; i < cols.size(); i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(cols.get(i).getName()).append("
").append(cols.get(i).getType());
+ }
+ sb.append(")");
+ if (table.getSerializationLib() != null) {
+ sb.append(" ROW FORMAT SERDE
'").append(table.getSerializationLib()).append("'");
+ }
+ if (table.getStorageHandler() != null) {
+ sb.append(" STORED BY
'").append(table.getStorageHandler().getClass().getName()).append("'");
+ } else if (table.getInputFormatClass() != null) {
+ sb.append(" STORED AS INPUTFORMAT
'").append(table.getInputFormatClass().getName()).append("'");
+ if (table.getOutputFormatClass() != null) {
+ sb.append(" OUTPUTFORMAT
'").append(table.getOutputFormatClass().getName()).append("'");
+ }
+ }
+ if (table.getDataLocation() != null) {
+ sb.append(" LOCATION '").append(table.getDataLocation()).append("'");
+ }
+ return sb.toString();
+ } catch (Exception e) {
+ LOG.warn("Failed to generate DDL for temp table: {}", tableName, e);
+ return null;
+ }
+ }
+
+ /**
+ * Hydrates a recovered session from a snapshot: restores database, configs,
+ * JARs, and temp tables.
+ */
+ public static void hydrateSession(HiveSession session, HiveSessionSnapshot
snapshot)
+ throws HiveSQLException {
+ try {
+ SessionState sessionState = session.getSessionState();
+ if (snapshot.getCurrentDatabase() != null) {
+ sessionState.setCurrentDatabase(snapshot.getCurrentDatabase());
+ }
+ if (snapshot.getOverriddenConfigurations() != null) {
+ for (Map.Entry<String, String> entry :
snapshot.getOverriddenConfigurations().entrySet()) {
+ session.getHiveConf().set(entry.getKey(), entry.getValue());
+ sessionState.getOverriddenConfigurations().put(entry.getKey(),
entry.getValue());
+ }
+ }
+ if (snapshot.getAddedJars() != null) {
+ for (String jar : snapshot.getAddedJars()) {
+ sessionState.add_resource(SessionState.ResourceType.JAR, jar);
+ }
+ }
+ if (snapshot.getTempTableDefinitions() != null) {
+ for (Map.Entry<String, String> entry :
snapshot.getTempTableDefinitions().entrySet()) {
+ try {
+ session.executeStatement(entry.getValue(), null);
+ } catch (Exception e) {
+ LOG.warn("Failed to restore temporary table {}: {}",
entry.getKey(), e.getMessage());
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOG.error("Failed to hydrate session: {}", session.getSessionHandle(),
e);
+ throw new HiveSQLException("Failed to hydrate recovered session", e);
+ }
+ }
+
+ /**
+ * Unwraps a HiveSession proxy to get the underlying HiveSessionImpl.
+ * Returns null if the session cannot be unwrapped.
+ */
+ public static HiveSessionImpl unwrapSession(HiveSession session) {
+ if (session instanceof HiveSessionImpl) {
+ return (HiveSessionImpl) session;
+ }
+ if (Proxy.isProxyClass(session.getClass())) {
+ InvocationHandler handler = Proxy.getInvocationHandler(session);
+ if (handler instanceof HiveSessionProxy) {
+ HiveSession base = ((HiveSessionProxy) handler).getBaseSession();
+ if (base instanceof HiveSessionImpl) {
+ return (HiveSessionImpl) base;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Saves the session snapshot to the state store.
+ */
+ public static void saveSnapshot(SessionStateStore store, HiveSession
session) {
+ if (store == null) {
+ return;
+ }
+ try {
+ HiveSessionImpl impl = unwrapSession(session);
+ if (impl == null) {
+ return;
+ }
+ HiveSessionSnapshot snapshot = impl.captureSnapshot();
+
store.saveSnapshot(session.getSessionHandle().getHandleIdentifier().toString(),
snapshot);
+ } catch (Exception e) {
Review Comment:
saveSnapshot() uses
session.getSessionHandle().getHandleIdentifier().toString() as the store key,
which only includes the public UUID and drops the secret UUID. This weakens
session-handle secrecy and enables session hijacking via recovery if a
sessionId becomes known. The store key should incorporate both public+secret
(or store/verify the secret separately) so that recovery requires the full
handle.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]