com.hypersocket.session.SessionServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.hypersocket.session.SessionServiceImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2013 Hypersocket Limited.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 ******************************************************************************/
package com.hypersocket.session;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.UUID;

import javax.annotation.PostConstruct;

import org.quartz.JobDataMap;
import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.stereotype.Service;

import com.hypersocket.auth.AuthenticationScheme;
import com.hypersocket.auth.AuthenticationService;
import com.hypersocket.auth.PasswordEnabledAuthenticatedServiceImpl;
import com.hypersocket.config.ConfigurationService;
import com.hypersocket.events.EventService;
import com.hypersocket.permissions.AccessDeniedException;
import com.hypersocket.permissions.PermissionCategory;
import com.hypersocket.permissions.PermissionStrategy;
import com.hypersocket.permissions.SystemPermission;
import com.hypersocket.realm.Principal;
import com.hypersocket.realm.Realm;
import com.hypersocket.realm.RealmService;
import com.hypersocket.resource.Resource;
import com.hypersocket.scheduler.SchedulerService;
import com.hypersocket.session.events.SessionClosedEvent;
import com.hypersocket.session.events.SessionEvent;
import com.hypersocket.session.events.SessionOpenEvent;
import com.hypersocket.tables.ColumnSort;

import net.sf.uadetector.OperatingSystemFamily;
import net.sf.uadetector.ReadableUserAgent;
import net.sf.uadetector.UserAgentStringParser;
import net.sf.uadetector.internal.data.domain.OperatingSystem;
import net.sf.uadetector.service.UADetectorServiceFactory;

@Service
public class SessionServiceImpl extends PasswordEnabledAuthenticatedServiceImpl
        implements SessionService, ApplicationListener<ContextStartedEvent> {

    @Autowired
    SessionRepository repository;

    @Autowired
    AuthenticationService authenticationService;

    @Autowired
    ConfigurationService configurationService;

    @Autowired
    SchedulerService schedulerService;

    @Autowired
    RealmService realmService;

    @Autowired
    EventService eventService;

    static final String SESSION_TIMEOUT = "session.timeout";

    static Logger log = LoggerFactory.getLogger(SessionServiceImpl.class);

    public static String TOKEN_PREFIX = "_TOK";

    UserAgentStringParser parser;

    Map<Session, List<ResourceSession<?>>> resourceSessions = new HashMap<Session, List<ResourceSession<?>>>();
    Map<String, SessionResourceToken<?>> sessionTokens = new HashMap<String, SessionResourceToken<?>>();

    Map<String, Session> nonCookieSessions = new HashMap<String, Session>();

    Session systemSession;

    @PostConstruct
    private void postConstruct() throws AccessDeniedException {

        if (log.isInfoEnabled()) {
            log.info("Loading User Agent database");
        }
        parser = UADetectorServiceFactory.getCachingAndUpdatingParser();
        if (log.isInfoEnabled()) {
            log.info("Loaded User Agent database");
        }

        PermissionCategory cat = permissionService.registerPermissionCategory(RESOURCE_BUNDLE, "session.category");

        for (SessionPermission perm : SessionPermission.values()) {
            permissionService.registerPermission(perm, cat);
        }

        eventService.registerEvent(SessionEvent.class, RESOURCE_BUNDLE);
        eventService.registerEvent(SessionOpenEvent.class, RESOURCE_BUNDLE);
        eventService.registerEvent(SessionClosedEvent.class, RESOURCE_BUNDLE);

    }

    private Session createSystemSession() {
        Session session = new Session();
        session.setId(UUID.randomUUID().toString());
        session.setCurrentRealm(realmService.getSystemRealm());
        session.setOs(System.getProperty("os.name"));
        session.setOsVersion(System.getProperty("os.version"));
        session.setPrincipal(realmService.getSystemPrincipal());
        session.setUserAgent("N/A");
        session.setUserAgentVersion("N/A");
        session.setRemoteAddress("N/A");
        session.system = true;

        repository.saveEntity(session);
        return session;
    }

    @Override
    public Session getSystemSession() {
        if (systemSession == null) {
            systemSession = createSystemSession();
        }
        return systemSession;
    }

    @Override
    public Session openSession(String remoteAddress, Principal principal, AuthenticationScheme completedScheme,
            String userAgent, Map<String, String> parameters) {

        Session session = null;

        if (userAgent == null) {
            userAgent = "Unknown";
        } else if (userAgent.startsWith("Hypersocket-Client")) {

            String[] values = userAgent.split(";");

            String agent = "Hypersocket Client";
            String agentVersion = "1.0";
            if (values.length > 1) {
                agentVersion = values[1];
            }
            String os = "Unknown";
            if (values.length > 2) {
                if (values[3].toLowerCase().startsWith("windows")) {
                    os = "Windows";
                } else if (values[3].toLowerCase().startsWith("mac os x")) {
                    os = "OS X";
                } else if (values[3].toLowerCase().startsWith("linux")) {
                    os = "Linux";
                } else {
                    os = values[3];
                }
            }
            String osVersion = "Unknown";
            if (values.length > 3) {
                osVersion = values[4];
            }

            session = repository.createSession(remoteAddress, principal, completedScheme, agent, agentVersion, os,
                    osVersion, configurationService.getIntValue(principal.getRealm(), SESSION_TIMEOUT));
        } else {
            ReadableUserAgent ua = parser.parse(userAgent);

            session = repository.createSession(remoteAddress, principal, completedScheme, ua.getFamily().getName(),
                    ua.getVersionNumber().toVersionString(), ua.getOperatingSystem().getFamily().getName(),
                    ua.getOperatingSystem().getVersionNumber().toVersionString(),
                    configurationService.getIntValue(principal.getRealm(), SESSION_TIMEOUT));
        }

        eventService.publishEvent(new SessionOpenEvent(this, session));
        return session;
    }

    @Override
    public Session getSession(String id) {
        return repository.getSessionById(id);
    }

    @Override
    public synchronized boolean isLoggedOn(Session session, boolean touch) {
        if (session == null)
            return false;

        repository.refresh(session);

        if (session.getSignedOut() == null) {

            Calendar currentTime = Calendar.getInstance();
            Calendar c = Calendar.getInstance();
            c.setTime(session.getLastUpdated());
            c.add(Calendar.MINUTE, session.getTimeout());

            if (log.isDebugEnabled()) {
                log.debug("Checking session timeout currentTime=" + currentTime.getTime() + " lastUpdated="
                        + session.getLastUpdated() + " timeoutThreshold=" + c.getTime());
            }

            if (c.before(currentTime)) {
                if (log.isDebugEnabled()) {
                    log.debug("Session has timed out");
                }
                closeSession(session);

                if (log.isDebugEnabled()) {
                    log.debug("Session " + session.getPrincipal().getPrincipalName() + "/" + session.getId()
                            + " is now closed");
                }

                return false;
            }

            if (touch) {
                session.touch();
                if (session.isReadyForUpdate()) {
                    repository.updateSession(session);
                    if (log.isDebugEnabled()) {
                        log.debug("Session " + session.getPrincipal().getPrincipalName() + "/" + session.getId()
                                + " state has been updated");
                    }
                }

            }
            return true;
        } else {
            return false;
        }

    }

    @Override
    public void closeSession(Session session) {

        if (session.getSignedOut() != null) {
            throw new IllegalArgumentException("Attempting to close a session which is already closed!");
        }

        if (nonCookieSessions.containsKey(session.getNonCookieKey())) {
            nonCookieSessions.remove(session.getNonCookieKey());
        }

        if (resourceSessions.containsKey(session)) {
            List<ResourceSession<?>> rs = resourceSessions.remove(session);
            for (ResourceSession<?> s : rs) {
                s.close();
            }
        }

        synchronized (sessionTokens) {
            Set<String> tokens = new HashSet<String>();
            for (SessionResourceToken<?> token : sessionTokens.values()) {
                if (token.getSession().equals(session)) {
                    tokens.add(token.getShortCode());
                }
            }

            for (String t : tokens) {
                sessionTokens.remove(t);
            }
        }

        session.setSignedOut(new Date());
        session.setNonCookieKey(null);
        repository.updateSession(session);

        if (!session.isSystem()) {
            eventService.publishEvent(new SessionClosedEvent(this, session));
        }
    }

    @Override
    public void switchRealm(Session session, Realm realm) throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM_ADMINISTRATION, SystemPermission.SYSTEM);

        if (log.isInfoEnabled()) {
            log.info("Switching " + session.getPrincipal().getName() + " to " + realm.getName() + " realm");
        }

        session.setCurrentRealm(realm);
        repository.updateSession(session);
    }

    protected void assertImpersonationPermission() throws AccessDeniedException {
        if (hasSessionContext()) {
            if (getCurrentSession().isImpersonating()) {
                verifyPermission(getCurrentSession().getPrincipal(), PermissionStrategy.EXCLUDE_IMPLIED,
                        SystemPermission.SYSTEM_ADMINISTRATION, SystemPermission.SYSTEM);
                return;
            }
        }

        assertAnyPermission(SystemPermission.SYSTEM_ADMINISTRATION, SystemPermission.SYSTEM);

    }

    @Override
    public void switchPrincipal(Session session, Principal principal) throws AccessDeniedException {
        switchPrincipal(session, principal, false);
    }

    @Override
    public void switchPrincipal(Session session, Principal principal, boolean inheritPermissions)
            throws AccessDeniedException {

        assertImpersonationPermission();

        if (log.isInfoEnabled()) {
            log.info("Switching " + session.getPrincipal().getName() + " to " + principal.getName());
        }

        session.setImpersonatedPrincipal(principal);
        session.setInheritPermissions(inheritPermissions);

        setCurrentSession(session, getCurrentLocale());
        repository.updateSession(session);
    }

    @Override
    public void registerResourceSession(Session session, ResourceSession<?> resourceSession) {

        if (!resourceSessions.containsKey(session)) {
            resourceSessions.put(session, new ArrayList<ResourceSession<?>>());
        }
        resourceSessions.get(session).add(resourceSession);
    }

    @Override
    public boolean hasResourceSession(Session session, Resource resource) {
        if (resourceSessions.containsKey(session)) {
            List<ResourceSession<?>> rs = resourceSessions.get(session);
            for (ResourceSession<?> s : rs) {
                if (s.getResource().equals(resource)) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public void unregisterResourceSession(Session session, ResourceSession<?> resourceSession) {

        if (resourceSessions.containsKey(session)) {
            resourceSessions.get(session).remove(resourceSession);
        }
    }

    @Override
    public List<Session> getActiveSessions() throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM, SystemPermission.SYSTEM_ADMINISTRATION);

        return repository.getActiveSessions();
    }

    @Override
    public Long getActiveSessionCount(boolean distinctUsers) throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM, SystemPermission.SYSTEM_ADMINISTRATION);

        return repository.getActiveSessionCount(distinctUsers);
    }

    @Override
    public Long getSessionCount(Date startDate, Date endDate, boolean distinctUsers) throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM, SystemPermission.SYSTEM_ADMINISTRATION);

        return repository.getSessionCount(startDate, endDate, distinctUsers);

    }

    @Override
    public Map<String, Long> getBrowserCount(Date startDate, Date endDate) throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM, SystemPermission.SYSTEM_ADMINISTRATION);

        return repository.getBrowserCount(startDate, endDate);

    }

    @Override
    public Map<String, Long> getIPCount(Date startDate, Date endDate) throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM, SystemPermission.SYSTEM_ADMINISTRATION);

        return repository.getIPCount(startDate, endDate);

    }

    @Override
    public Map<String, Long> getOSCount(Date startDate, Date endDate) throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM, SystemPermission.SYSTEM_ADMINISTRATION);

        return repository.getOSCount(startDate, endDate);

    }

    @Override
    public <T> SessionResourceToken<T> createSessionToken(T resource) {

        SessionResourceToken<T> token = new SessionResourceToken<T>(getCurrentSession(), resource);
        sessionTokens.put(token.getShortCode(), token);
        return token;
    }

    @Override
    public <T> SessionResourceToken<T> getSessionToken(String shortCode, Class<T> resourceClz) {

        if (sessionTokens.containsKey(shortCode)) {

            @SuppressWarnings("unchecked")
            SessionResourceToken<T> token = (SessionResourceToken<T>) sessionTokens.get(shortCode);

            if (isLoggedOn(token.getSession(), true)) {
                return token;
            }
        }

        return null;
    }

    @Override
    public <T> T getSessionTokenResource(String shortCode, Class<T> resourceClz) {

        if (sessionTokens.containsKey(shortCode)) {

            @SuppressWarnings("unchecked")
            SessionResourceToken<T> token = (SessionResourceToken<T>) sessionTokens.get(shortCode);

            if (isLoggedOn(token.getSession(), true)) {
                return token.getResource();
            }
        }

        return null;
    }

    @Override
    public Session getNonCookieSession(String remoteAddr, String requestHeader,
            String authenticationSchemeResourceKey) throws AccessDeniedException {

        String key = createNonCookieSessionKey(remoteAddr, requestHeader, authenticationSchemeResourceKey);

        Session session = nonCookieSessions.get(key);

        if (session != null) {
            if (!isLoggedOn(session, true)) {
                throw new AccessDeniedException();
            }
            return session;
        }
        throw new AccessDeniedException();
    }

    @Override
    public void registerNonCookieSession(String remoteAddr, String requestHeader,
            String authenticationSchemeResourceKey, Session session) {

        String key = createNonCookieSessionKey(remoteAddr, requestHeader, authenticationSchemeResourceKey);

        session.setNonCookieKey(key);
        nonCookieSessions.put(key, session);
    }

    private String createNonCookieSessionKey(String remoteAddr, String requestHeader,
            String authenticationSchemeResourceKey) {
        StringBuffer buf = new StringBuffer();
        buf.append(remoteAddr);
        buf.append("|");
        buf.append(requestHeader);
        buf.append("|");
        buf.append(authenticationSchemeResourceKey);
        return buf.toString();
    }

    @Override
    public void revertPrincipal(Session session) throws AccessDeniedException {

        assertImpersonationPermission();

        if (log.isInfoEnabled()) {
            log.info("Switching " + session.getCurrentPrincipal().getName() + " to "
                    + session.getPrincipal().getName());
        }

        session.setImpersonatedPrincipal(null);
        session.setInheritPermissions(false);

        setCurrentSession(session, getCurrentLocale());
        repository.updateSession(session);
    }

    @Override
    public void onApplicationEvent(ContextStartedEvent event) {

        if (log.isInfoEnabled()) {
            log.info("Scheduling session reaper job");
        }

        for (Session session : repository.getSystemSessions()) {
            if (systemSession != null && systemSession.equals(session)) {
                continue;
            }
            closeSession(session);
        }

        try {
            JobDataMap data = new JobDataMap();
            data.put("jobName", "firstRunSessionReaperJob");
            data.put("firstRun", true);

            schedulerService.scheduleNow(SessionReaperJob.class, data);

            data = new JobDataMap();
            data.put("jobName", "sessionReaperJob");

            schedulerService.scheduleIn(SessionReaperJob.class, data, 60000, 60000);
        } catch (SchedulerException e) {
            log.error("Failed to schedule session reaper job", e);
        }

    }

    @Override
    public List<?> searchResources(Realm realm, String searchPattern, int start, int length, ColumnSort[] sorting)
            throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM_ADMINISTRATION, SessionPermission.READ);

        return repository.search(realm, searchPattern, start, length, sorting);
    }

    @Override
    public Long getResourceCount(Realm realm, String searchPattern) throws AccessDeniedException {

        assertAnyPermission(SystemPermission.SYSTEM_ADMINISTRATION, SessionPermission.READ);

        return repository.getResourceCount(realm, searchPattern);
    }

}