/*
* Password Management Servlets (PWM)
* http://www.pwm-project.org
*
* Copyright (c) 2006-2009 Novell, Inc.
* Copyright (c) 2009-2017 The PWM Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package password.pwm.svc.sessiontrack;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import password.pwm.PwmApplication;
import password.pwm.bean.LocalSessionStateBean;
import password.pwm.bean.LoginInfoBean;
import password.pwm.bean.UserIdentity;
import password.pwm.bean.UserInfoBean;
import password.pwm.bean.pub.SessionStateInfoBean;
import password.pwm.error.PwmException;
import password.pwm.health.HealthRecord;
import password.pwm.http.PwmSession;
import password.pwm.svc.PwmService;
import password.pwm.util.logging.PwmLogger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class SessionTrackService implements PwmService {
private static final PwmLogger LOGGER = PwmLogger.forClass(SessionTrackService.class);
private final transient Map<PwmSession,Boolean> pwmSessions = new ConcurrentHashMap<>();
private final Cache<UserIdentity,Object> recentLoginCache = Caffeine.newBuilder()
.maximumSize(10)
.build();
private PwmApplication pwmApplication;
@Override
public STATUS status() {
return STATUS.OPEN;
}
@Override
public void init(final PwmApplication pwmApplication) throws PwmException {
this.pwmApplication = pwmApplication;
}
@Override
public void close() {
pwmSessions.clear();
}
@Override
public List<HealthRecord> healthCheck() {
return Collections.emptyList();
}
@Override
public ServiceInfo serviceInfo() {
return null;
}
public enum DebugKey {
HttpSessionCount,
HttpSessionTotalSize,
HttpSessionAvgSize,
}
public void addSessionData(final PwmSession pwmSession) {
pwmSessions.put(pwmSession,Boolean.FALSE);
}
public void removeSessionData(final PwmSession pwmSession) {
pwmSessions.remove(pwmSession);
}
private Set<PwmSession> copyOfSessionSet() {
final Set<PwmSession> newSet = new HashSet<>();
newSet.addAll(pwmSessions.keySet());
return newSet;
}
public Map<DebugKey, String> getDebugData() {
try {
final Collection<PwmSession> sessionCopy = copyOfSessionSet();
int sessionCounter = 0;
long sizeTotal = 0;
for (final PwmSession pwmSession : sessionCopy) {
try {
sizeTotal += pwmSession.size();
sessionCounter++;
} catch (Exception e) {
LOGGER.error("error during session size calculation: " + e.getMessage());
}
}
final Map<DebugKey, String> returnMap = new HashMap<>();
returnMap.put(DebugKey.HttpSessionCount, String.valueOf(sessionCounter));
returnMap.put(DebugKey.HttpSessionTotalSize, String.valueOf(sizeTotal));
returnMap.put(DebugKey.HttpSessionAvgSize,
sessionCounter < 1 ? "0" : String.valueOf((int) (sizeTotal / sessionCounter)));
return returnMap;
} catch (Exception e) {
LOGGER.error("error during session debug generation: " + e.getMessage());
}
return Collections.emptyMap();
}
public int ldapConnectionCount() {
int counter = 0;
try {
for (final String identifer : pwmApplication.getConfig().getLdapProfiles().keySet()) {
if (pwmApplication.getProxyChaiProvider(identifer).isConnected()) {
counter++;
}
}
for (final PwmSession loopSession : currentValidSessionSet()) {
if (loopSession != null) {
if (loopSession.getSessionManager().hasActiveLdapConnection()) {
counter++;
}
}
}
} catch (Exception e) {
LOGGER.error("unexpected error counting ldap connections: " + e.getMessage());
}
return counter;
}
private Set<PwmSession> currentValidSessionSet() {
final Set<PwmSession> returnSet = new HashSet<>();
for (final PwmSession pwmSession : copyOfSessionSet()) {
if (pwmSession != null) {
returnSet.add(pwmSession);
}
}
return returnSet;
}
public Iterator<SessionStateInfoBean> getSessionInfoIterator() {
final Iterator<PwmSession> sessionIterator = new HashSet<>(currentValidSessionSet()).iterator();
return new Iterator<SessionStateInfoBean>() {
@Override
public boolean hasNext() {
return sessionIterator.hasNext();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public SessionStateInfoBean next() {
final PwmSession pwmSession = sessionIterator.next();
if (pwmSession != null) {
return infoBeanFromPwmSession(pwmSession);
}
return null;
}
};
}
private static SessionStateInfoBean infoBeanFromPwmSession(final PwmSession loopSession) {
final LocalSessionStateBean loopSsBean = loopSession.getSessionStateBean();
final LoginInfoBean loginInfoBean = loopSession.getLoginInfoBean();
final SessionStateInfoBean sessionStateInfoBean = new SessionStateInfoBean();
sessionStateInfoBean.setLabel(loopSession.getSessionStateBean().getSessionID());
sessionStateInfoBean.setCreateTime(loopSession.getSessionStateBean().getSessionCreationTime());
sessionStateInfoBean.setLastTime(loopSession.getSessionStateBean().getSessionLastAccessedTime());
sessionStateInfoBean.setIdle(loopSession.getIdleTime().asCompactString());
sessionStateInfoBean.setLocale(loopSsBean.getLocale());
sessionStateInfoBean.setSrcAddress(loopSsBean.getSrcAddress());
sessionStateInfoBean.setSrcHost(loopSsBean.getSrcHostname());
sessionStateInfoBean.setLastUrl(loopSsBean.getLastRequestURL());
sessionStateInfoBean.setIntruderAttempts(loopSsBean.getIntruderAttempts());
if (loopSession.isAuthenticated()) {
final UserInfoBean loopUiBean = loopSession.getUserInfoBean();
sessionStateInfoBean.setLdapProfile(loginInfoBean.isAuthenticated() ? loopUiBean.getUserIdentity().getLdapProfileID() : "");
sessionStateInfoBean.setUserDN(loginInfoBean.isAuthenticated() ? loopUiBean.getUserIdentity().getUserDN() : "");
sessionStateInfoBean.setUserID(loginInfoBean.isAuthenticated() ? loopUiBean.getUsername() : "");
}
return sessionStateInfoBean;
}
public int sessionCount() {
return currentValidSessionSet().size();
}
public void addRecentLogin(final UserIdentity userIdentity) {
recentLoginCache.put(userIdentity,this);
}
public List<UserIdentity> getRecentLogins() {
return Collections.unmodifiableList(new ArrayList<>(recentLoginCache.asMap().keySet()));
}
}