Cb 3738 exception when logout invalid update token (#1853)

* CB-3738 log all gql errors

* CB-3310 do not update token for child sessions

* CB-3738 skip headless session on refresh

---------

Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com>
This commit is contained in:
Alexander Skoblikov
2023-07-28 13:20:47 +03:00
committed by GitHub
co-authored by mr-anton-t
parent d2f1f29e80
commit d93d7748cf
5 changed files with 64 additions and 30 deletions
@@ -80,16 +80,21 @@ public class WebUserContext implements SMCredentialsProvider {
* refresh context state based on new token from security manager
*
* @param smAuthInfo - auth info from security manager
* @return - true if context changed
* @throws DBException - if user already authorized and new token come from another user
*/
public synchronized boolean refresh(SMAuthInfo smAuthInfo) throws DBException {
if (smAuthInfo.getAuthStatus() != SMAuthStatus.SUCCESS || smAuthInfo.getSmAccessToken() == null) {
throw new DBCException("Authorization did not complete successfully");
}
if (smAuthInfo.getAuthPermissions() == null) {
if (smAuthInfo.getAuthPermissions() == null && !isAuthorizedInSecurityManager()) {
throw new DBCException("Required information about session permissions is missing");
}
return refresh(smAuthInfo.getSmAccessToken(), smAuthInfo.getSmRefreshToken(), smAuthInfo.getAuthPermissions());
boolean sessionChanged = !CommonUtils.equalObjects(smSessionId, smAuthInfo.getAuthPermissions().getSessionId());
if (smAuthInfo.getAuthStatus() != SMAuthStatus.SUCCESS || (sessionChanged && smAuthInfo.getSmAccessToken() == null)) {
throw new DBCException("Authorization did not complete successfully");
}
if (sessionChanged) {
return refresh(smAuthInfo.getSmAccessToken(), smAuthInfo.getSmRefreshToken(), smAuthInfo.getAuthPermissions());
}
return false;
}
public synchronized boolean refresh(
@@ -108,7 +113,7 @@ public class WebUserContext implements SMCredentialsProvider {
smAuthPermissions.getSessionId(),
smAuthPermissions.getPermissions()
);
this.refreshToken = smRefreshToken;
setRefreshToken(smRefreshToken);
setUserPermissions(smAuthPermissions.getPermissions());
this.securityController = application.createSecurityController(this);
this.adminSecurityController = application.getAdminSecurityController(this);
@@ -131,7 +136,7 @@ public class WebUserContext implements SMCredentialsProvider {
return;
}
var newTokens = securityController.refreshSession(refreshToken);
this.refreshToken = newTokens.getSmRefreshToken();
setRefreshToken(newTokens.getSmRefreshToken());
this.smCredentials = new SMCredentials(
newTokens.getSmAccessToken(),
smCredentials.getUserId(),
@@ -204,6 +209,7 @@ public class WebUserContext implements SMCredentialsProvider {
*/
public synchronized void refreshPermissions() throws DBException {
if (isAuthorizedInSecurityManager()) {
log.debug("refresh permissions " + getUserId() + " " + getSmSessionId());
setUserPermissions(securityController.getTokenPermissions().getPermissions());
} else {
setUserPermissions(getDefaultPermissions());
@@ -238,4 +244,8 @@ public class WebUserContext implements SMCredentialsProvider {
public Set<String> getAccessibleProjectIds() {
return accessibleProjectIds;
}
private void setRefreshToken(@Nullable String refreshToken) {
this.refreshToken = refreshToken;
}
}
@@ -23,7 +23,7 @@ import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
public class SessionStateJob extends PeriodicSystemJob {
private static final Log log = Log.getLog(SessionStateJob.class);
private static final int PERIOD_MS = 60_000; // once per 60 seconds
private static final int PERIOD_MS = 30_000; // once per 60 seconds
public SessionStateJob(@NotNull CBPlatform platform) {
super("Session state sender", platform, PERIOD_MS);
@@ -174,6 +174,7 @@ public abstract class WebServiceBindingBase<API_TYPE extends DBWService> impleme
throw e.getTargetException();
}
} catch (Throwable ex) {
log.error("Unexpected error during gql request", ex);
if (SMUtils.isTokenExpiredExceptionWasHandled(ex)) {
WebSession webSession = findWebSession(env);
if (webSession != null) {
@@ -326,11 +326,18 @@ public class WebSessionManager {
/**
* Send session state with remaining alive time to all cached session
*/
public void sendSessionsStates() throws DBException {
public void sendSessionsStates() {
synchronized (sessionMap) {
for (var session : sessionMap.values()) {
session.getUserContext().refreshPermissions();
session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid()));
if (session instanceof WebHeadlessSession) {
continue;
}
try {
session.getUserContext().refreshPermissions();
session.addSessionEvent(new WSSessionStateEvent(session.getRemainingTime(), session.isValid()));
} catch (Exception e) {
log.error("Failed to refresh session state: " + session.getSessionId(), e);
}
}
}
}
@@ -1183,7 +1183,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
var smTokens = generateNewSessionToken(smSessionId, null, null, dbCon);
var permissions = getAnonymousUserPermissions();
txn.commit();
return SMAuthInfo.success(
return SMAuthInfo.successMainSession(
UUID.randomUUID().toString(),
smTokens.getSmAccessToken(),
smTokens.getSmRefreshToken(),
@@ -1215,6 +1215,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
var authProgressMonitor = new LoggingProgressMonitor(log);
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
boolean isMainSession = previousSmSessionId == null;
Map<String, Object> securedUserIdentifyingCredentials = userCredentials;
WebAuthProviderDescriptor authProviderDescriptor = getAuthProvider(authProviderId);
var authProviderInstance = authProviderDescriptor.getInstance();
@@ -1236,7 +1237,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
appSessionId,
previousSmSessionId,
sessionType,
sessionParameters
sessionParameters,
isMainSession
);
if (SMAuthProviderFederated.class.isAssignableFrom(authProviderInstance.getClass())) {
@@ -1252,7 +1254,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
return finishAuthentication(
SMAuthInfo.inProgress(
authAttemptId,
null, Map.of(new SMAuthConfigurationReference(authProviderId, null), securedUserIdentifyingCredentials)
null,
Map.of(new SMAuthConfigurationReference(authProviderId, null), securedUserIdentifyingCredentials)
),
true,
false
@@ -1286,7 +1289,8 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
String appSessionId,
String prevSessionId,
SMSessionType sessionType,
Map<String, Object> sessionParameters
Map<String, Object> sessionParameters,
boolean isMainSession
) throws DBException {
String authAttemptId = UUID.randomUUID().toString();
try (Connection dbCon = database.openConnection()) {
@@ -1489,7 +1493,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
SMTokens smTokens = findTokenBySmSession(smSessionId);
SMAuthPermissions authPermissions = getTokenPermissions(smTokens.getSmAccessToken());
String authRole = readTokenAuthRole(smTokens.getSmAccessToken());
var successAuthStatus = SMAuthInfo.success(
var successAuthStatus = SMAuthInfo.successMainSession(
authId,
smTokens.getSmAccessToken(),
smTokens.getSmRefreshToken(),
@@ -1517,7 +1521,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
for (SMAuthInfo authData : allLatestAuthData) {
mergedData.putAll(authData.getAuthData());
}
return SMAuthInfo.success(
return SMAuthInfo.successMainSession(
"restore_session_attempt_" + UUID.randomUUID(),
latestActiveSmTokens.getAccessToken(),
latestActiveSmTokens.getRefreshToken(),
@@ -1597,6 +1601,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
return currentUserCreds;
}
@NotNull
private SMTokens findTokenBySmSession(String smSessionId) throws DBException {
try (Connection dbCon = database.openConnection();
PreparedStatement dbStat = dbCon.prepareStatement(
@@ -1697,12 +1702,13 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
SMTokens smTokens = null;
SMAuthPermissions permissions = null;
String activeUserId = null;
if (!isMainAuthSession) {
var accessToken = findTokenBySmSession(authAttemptSessionInfo.getSmSessionId()).getSmAccessToken();
//this is an additional authorization and we should to return the original permissions and userId
smTokens = findTokenBySmSession(authAttemptSessionInfo.getSmSessionId());
permissions = getTokenPermissions(smTokens.getSmAccessToken());
permissions = getTokenPermissions(accessToken);
activeUserId = permissions.getUserId();
}
String activeUserId = permissions == null ? null : permissions.getUserId();
Map<SMAuthConfigurationReference, Object> storedUserData = new LinkedHashMap<>();
SMTeam[] allTeams = null;
@@ -1768,7 +1774,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
String tokenAuthRole = updateUserAuthRoleIfNeeded(activeUserId, detectedAuthRole);
if (smTokens == null && permissions == null) {
if (isMainAuthSession) {
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
String smSessionId;
@@ -1798,15 +1804,24 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
}
var authStatus = forceExpireAuthAfterSuccess ? SMAuthStatus.EXPIRED : SMAuthStatus.SUCCESS;
updateAuthStatus(authId, authStatus, storedUserData, null, permissions.getSessionId());
return SMAuthInfo.success(
authId,
smTokens.getSmAccessToken(),
//refresh token must be sent only once
isMainAuthSession ? smTokens.getSmRefreshToken() : null,
permissions,
authInfo.getAuthData(),
tokenAuthRole
);
if (isMainAuthSession) {
return SMAuthInfo.successMainSession(
authId,
smTokens.getSmAccessToken(),
//refresh token must be sent only from main session
smTokens.getSmRefreshToken(),
permissions,
authInfo.getAuthData(),
tokenAuthRole
);
} else {
return SMAuthInfo.successChildSession(
authId,
permissions,
authInfo.getAuthData()
);
}
}
private void autoUpdateUserTeams(
@@ -2057,6 +2072,7 @@ public class CBEmbeddedSecurityController implements SMAdminController, SMAuthen
return getTokenPermissions(activeUserCredentials.getSmAccessToken());
}
@NotNull
private SMAuthPermissions getTokenPermissions(@NotNull String token) throws DBException {
String userId;
String sessionId;