fi.vm.kapa.identification.proxy.utils.SessionHandlingUtils.java Source code

Java tutorial

Introduction

Here is the source code for fi.vm.kapa.identification.proxy.utils.SessionHandlingUtils.java

Source

/**
 * The MIT License
 * Copyright (c) 2015 Population Register Centre
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package fi.vm.kapa.identification.proxy.utils;

import fi.vm.kapa.identification.dto.SessionDTO;
import fi.vm.kapa.identification.type.AuthMethod;
import fi.vm.kapa.identification.type.SessionProfile;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.inject.Singleton;

import java.io.File;
import java.io.FileReader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Service
@Singleton
public class SessionHandlingUtils {

    private static final Logger logger = LoggerFactory.getLogger(SessionHandlingUtils.class);
    private static final String COMMON_DELIM = "::";
    private static final String CONCATENATE_KEY = "CONCAT";
    private static final String PICK_FIRST_KEY = "FIRST";
    private static final String PICK_FIRST_DELIM = " ";

    private Map<String, String> attributeMap = new HashMap<>();
    private Map<String, String> legacyAttributeMap = new HashMap<>();
    private ConcurrentMap<String, Map<AuthMethod, SessionDTO>> sessionsCache = new ConcurrentHashMap<>();

    @Value("${attribute.map.file}")
    private String attributeMapFile;

    @Value("${legacy.attribute.map.file}")
    private String legacyAttributeMapFile;

    @Value("${authentication.provider.key}")
    private String authenticationProviderKey;

    @Value("${authentication.default.provider.key}")
    private String authenticationDefaultProviderKey;

    @Value("${saml.auth.provider.key}")
    private String samlAuthProviderKey;

    @Value("${legacy.version.key}")
    private String legacyVersionKey;

    @Value("${legacy.version.value}")
    private String legacyVersionValue;

    @PostConstruct
    public void initSessionHandlingService() throws Exception {
        try {
            CSVParser csvParser = new CSVParser(new FileReader(new File(attributeMapFile)),
                    CSVFormat.DEFAULT.withDelimiter(';').withCommentMarker('#'));

            CSVParser csvLegacyParser = new CSVParser(new FileReader(new File(legacyAttributeMapFile)),
                    CSVFormat.DEFAULT.withDelimiter(';').withCommentMarker('#'));

            /* The attribute mapper files have the following syntax:
             * [SP-attribute-key];[External-attribute-mapper-key]
             */
            csvParser.forEach(record -> attributeMap.put(record.get(0), record.get(1)));
            csvLegacyParser.forEach(record -> legacyAttributeMap.put(record.get(0), record.get(1)));
        } catch (Exception e) {
            logger.error("Error initializing CSV parser", e);
        }
    }

    public void parseSessionData(SessionDTO session, Map<String, String> spSessionData, String authContextStr,
            String authProvider) {

        if (session.getSessionProfile() == SessionProfile.TUNNISTUSFI_LEGACY) {
            session.addSessionData(samlAuthProviderKey, authContextStr);
        } else if (session.getSessionProfile() == SessionProfile.VETUMA_SAML2) {
            session.addSessionData(samlAuthProviderKey, authProvider);
        }

        // Only those values what the SP has sent and what the IdP handles will be added to session
        spSessionData.keySet().forEach(spKey -> {
            if (attributeMap.containsKey(spKey) && StringUtils.isNotBlank(spSessionData.get(spKey))) {
                session.addSessionData(attributeMap.get(spKey), spSessionData.get(spKey));
            }
        });
    }

    public void parseLegacyData(SessionDTO session, Map<String, String> spSessionData) {
        switch (session.getSessionProfile()) {
        case TUNNISTUSFI_LEGACY:
            spSessionData.keySet().forEach(spKey -> {
                if (legacyAttributeMap.containsKey(spKey) && StringUtils.isNotBlank(spSessionData.get(spKey))) {
                    session.addSessionData(legacyAttributeMap.get(spKey), spSessionData.get(spKey));
                }
            });
            // This must be checked always since legacy services require this
            if (!session.getSessionData().containsKey(legacyVersionKey)) {
                session.addSessionData(legacyVersionKey, legacyVersionValue);
            }
            break;

        case VETUMA_LEGACY:
            break;
        }
    }

    public void addAdditionalXRoadData(SessionDTO session, Map<String, String> xroadData) {
        xroadData.keySet().forEach(xrKey -> {
            if (attributeMap.containsKey(xrKey) && StringUtils.isNotBlank(xroadData.get(xrKey))) {
                session.addSessionData(attributeMap.get(xrKey), xroadData.get(xrKey));
            }
        });
    }

    public void parseCustomHandledData(SessionDTO session) {
        Map<String, String> sessionData = session.getSessionData();

        if (attributeMap.containsKey(CONCATENATE_KEY)) {
            String[] concatenate = attributeMap.get(CONCATENATE_KEY).split(COMMON_DELIM);
            /* The first value must specify the value to which the next
             * values are concatenated in the order they are listed in
             */
            String concatKey = concatenate[0];
            StringBuilder sb = new StringBuilder();
            for (int i = 1; i < concatenate.length; i++) {
                if (sessionData.containsKey(concatenate[i])) {
                    sb.append(sessionData.get(concatenate[i]));
                    sb.append(" ");
                }
            }
            session.addSessionData(concatKey, sb.toString().trim());
        }
        if (attributeMap.containsKey(PICK_FIRST_KEY)) {
            String[] pickFirst = attributeMap.get(PICK_FIRST_KEY).split(COMMON_DELIM);
            /* The first value must specify the value to which the next
             * value is parsed into, this only picks, as the name suggests,
             * the first value with the given delimiter from the session data
             */
            if (sessionData.containsKey(pickFirst[1])) {
                session.addSessionData(pickFirst[0],
                        sessionData.get(pickFirst[1]).split(PICK_FIRST_DELIM)[0].trim());
            }
        }
    }

    public SessionDTO getSessionByKeyAndAuthMethod(String key, AuthMethod authMethod) {
        return sessionsCache.getOrDefault(key, Collections.emptyMap()).get(authMethod);
    }

    /*
       This is used during the authentication process session finalising phase.
     */
    public Map<AuthMethod, SessionDTO> getSessionDTOMapByKey(String key) {
        return sessionsCache.get(key);
    }

    public void insertIntoSessionCache(String key, AuthMethod authMethod, SessionDTO session) {
        Map<AuthMethod, SessionDTO> authMethodSessionDTOMap = sessionsCache.getOrDefault(key,
                Collections.emptyMap());
        if (!authMethodSessionDTOMap.isEmpty()) {
            //Existing session with same authMethod is overwritten
            authMethodSessionDTOMap.put(authMethod, session);
        } else {
            ConcurrentMap<AuthMethod, SessionDTO> newSessionDTOMap = new ConcurrentHashMap<>();
            newSessionDTOMap.put(authMethod, session);
            authMethodSessionDTOMap = sessionsCache.putIfAbsent(key, newSessionDTOMap);
            //Check and update existing entry
            if (authMethodSessionDTOMap != null) {
                authMethodSessionDTOMap.put(authMethod, session);
            }
        }
    }

    /* Called when uid is set as the final key or session data is updated
     */
    public void replaceSessionCacheKey(String oldKey, String newKey, AuthMethod authMethod, SessionDTO session) {
        sessionsCache.remove(oldKey);
        insertIntoSessionCache(newKey, authMethod, session);
    }

    /* Invalidates the session, sets invalidated attribute for all auth methods in session
     */
    public void invalidateCachedSessionsByKey(String key) {
        sessionsCache.getOrDefault(key, Collections.emptyMap())
                .forEach((authMethod, sessionDTO) -> sessionDTO.setVtjDataInvalid(true));
    }

    /* Checks that no session is invalid
     */
    public boolean invalidSessionsInCacheByKey(String key) {
        boolean invalidSessions = false;
        Map<AuthMethod, SessionDTO> authMethodSessionDTOMap = sessionsCache.getOrDefault(key,
                Collections.emptyMap());
        if (!authMethodSessionDTOMap.isEmpty()) {
            for (Map.Entry<AuthMethod, SessionDTO> sessionDTOEntry : authMethodSessionDTOMap.entrySet()) {
                if (sessionDTOEntry.getValue().isVtjDataInvalid()) {
                    invalidSessions = true;
                }
            }
        }
        return invalidSessions;
    }

    public SessionDTO removeFromSessionCache(String key, AuthMethod authMethod) {
        Map<AuthMethod, SessionDTO> authMethodSessionDTOMap = sessionsCache.getOrDefault(key,
                Collections.emptyMap());
        SessionDTO removedSession = null;
        if (!authMethodSessionDTOMap.isEmpty()) {
            removedSession = authMethodSessionDTOMap.remove(authMethod);
            if (authMethodSessionDTOMap.isEmpty()) {
                sessionsCache.remove(key);
            }
        }
        return removedSession != null ? removedSession : new SessionDTO();
    }

    public Map<String, Map<AuthMethod, SessionDTO>> getSessionsCache() {
        return sessionsCache;
    }

    public int getSessionsCacheSize() {
        return sessionsCache.size();
    }

    public boolean cacheContainsKey(String key) {
        return sessionsCache.containsKey(key);
    }

    public boolean authMethodsInPermittedMethods(String authMethods, String permittedMethods) {
        Set<String> requestedMethodSet = new HashSet<String>(Arrays.asList(authMethods.split(";")));
        Set<String> permittedMethodSet = new HashSet<String>(Arrays.asList(permittedMethods.split(";")));

        if (permittedMethodSet.containsAll(requestedMethodSet)) {
            return true;
        }
        return false;
    }

    public boolean authMethodInPermittedMethods(String authMethod, String permittedAuthMethods) {
        Set<String> permittedMethodSet = new HashSet<String>(Arrays.asList(permittedAuthMethods.split(";")));

        if (permittedMethodSet.contains(authMethod)) {
            return true;
        }
        return false;
    }

    public void debugLogSessionStatus() {
        logger.debug("----------Sessions status----------");
        logger.debug("-----------------------------------");
        sessionsCache.forEach((key, sessionDTOMap) -> {
            logger.debug("***KEY: " + key.toString());
            sessionDTOMap.forEach((authMethod, sessionDTO) -> {
                logger.debug("******AUTHMETHOD: " + authMethod.toString());
                logger.debug("------CKEY: " + sessionDTO.getConversationKey());
                logger.debug("------REQUESTED METHODS: " + sessionDTO.getRequestedAuthenticationMethods());
                logger.debug("------SESSIONID: " + sessionDTO.getSessionId());
                logger.debug("------RELYING PARTY: " + sessionDTO.getRelyingPartyEntityId());
                sessionDTO.getSessionData().forEach((attrKey, attrValue) -> {
                    logger.debug("------------SESSIONATTR: " + attrKey + " VALUE: " + attrValue);
                });
                logger.debug("------SESSIONPROFILE: " + sessionDTO.getSessionProfile().toString());
                logger.debug("------TIMESTAMP: " + sessionDTO.getTimestamp());
                logger.debug("------VALIDATED: " + sessionDTO.isValidated());
            });
        });
    }

    public String usedAuthMethod(AuthMethod authMethod) {
        return authMethod.name();
    }
}