/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.security.authentication; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLEncoder; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.common.VdcUtil; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.sun.jersey.api.client.ClientResponse; import org.apache.commons.lang.StringUtils; /** * Client to make internal api logout calls to local authsvc */ public class InternalLogoutClient { private static final Logger log = LoggerFactory.getLogger(InternalLogoutClient.class); private static final int MAX_LOGOUT_RETRIES = 5; private static final URI LOGOUT_URI = URI.create("/logout"); @Autowired private AuthSvcEndPointLocator authSvcEndPointLocator; @Autowired protected TokenEncoder tokenEncoder; /** * Basic internal api call to authsvc to logout a user. * * @param username optional. If passed, that user will be logged out (if the token * present in the request corresponds to a user with SECURITY_ADMIN role). Else, * the user corresponding to the token from the request is what will get logged out. * @param req * @return */ public boolean logoutUser(String username, HttpServletRequest req) { return logoutUser(username, req, true, true); } /** * Alternate version of the internal api call to authsvc which allows toggling * the force flag on/off, and allows multiple retries or not. * * @param username * @param req * @param force * @param retry * @return */ public boolean logoutUser(String username, HttpServletRequest req, boolean force, boolean retry) { // get the auth token from the request, we need to pass // it along the logout request String rawToken = req.getHeader(RequestProcessingUtils.AUTH_TOKEN_HEADER); if (rawToken == null) { if (req.getCookies() != null) { for (Cookie cookie : req.getCookies()) { if (cookie.getName().equalsIgnoreCase(RequestProcessingUtils.AUTH_TOKEN_HEADER)) { rawToken = cookie.getValue(); log.debug("Got token from cookies for internal logout request"); break; } } } } TokenOnWire tw = tokenEncoder.decode(rawToken); if (tw == null) { log.error("Could not logout user. Token does not decode."); return false; } /** * - If no username parameter is supplied, this is a regular logout or logout force * for a given token. In this case do notify other VDCs if you are the originator of this token. * Other wise, do not notify (you are being notified). * - If the username parameter is supplied, this is a logout?username=<user>, notify is false because * either this is invoked from PasswordService and this is always for local users, or this invoked * from TokenService.logout which received this logout?username= request from another vdc, so no * need to propagate further (this would cause an infinite loop) */ boolean notify = false; if (StringUtils.isBlank(username)) { log.debug("no username"); notify = VdcUtil.getLocalShortVdcId().equals(URIUtil.parseVdcIdFromURI(tw.getTokenId())) ? true : false; } // perform logout of the local copy, if notifyVDC is true, this will also notify vdcs that have a copy // of the token. log.info("LogoutClient: {}", notify ? "will set the notify flag to true when sending logout request to authsvc" : "Just deleting local copy of token."); String endpoint = null; int attempts = 0; if (StringUtils.isNotBlank(username)) { try { username = URLEncoder.encode(username, "UTF-8"); } catch (UnsupportedEncodingException e) { throw APIException.badRequests.unableToEncodeString(username, e); } } int retries = retry ? MAX_LOGOUT_RETRIES : 1; while (attempts < retries) { log.debug("Logout attempt {}", ++attempts); AuthSvcClientIterator authSvcClientItr = new AuthSvcClientIterator(authSvcEndPointLocator); try { if (authSvcClientItr.hasNext()) { endpoint = authSvcClientItr.peek().toString(); log.debug("AuthenticationProvider endpoint: {}", endpoint); String fullRequest = LOGOUT_URI + String.format("?force=%s&proxytokens=false¬ifyvdcs=%s%s", force == true ? "true" : "false", notify == true ? "true" : "false", username == null ? "" : "&username=" + username); log.info(fullRequest); final ClientResponse response = authSvcClientItr.get(URI.create(fullRequest), rawToken); final int status = response.getStatus(); String errorRaw = response.getEntity(String.class); log.debug("Status: {}", status); log.debug("Response entity: {}", errorRaw); if (status == ClientResponse.Status.OK.getStatusCode()) { log.info("User logged out successfully. User will have to re-login."); return true; } else if (status == ClientResponse.Status.UNAUTHORIZED.getStatusCode() && !notify) { log.info("401 Status code from logout request. Token did not exist or was already deleted."); } else { log.warn("Unexpected response code {}.", status); } } } catch (Exception e) { log.info("Exception connecting to {}. ", endpoint, e); } } return false; } }