/* * Copyright 2015 Evgeny Dolganov (evgenij.dolganov@gmail.com). * * Licensed 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 och.chat.service; import static och.api.model.BaseBean.*; import static och.api.model.PropKey.*; import static och.api.model.user.SecurityContext.*; import static och.util.Util.*; import static och.util.servlet.WebUtil.*; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import och.api.annotation.Secured; import och.api.exception.DebugException; import och.api.exception.InvalidInputException; import och.api.exception.client.NoClientSessionException; import och.api.exception.user.AccessDeniedException; import och.api.exception.web.MaxSessionsCountByIpException; import och.api.model.chat.account.PrivilegeType; import och.api.model.client.ClientInfo; import och.api.model.client.ClientSession; import och.api.model.user.User; import och.api.remote.chats.InitUserTokenReq; import och.api.remote.chats.RemoveUserSessionReq; import och.api.remote.chats.UpdateUserSessionsReq; import och.chat.service.event.client.ClientSessionDesroyedEvent; import och.chat.service.event.user.UserSessionCreatedEvent; import och.chat.service.event.user.UserSessionDestroyedEvent; import och.chat.service.event.user.UserSessionUpdatedEvent; import och.chat.service.model.UserSession; import och.comp.cache.impl.CacheImpl; import och.comp.web.BaseServlet.WebSecurityProvider; public class SecurityService extends BaseChatService implements HttpSessionListener, WebSecurityProvider { public static final String PRIVILEGES_PARAM = "privilegesByAccount"; public static final String CLIENT_INFO = "clientInfo"; public static final String USER_INFO = "userInfo"; CacheImpl cache; public SecurityService(ChatsAppContext c) { super(c); cache = c.cache; } public void init(ServletContext servletContext){ servletContext.addListener(this); } @Override public void sessionCreated(HttpSessionEvent se) { } @Override public void sessionDestroyed(HttpSessionEvent se) { HttpSession session = se.getSession(); ClientSession clientSession = (ClientSession) session.getAttribute(CLIENT_INFO); if(clientSession != null){ List<String> accIds = clientSession.getAccIds(); c.events.tryFireEvent(new ClientSessionDesroyedEvent(clientSession)); for(String accId : accIds){ log.info("["+accId+"] "+"CLIENT session closed: ip="+clientSession.info.ip +", userAgent="+clientSession.info.userAgent +", sessionId="+session.getId()); } } UserSession userSession = (UserSession) session.getAttribute(USER_INFO); if(userSession != null){ Map<String, Set<PrivilegeType>> privsByAcc = userSession.privsByAcc; c.events.tryFireEvent(new UserSessionDestroyedEvent(userSession)); for (Entry<String, Set<PrivilegeType>> entry : privsByAcc.entrySet()) { String accId = entry.getKey(); if( ! c.root.chats.isAccExists(accId)) continue; log.info("["+accId+"] "+"OPERATOR session closed: privs="+entry.getValue() +", ip="+userSession.info.ip +", userAgent="+userSession.info.userAgent +", sessionId="+session.getId()); } } } public boolean initClientSessionForAcc(HttpServletRequest req, String accId) { String clientIp = getClientIp(req); String userAgent = getUserAgent(req); boolean isNewSession = false; HttpSession session = req.getSession(false); if(session == null){ int maxSessionsCount = getMaxSessionsCountForChatClient(clientIp); int curSessionsByIp = c.sessionsCounter.getSessionsCount(clientIp); if(curSessionsByIp >= maxSessionsCount) throw new MaxSessionsCountByIpException(clientIp); session = req.getSession(true); if(session.isNew()){ session.setMaxInactiveInterval(props.getIntVal(chats_sessionLivetime)); isNewSession = true; } } ClientSession clientSession = (ClientSession) session.getAttribute(CLIENT_INFO); if(clientSession == null){ clientSession = new ClientSession(session.getId(), clientIp, userAgent); session.setAttribute(CLIENT_INFO, clientSession); clientSession.addAccId(accId); log.info("["+accId+"] "+"CLIENT session created: " +"ip="+clientIp +", userAgent="+userAgent +", sessionId="+session.getId()); } else { boolean added = clientSession.addAccId(accId); if(added) { log.info("["+accId+"] "+"CLIENT session taken: " +"ip="+clientIp +", userAgent="+userAgent +", sessionId="+session.getId()); } } return isNewSession; } public ClientInfo getClientInfo(HttpServletRequest req){ String ip = getClientIp(req); String userAgent = getUserAgent(req); return new ClientInfo(ip, userAgent); } /** * @return session or null */ public ClientSession getClientSession(HttpServletRequest req){ HttpSession session = req.getSession(false); if(session == null) return null; return (ClientSession) session.getAttribute(CLIENT_INFO); } public ClientSession findClientSession(HttpServletRequest req) throws NoClientSessionException { ClientSession clientSession = getClientSession(req); if(clientSession == null) throw new NoClientSessionException(); return clientSession; } public int getMaxSessionsCountForChatClient(String clientIp) { int out = props.getVal(chats_maxSessionsByIP+"_"+clientIp, -1); if(out == -1){ out = props.getIntVal(chats_maxSessionsByIP); } return out; } @Override public User getUserFromSession(HttpServletRequest req) { UserSession userSession = getUserSession(req); if(userSession == null) return null; User user = new User(userSession.userId); user.putParam(PRIVILEGES_PARAM, userSession.clonePrivsByAcc()); return user; } @Override public boolean hasClientSession(HttpServletRequest req) { return getClientSession(req) != null; } /** * Сохранение инфы о юзере, на основе которой будет создана сессия юзера */ public void initUserToken(InitUserTokenReq data) { if(props.getBoolVal(chatApp_debug_failInitToken)){ throw new DebugException(); } validateState(data); cache.putObjVal(data.token, data, props.getIntVal(users_waitChatSessionTokenLivetime)); } public Integer getUserTokenLivetime(String token) { return cache.getItemLivetime(token); } /** * Создание сессии на основе токена, сохраненного ранее. * Если сессия уже есть, то пропуск */ public long initUserSession(HttpServletRequest req, String reqToken) throws InvalidInputException, AccessDeniedException { if( ! hasText(reqToken)) throw new InvalidInputException("reqToken is null"); InitUserTokenReq tokenData = (InitUserTokenReq) cache.getObjVal(reqToken); if(isEmpty(tokenData)) throw new InvalidInputException("no data by token: "+reqToken); //check client String clientIp = getClientIp(req); if( ! tokenData.clientIp.equals(clientIp)){ throw new AccessDeniedException("invalid client ip: "+clientIp); } String userAgent = getUserAgent(req); if( ! tokenData.clientUserAgent.equals(userAgent)){ throw new AccessDeniedException("invalid userAgent: "+userAgent); } UserSession userSession = null; HttpSession session = req.getSession(false); if(session != null){ userSession = (UserSession)session.getAttribute(USER_INFO); } //session already exists if(userSession != null){ if(userSession.userId == tokenData.userId){ return userSession.userId; } else { throw new InvalidInputException("already has a session for other user. req token: "+tokenData); } } //create new operator's session if(session == null) session = req.getSession(true); userSession = new UserSession(tokenData); session.setAttribute(USER_INFO, userSession); c.events.tryFireEvent(new UserSessionCreatedEvent(userSession)); //log by single acc for(Entry<String, Set<PrivilegeType>> entry : userSession.privsByAcc.entrySet()){ String accId = entry.getKey(); if( ! c.root.chats.isAccExists(accId)) continue; log.info("["+accId+"] "+"OPERATOR session created: privs="+entry.getValue() +", ip="+clientIp +", userAgent="+userAgent +", sessionId="+session.getId()); } return userSession.userId; } public void removeUserSession(RemoveUserSessionReq req){ validateState(req); String token = req.token; //clear cache cache.removeObjVal(token); //clear sessions Collection<HttpSession> sessions = c.sessionsHolder.getState(); for (HttpSession session : sessions) { UserSession userSession = (UserSession) session.getAttribute(USER_INFO); if(userSession == null) continue; if( ! userSession.token.equals(token)) continue; session.removeAttribute(USER_INFO); c.events.tryFireEvent(new UserSessionDestroyedEvent(userSession)); //log by single acc for(Entry<String, Set<PrivilegeType>> entry : userSession.privsByAcc.entrySet()){ String accId = entry.getKey(); if( ! c.root.chats.isAccExists(accId)) continue; log.info("["+accId+"] "+"OPERATOR session removed: privs="+entry.getValue() +", ip="+userSession.info.ip +", userAgent="+userSession.info.userAgent +", sessionId="+session.getId()); } } } public void updateUserSessions(UpdateUserSessionsReq data) { Collection<HttpSession> sessions = c.sessionsHolder.getState(); for (HttpSession session : sessions) { UserSession userSession = (UserSession) session.getAttribute(USER_INFO); if(userSession == null) continue; if(userSession.userId != data.userId) continue; Map<String, Set<PrivilegeType>> oldPrivs = userSession.privsByAcc; UserSession clone = userSession.clone(); clone.privsByAcc = data.privilegesByAccount(); session.setAttribute(USER_INFO, clone); c.events.tryFireEvent(new UserSessionUpdatedEvent(clone)); //log by single acc for(Entry<String, Set<PrivilegeType>> entry : userSession.privsByAcc.entrySet()){ String accId = entry.getKey(); if( ! c.root.chats.isAccExists(accId)) continue; Set<PrivilegeType> newValues = entry.getValue(); Set<PrivilegeType> oldValues = oldPrivs.get(accId); if(oldValues == null || ! oldValues.equals(newValues)){ log.info("["+accId+"] "+"OPERATOR session updated: privs="+newValues +", ip="+userSession.info.ip +", userAgent="+userSession.info.userAgent +", sessionId="+session.getId()); } } } } @Secured public InitUserTokenReq getUserTokenDataFromCache(String token){ checkAccessFor_ADMIN(); return (InitUserTokenReq) cache.getObjVal(token); } private UserSession getUserSession(HttpServletRequest req){ HttpSession session = req.getSession(false); if(session == null) return null; return (UserSession) session.getAttribute(USER_INFO); } }