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

fix: [CO-440] properly de-register Account AuthToken in EndSession API #119

Merged
merged 1 commit into from
Nov 24, 2022
Merged
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
5 changes: 3 additions & 2 deletions store/src/java/com/zimbra/cs/account/ZimbraAuthToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.google.common.base.MoreObjects;
import com.zimbra.common.account.Key.AccountBy;
import com.zimbra.common.account.ZAttrProvisioning;
import com.zimbra.common.auth.ZAuthToken;
import com.zimbra.common.localconfig.LC;
import com.zimbra.common.service.ServiceException;
Expand Down Expand Up @@ -334,8 +335,8 @@ public void deRegister() throws AuthTokenException {
Account acct = Provisioning.getInstance().getAccountById(properties.getAccountId());
if (acct != null) {
acct.removeAuthTokens(
String.valueOf(properties.getTokenID()), properties.getServerVersion());
if (acct.getBooleanAttr(Provisioning.A_zimbraLogOutFromAllServers, false)) {
String.valueOf(properties.getTokenID()), String.valueOf(properties.getServerVersion()));
frisonisland marked this conversation as resolved.
Show resolved Hide resolved
if (acct.getBooleanAttr(ZAttrProvisioning.A_zimbraLogOutFromAllServers, false)) {
AuthTokenRegistry.addTokenToQueue(this);
}
}
Expand Down
165 changes: 90 additions & 75 deletions store/src/java/com/zimbra/cs/service/account/EndSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,6 @@

package com.zimbra.cs.service.account;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.zimbra.common.service.ServiceException;
import com.zimbra.common.soap.AccountConstants;
import com.zimbra.common.soap.Element;
Expand All @@ -28,82 +19,106 @@
import com.zimbra.soap.SoapServlet;
import com.zimbra.soap.ZimbraSoapContext;
import com.zimbra.soap.account.message.EndSessionRequest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* End the current session immediately cleaning up all resources used by the session
* including the notification buffer and logging the session out from IM if applicable
* End the current session immediately cleaning up all resources used by the session including the
* notification buffer and logging the session out from IM if applicable
*/
public class EndSession extends AccountDocumentHandler {

@Override
public Element handle(Element request, Map<String, Object> context)
throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
EndSessionRequest req = JaxbUtil.elementToJaxb(request);
String sessionId = req.getSessionId();
boolean clearCookies = req.isLogOff();
boolean clearAllSessions = req.isClearAllSoapSessions();
boolean excludeCurrrentSession = req.isExcludeCurrentSession();
Account account = getAuthenticatedAccount(zsc);
@Override
public Element handle(Element request, Map<String, Object> context) throws ServiceException {
ZimbraSoapContext zsc = getZimbraSoapContext(context);
EndSessionRequest req = JaxbUtil.elementToJaxb(request);
String sessionId = req.getSessionId();
final boolean clearCookies = req.isLogOff();
final boolean clearAllSessions = req.isClearAllSoapSessions();
final boolean excludeCurrentSession = req.isExcludeCurrentSession();
Account account = getAuthenticatedAccount(zsc);

if (clearAllSessions) {
String currentSessionId = null;
if (excludeCurrrentSession && zsc.hasSession()) {
Session currentSession = getSession(zsc);
currentSessionId = currentSession.getSessionId();
}
Collection<Session> sessionCollection = SessionCache.getSoapSessions(account.getId());
if (sessionCollection != null) {
List<Session> sessions = new ArrayList<Session>(sessionCollection);
Iterator<Session> itr = sessions.iterator();
while (itr.hasNext()) {
Session session = itr.next();
itr.remove();
clearSession(session, currentSessionId);
}
}
} else if (!StringUtil.isNullOrEmpty(sessionId)) {
Session s = SessionCache.lookup(sessionId, account.getId());
if (s == null) {
throw ServiceException.FAILURE("Failed to find session with given sessionId", null);
} else {
clearSession(s, null);
}
} else {
if (zsc.hasSession()) {
Session s = getSession(zsc);
endSession(s);
}
if (clearCookies || account.isForceClearCookies()) {
context.put(SoapServlet.INVALIDATE_COOKIES, true);
try {
AuthToken at = zsc.getAuthToken();
HttpServletRequest httpReq = (HttpServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
HttpServletResponse httpResp = (HttpServletResponse) context.get(SoapServlet.SERVLET_RESPONSE);
at.encode(httpReq, httpResp, true);
at.deRegister();
} catch (AuthTokenException e) {
throw ServiceException.FAILURE("Failed to de-register an auth token", e);
}
}
if (clearAllSessions) {
clearAllSessions(zsc, excludeCurrentSession, account);
} else if (!StringUtil.isNullOrEmpty(sessionId)) {
Session s = SessionCache.lookup(sessionId, account.getId());
if (s == null) {
throw ServiceException.FAILURE("Failed to find session with given sessionId", null);
} else {
clearSession(s, null);
}
} else {
if (zsc.hasSession()) {
Session s = getSession(zsc);
endSession(s);
}
if (clearCookies || account.isForceClearCookies()) {
context.put(SoapServlet.INVALIDATE_COOKIES, true);
try {
AuthToken at = zsc.getAuthToken();
HttpServletRequest httpReq =
(HttpServletRequest) context.get(SoapServlet.SERVLET_REQUEST);
HttpServletResponse httpResp =
(HttpServletResponse) context.get(SoapServlet.SERVLET_RESPONSE);
at.encode(httpReq, httpResp, true);
at.deRegister();
} catch (AuthTokenException e) {
throw ServiceException.FAILURE("Failed to de-register an auth token", e);
}
}
}

return zsc.createElement(AccountConstants.END_SESSION_RESPONSE);
}

Element response = zsc.createElement(AccountConstants.END_SESSION_RESPONSE);
return response;
/**
* @param zsc SoapContext of the request
* @param excludeCurrentSession exclude current session
* @param account Account whose session will be cleared
* @throws ServiceException
*/
private void clearAllSessions(
final ZimbraSoapContext zsc, boolean excludeCurrentSession, Account account)
throws ServiceException {
String currentSessionId = null;
if (excludeCurrentSession && zsc.hasSession()) {
Session currentSession = getSession(zsc);
currentSessionId = currentSession.getSessionId();
}
Collection<Session> sessionCollection = SessionCache.getSoapSessions(account.getId());
if (sessionCollection != null) {
List<Session> sessions = new ArrayList<>(sessionCollection);
Iterator<Session> itr = sessions.iterator();
while (itr.hasNext()) {
Session session = itr.next();
itr.remove();
clearSession(session, currentSessionId);
}
}
}

private void clearSession(Session session, String currentSessionId) throws ServiceException {
if(session instanceof SoapSession && !session.getSessionId().equalsIgnoreCase(currentSessionId)) {
AuthToken at = ((SoapSession) session).getAuthToken();
if (at != null) {
try {
at.deRegister();
} catch (AuthTokenException e) {
throw ServiceException.FAILURE("Failed to de-register an auth token", e);
}
}
endSession(session);
/**
* @param session Session to clear
* @param currentSessionId current session ID
* @throws ServiceException exception if an error occurs during session cleanup
*/
private void clearSession(Session session, String currentSessionId) throws ServiceException {
if (session instanceof SoapSession
&& !session.getSessionId().equalsIgnoreCase(currentSessionId)) {
AuthToken at = ((SoapSession) session).getAuthToken();
if (at != null) {
try {
at.deRegister();
} catch (AuthTokenException e) {
throw ServiceException.FAILURE("Failed to de-register an auth token", e);
}
}
endSession(session);
}

}
}