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}