001/* 002 * Copyright (c) 2013 - 2016 TDP Ltd All Rights Reserved. 003 * TDP Ltd grants permission, free of charge, to any person obtaining copies 004 * of this software and its associated documentation files (the "Software"), 005 * to deal in the Software without restriction, including to use, copy, adapt, 006 * publish, distribute, display, perform, sublicense, and sell copies of the 007 * Software, subject to the following condition: You must include the above 008 * copyright notice and this permission notice in all full or partial copies 009 * of the Software. 010 * 011 * TDP LTD PROVIDES THE SOFTWARE "AS IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY, 012 * INCLUDING WITHOUT THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 013 * PARTICULAR PURPOSE, AND NON-INFRINGMENT. TDP LTD, THE AUTHORS OF THE SOFTWARE, 014 * AND THE OWNERS OF COPYRIGHT IN THE SOFTWARE ARE NOT LIABLE FOR ANY CLAIM, DAMAGES, 015 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING 016 * FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 017 * THE SOFTWARE. 018 */ 019package cz.tdp.kshield.notification; 020 021import java.util.ArrayList; 022import java.util.Collection; 023import java.util.List; 024import java.util.concurrent.ConcurrentHashMap; 025import java.util.concurrent.ConcurrentMap; 026import java.util.concurrent.CopyOnWriteArrayList; 027 028import org.apache.commons.logging.Log; 029import org.apache.commons.logging.LogFactory; 030 031import cz.tdp.kshield.client.UserInfo; 032import cz.tdp.kshield.integration.AuthenticationService; 033 034public class KShieldAuthManagerImpl implements KShieldAuthManager 035{ 036 private final AuthenticationService authService; 037 038 private static final UserInfo NULL_AUTH = new UserInfo(); 039 private final ConcurrentMap<String,UserInfo> authenticatedUsers = new ConcurrentHashMap<>(); 040 041 private final ConcurrentMap<String,Collection<KShieldEndpoint>> endpoints = new ConcurrentHashMap<>(); 042 043 private final List<AuthChangeHandler> onLogin = new ArrayList<>(4); 044 private final List<AuthChangeHandler> onLogout = new ArrayList<>(4); 045 046 public KShieldAuthManagerImpl(AuthenticationService authService) { 047 this.authService = authService; 048 } 049 050 @Override 051 public void addAuth(UserInfo userInfo) { 052 if (userInfo == null) throw new IllegalArgumentException("Missing userInfo parameter!"); 053 054 final String ipAddress = userInfo.getIpAddress(); 055 056 if (ipAddress != null) { 057 if (log.isDebugEnabled()) { 058 log.debug("add auth: ip=" + ipAddress + ", user=" + userInfo.getUserID()); 059 } 060 061 authenticatedUsers.put(ipAddress, userInfo); 062 handleOnLogin(userInfo); 063 signal(ipAddress); 064 } 065 else { 066 log.warn("add auth: missing ip address"); 067 } 068 } 069 070 @Override 071 public void removeAuth(String ipAddress) { 072 if (log.isDebugEnabled()) { 073 log.debug("disable auth: ip=" + ipAddress); 074 } 075 076 final UserInfo userInfo = authenticatedUsers.put(ipAddress, NULL_AUTH); 077 if (userInfo != null && userInfo.getIpAddress() != null) { 078 handleOnLogout(userInfo); 079 } 080 signal(ipAddress); 081 } 082 083 @Override 084 public void resetAuth(String ipAddress) { 085 if (isNotEmpty(ipAddress)) { 086 if (log.isDebugEnabled()) { 087 log.debug("reset authentication for ip=" + ipAddress); 088 } 089 090 authenticatedUsers.remove(ipAddress); 091 signal(ipAddress); 092 } 093 else { 094 log.debug("reset all authentications"); 095 096 authenticatedUsers.clear(); 097 098 for (String addr : endpoints.keySet()) { 099 signal(addr); 100 } 101 } 102 } 103 104 @Override 105 public void registerOnLoginHandler(AuthChangeHandler handler) { 106 onLogin.add(handler); 107 } 108 109 @Override 110 public void registerOnLogoutHandler(AuthChangeHandler handler) { 111 onLogout.add(handler); 112 } 113 114 private void signal(String ipAddress) { 115 final Collection<KShieldEndpoint> list = endpoints.get(ipAddress); 116 if (list != null) { 117 signal(list); 118 } 119 } 120 121 private void signal(Collection<KShieldEndpoint> list) { 122 for (KShieldEndpoint ws : list) { 123 if (ws.isActive()) { 124 if (log.isDebugEnabled()) { 125 log.debug("signalling WebSocket (" + ws + ")"); 126 } 127 ws.signal(); 128 } 129 } 130 } 131 132 @Override 133 public boolean hasAuth(String ipAddress, String user) { 134 UserInfo userInfo = authenticatedUsers.get(ipAddress); 135 136 final String userID; 137 138 if (userInfo == null) { 139 userInfo = authService.createUserInfo(ipAddress); 140 141 if (userInfo != null && userInfo.getUserID() != null) { 142 userID = userInfo.getUserID(); 143 144 if (log.isDebugEnabled()) { 145 log.debug("kshield auth changed: add ip=" + ipAddress + ", user=" + userID); 146 } 147 148 addAuth(userInfo); 149 } 150 else { 151 if (log.isDebugEnabled()) { 152 log.debug("kshield auth changed: remove ip=" + ipAddress); 153 } 154 155 removeAuth(ipAddress); 156 157 userID = null; 158 } 159 } 160 else { 161 userID = userInfo.getUserID(); 162 } 163 164 return user != null ? user.equals(userID) : isNotEmpty(userID); 165 } 166 167 private void handleOnLogin(UserInfo userInfo) { 168 for (AuthChangeHandler handler : onLogin) { 169 handler.handle(userInfo); 170 } 171 } 172 173 private void handleOnLogout(UserInfo userInfo) { 174 for (AuthChangeHandler handler : onLogout) { 175 handler.handle(userInfo); 176 } 177 } 178 179 @Override 180 public void addEndpoint(String ipAddress, KShieldEndpoint ws) { 181 if (log.isDebugEnabled()) { 182 log.debug("adding WebSocket Endpoint ("+ws+") for ipAddress=" + ipAddress); 183 } 184 185 Collection<KShieldEndpoint> list = endpoints.get(ipAddress); 186 if (list == null) { 187 list = new CopyOnWriteArrayList<>(); 188 final Collection<KShieldEndpoint> origList = endpoints.putIfAbsent(ipAddress, list); 189 if (origList != null) { 190 list = origList; 191 } 192 } 193 194 list.add(ws); 195 } 196 197 @Override 198 public void removeEndpoint(String ipAddress, KShieldEndpoint ws) { 199 if (ipAddress == null) throw new IllegalArgumentException("Missing ipAddress parameter!"); 200 201 if (log.isDebugEnabled()) { 202 log.debug("removing WebSocket Endpoint ("+ws+") for ipAddress=" + ipAddress); 203 } 204 205 final Collection<KShieldEndpoint> list = endpoints.get(ipAddress); 206 if (list != null) { 207 list.remove(ws); 208 } 209 } 210 211 @Override 212 public String poll(String ipAddress, String userID, boolean authenticated) { 213 if (log.isDebugEnabled()) { 214 log.debug("poll: " + ipAddress + ", userID="+userID + ", authenticated=" + authenticated); 215 } 216 217 final String result; 218 219 if (authenticated) { 220 if (isNotEmpty(userID)) { 221 if (hasAuth(ipAddress, userID)) { 222 result = "LOGIN"; 223 } 224 else { 225 result = "LOGOUT"; 226 } 227 } 228 else { 229 result = "AUTH"; 230 } 231 } 232 else if (hasAuth(ipAddress, null)) { 233 result = "LOGIN"; 234 } 235 else { 236 result = "OFF"; 237 } 238 239 return result; 240 } 241 242 private boolean isNotEmpty(String userID) { 243 return userID != null && !userID.isEmpty(); 244 } 245 246 private static final Log log = LogFactory.getLog(KShieldAuthManagerImpl.class); 247}