nl.b3p.gis.viewer.services.GisSecurityRealm.java Source code

Java tutorial

Introduction

Here is the source code for nl.b3p.gis.viewer.services.GisSecurityRealm.java

Source

/*
 * B3P Gisviewer is an extension to Flamingo MapComponents making
 * it a complete webbased GIS viewer and configuration tool that
 * works in cooperation with B3P Kaartenbalie.
 *
 * Copyright 2006, 2007, 2008 B3Partners BV
 * 
 * This file is part of B3P Gisviewer.
 * 
 * B3P Gisviewer 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 3 of the License, or
 * (at your option) any later version.
 * 
 * B3P Gisviewer 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 B3P Gisviewer.  If not, see <http://www.gnu.org/licenses/>.
 */
package nl.b3p.gis.viewer.services;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import nl.b3p.commons.security.XmlSecurityDatabase;
import nl.b3p.commons.services.FormUtils;
import nl.b3p.commons.services.B3PCredentials;
import nl.b3p.commons.services.HttpClientConfigured;
import nl.b3p.wms.capabilities.ServiceProvider;
import nl.b3p.wms.capabilities.WMSCapabilitiesReader;
import nl.b3p.zoeker.configuratie.Bron;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.securityfilter.filter.SecurityFilter;
import org.securityfilter.filter.SecurityRequestWrapper;
import org.securityfilter.realm.ExternalAuthenticatedRealm;
import org.securityfilter.realm.FlexibleRealmInterface;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class GisSecurityRealm implements FlexibleRealmInterface, ExternalAuthenticatedRealm {

    private static final Log log = LogFactory.getLog(GisSecurityRealm.class);
    private static final String FORM_USERNAME = "j_username";
    private static final String FORM_PASSWORD = "j_password";
    private static final String FORM_CODE = "j_code";
    private static final String CAPABILITIES_QUERYSTRING = "REQUEST=GetCapabilities&VERSION=1.1.1&SERVICE=WMS";
    static protected Map<String, ServiceProvider> perUserNameSPCache = new HashMap();

    public Principal authenticate(SecurityRequestWrapper request) {
        String username = FormUtils.nullIfEmpty(request.getParameter(FORM_USERNAME));
        String password = FormUtils.nullIfEmpty(request.getParameter(FORM_PASSWORD));
        String code = FormUtils.nullIfEmpty(request.getParameter(FORM_CODE));

        String appCode = FormUtils.nullIfEmpty(request.getParameter("appCode"));
        String cmsPageId = FormUtils.nullIfEmpty(request.getParameter("cmsPageId"));
        String forceViewer = FormUtils.nullIfEmpty(request.getParameter("forceViewer"));

        HttpSession session = request.getSession();
        //        session.setAttribute("loginForm", true);

        if (appCode != null) {
            StringBuilder sb = new StringBuilder();
            sb.append("viewer.do?appCode=");
            sb.append(appCode);
            if (cmsPageId != null) {
                sb.append("&cmsPageId=");
                sb.append(cmsPageId);
            }
            if (forceViewer != null) {
                sb.append("&forceViewer=");
                sb.append(forceViewer);
            }
            session.setAttribute(SecurityFilter.SAVED_REQUEST_URL, sb.toString());
        }

        return authenticate(username, password, code, request);
    }

    public Principal getAuthenticatedPrincipal(String username, String password) {
        return authenticate(username, password);
    }

    public boolean isUserInRole(Principal principal, String rolename) {
        if (principal == null) {
            return false;
        }
        boolean inRole = ((GisPrincipal) principal).isInRole(rolename);
        if (!inRole) {
            inRole = XmlSecurityDatabase.isUserInRole(principal.getName(), rolename);
        }
        return inRole;
    }

    public static String createCapabilitiesURL(String code) {
        String url = HibernateUtil.createPersonalKbUrl(code);
        if (url.indexOf('?') == -1) {
            url += "?";
        }
        if (url.indexOf('?') == url.length() - 1) {
            url += CAPABILITIES_QUERYSTRING;
        } else if (url.lastIndexOf('&') == url.length() - 1) {
            url += CAPABILITIES_QUERYSTRING;
        } else {
            url += "&" + CAPABILITIES_QUERYSTRING;
        }

        log.debug("Using external kb url: " + url);

        return url;
    }

    public static String createInternalCapabilitiesURL(String code) {
        String url = HibernateUtil.createInternalKbUrl(code);

        if (url == null || url.equals("")) {
            url = HibernateUtil.createPersonalKbUrl(code);
        }

        if (url.indexOf('?') == -1) {
            url += "?";
        }
        if (url.indexOf('?') == url.length() - 1) {
            url += CAPABILITIES_QUERYSTRING;
        } else if (url.lastIndexOf('&') == url.length() - 1) {
            url += CAPABILITIES_QUERYSTRING;
        } else {
            url += "&" + CAPABILITIES_QUERYSTRING;
        }

        log.debug("Using internal kb url: " + url);

        return url;
    }

    protected static GisPrincipal authenticateFake(String username) {

        List roles = new ArrayList();
        roles.add(HibernateUtil.GEBRUIKERS_ROL);
        roles.add(HibernateUtil.THEMABEHEERDERS_ROL);

        return new GisPrincipal(username, roles);
    }

    public static GisPrincipal authenticateHttp(String location, String username, String password, String code,
            SecurityRequestWrapper request) {

        log.debug("Start authenticateHttp()");

        WMSCapabilitiesReader wmscr = new WMSCapabilitiesReader();
        ServiceProvider sp = null;

        /* TODO: Wat te doen als de Applicatie een gebruikerscode heeft die ongeldig is
         Bijvoorbeeld ABC ? Ik denk dat je dan gewoon niet kunt inloggen. Misschien aan gisviewerconfig
         kant controleren of ingevulde gebruikerscode bij opslaan van een Applicatie wel
         geldig is ?
         */

        //Indien location "_VIEWER_CONFIG=true" bevat dan onder aparte key opslaan
        //omdat dan alle kaarten worden opgevraagd tbv configuratie. Welke kaarten
        //feitelijk worden opgehaald wordt bepaald door rol bij username/pw.
        //Indien via code ingelogd cachen met code
        String key = "";
        if (username == null && code != null) {
            key = code;
        } else if (username != null && username.equals("anoniem")) {
            key = code;
        } else if (code == null && username != null) {
            key = username;
        } else if (username != null && password != null) {
            key = username;
        }

        boolean isConfig = location == null ? false : location.contains("_VIEWER_CONFIG=true");
        if (key == null || key.isEmpty()) {
            return null;
        } else if (isConfig) {
            key += "_VIEWER_CONFIG";
        }

        String ip = null;

        if (request != null) {
            ip = request.getRemoteAddr();

            if (ip == null) {
                ip = request.getLocalAddr();
            }
        }

        log.debug("Username: " + username + ", Password: ****, Code: " + code + ", Key: " + key + ", Ip: " + ip);

        /* Do user/password check at kaartenbalie. */
        if (username != null && code == null || code.isEmpty() || code.equals("")
                || code.equalsIgnoreCase("null")) {

            log.debug("Checking login with kaartenbalie!");

            boolean canLogin = GisSecurityRealm.canLoginKaartenbalie(username, password, ip);

            if (!canLogin) {
                log.error("Gebruiker " + username + " is ongeldig. IP-adres: " + ip);

                return null;
            }
        }

        try {
            /* 
             WMS getCapabilities (serviceprovider) cachen.
             Als 'cacheOnDisk' en 'cacheOnDiskPath' params niet
             in web.xml staan plaatst hij de ServiceProvider
             objecten in geheugen (HashMap)            
             */
            Boolean cacheOnDisk = HibernateUtil.cacheOnDisk;
            if (cacheOnDisk != null && cacheOnDisk && isCachedOnDisk(key)) {
                sp = readCacheFromDisk(key);

                log.debug("User from DISK cache " + sp.getUserName() + " using key " + key);
            }

            if (cacheOnDisk == null || !cacheOnDisk && isInSPCache(key)) {
                sp = getFromSPCache(key);

                log.debug("User from MEM cache " + sp.getUserName() + " using key " + key);

            } else if (sp == null) {
                sp = wmscr.getProvider(location, username, password, ip);

                if (username != null && password != null && password.length() > 0) {
                    sp.setPassword(password);
                }

                if (cacheOnDisk != null && cacheOnDisk) {
                    writeCacheToDisk(key, sp);

                    log.debug("Login new in DISK cache for user " + sp.getUserName() + " using key " + key);
                } else {
                    putInSPCache(key, sp);

                    log.debug("Login new in MEM for user " + sp.getUserName() + " using key " + key);
                }
            }

        } catch (Exception ex) {
            if (log.isDebugEnabled()) {
                log.debug("Error reading GetCapabilities ", ex);
            } else {
                log.error("Error reading GetCapabilities: " + ex.getLocalizedMessage());
            }

            return null;
        }

        if (sp == null) {
            log.error("No ServiceProvider found, denying login!");
            return null;
        }

        /* code uit service provider gebruiken */
        if (sp.getPersonalCode() != null) {
            code = sp.getPersonalCode();
        }

        if (sp.getUserName() != null) {
            username = sp.getUserName();
        }

        if (username == null || username.length() < 1) {
            username = HibernateUtil.ANONYMOUS_USER;
        }

        log.debug("Login for user: " + username);

        return new GisPrincipal(username, password, code, sp);
    }

    /* Do user/password check at kaartenbalie */
    public static boolean canLoginKaartenbalie(String username, String password, String ip) {

        String loginKbUrl = HibernateUtil.getKbLoginUrl();
        String kbUrl = HibernateUtil.getKbUrl();

        /* For when loginKbUrl not in web.xml */
        if (loginKbUrl == null && kbUrl != null) {
            if (kbUrl.contains("/services")) {
                loginKbUrl = kbUrl;
                loginKbUrl = loginKbUrl.replaceAll("/services", "/login");
            }
        }

        if (loginKbUrl == null || username == null || password == null || loginKbUrl.isEmpty() || username.isEmpty()
                || password.isEmpty()) {

            return false;
        }

        B3PCredentials cred = new B3PCredentials();
        cred.setUserName(username);
        cred.setPassword(password);
        cred.setUrl(loginKbUrl);
        cred.setPreemptive(true);

        HttpClientConfigured hcc = new HttpClientConfigured(cred);
        log.debug("method url: " + loginKbUrl);
        HttpGet request = new HttpGet(loginKbUrl);
        request.addHeader("X-Forwarded-For", ip);

        HttpResponse response = null;
        try {
            response = hcc.execute(request);
            int statusCode = response.getStatusLine().getStatusCode();
            log.debug("Status code: " + statusCode);
            if (statusCode != 200) {
                return false;
            }
        } catch (IOException ex) {
            log.debug("Exception False: ", ex);
            return false;
        } finally {
            hcc.close(response);
            hcc.close();
        }

        return true;
    }

    private static boolean isExpired(Date expireDate) {
        Date now = new Date();

        if (expireDate != null && expireDate.before(now)) {
            return true;
        }

        return false;
    }

    public Principal authenticate(String username, String password) {
        return authenticate(username, password, null);
    }

    public static Principal authenticate(String username, String password, String code) {

        // Eventueel fake Principal aanmaken
        if (!HibernateUtil.isCheckLoginKaartenbalie()) {
            return authenticateFake(username);
        }
        String url = createInternalCapabilitiesURL(code);
        return authenticateHttp(url, username, password, code, null);
    }

    public static Principal authenticate(String username, String password, String code,
            SecurityRequestWrapper request) {

        // Eventueel fake Principal aanmaken
        if (!HibernateUtil.isCheckLoginKaartenbalie()) {
            return authenticateFake(username);
        }
        String url = createInternalCapabilitiesURL(code);
        return authenticateHttp(url, username, password, code, request);
    }

    public Principal getAuthenticatedPrincipal(String username) {
        return null;
    }

    public static synchronized boolean isInSPCache(String userName) {
        if (Bron.isCacheExpired()) {
            flushSPCache();

            return false;
        }

        if (perUserNameSPCache.containsKey(userName)) {
            return true;
        }

        return false;
    }

    public static synchronized void putInSPCache(String userName, ServiceProvider sp) {
        perUserNameSPCache.put(userName, sp);
    }

    public static synchronized ServiceProvider getFromSPCache(String userName) {

        return (ServiceProvider) perUserNameSPCache.get(userName);
    }

    private static boolean isCachedOnDisk(String key) {
        if (key == null || key.isEmpty()) {
            return false;
        }

        String cacheOnDiskPath = HibernateUtil.cacheOnDiskPath;

        String fileName = cacheOnDiskPath + key + ".xml";

        File f = new File(fileName);
        if (f.exists() && !f.isDirectory()) {
            return true;
        }

        return false;
    }

    private static boolean isCachePathValid() {
        String cacheOnDiskPath = HibernateUtil.cacheOnDiskPath;
        File f = new File(cacheOnDiskPath);
        if (f.isDirectory()) {
            return true;
        }
        return false;
    }

    private static void writeCacheToDisk(String key, ServiceProvider sp) {
        if (key == null || sp == null || key.isEmpty() || !isCachePathValid()) {
            return;
        }

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = null;

        try {
            docBuilder = docFactory.newDocumentBuilder();
        } catch (ParserConfigurationException ex) {
        }

        Document doc = docBuilder.newDocument();
        Element rootElement = doc.createElement("WMT_MS_Capabilities");
        doc.appendChild(rootElement);

        Element spElem = sp.toElement(doc, rootElement);

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = null;

        try {
            transformer = transformerFactory.newTransformer();
        } catch (TransformerConfigurationException ex) {
        }

        String cacheOnDiskPath = HibernateUtil.cacheOnDiskPath;
        String fileName = cacheOnDiskPath + key + ".xml";

        DOMSource source = new DOMSource(doc);

        FileOutputStream fop = null;
        File file;
        try {
            file = new File(fileName);
            fop = new FileOutputStream(file);
            // if file doesnt exists, then create it
            if (!file.exists()) {
                file.createNewFile();
            }

            StreamResult result = new StreamResult(fop);

            transformer.transform(source, result);

        } catch (Exception ex) {
            log.error("Error writing cache to disk: ", ex);
        } finally {
            try {
                if (fop != null) {
                    fop.close();
                }
            } catch (IOException e) {
                log.error("Error closing stream for cache to disk: ", e);
            }
        }
    }

    private static ServiceProvider readCacheFromDisk(String key) {
        if (key == null || key.isEmpty()) {
            return null;
        }

        String cacheOnDiskPath = HibernateUtil.cacheOnDiskPath;
        String fileName = cacheOnDiskPath + key + ".xml";
        File file = new File(fileName);

        ByteArrayInputStream in = null;
        try {
            in = new ByteArrayInputStream(FileUtils.readFileToByteArray(file));
        } catch (IOException ex) {
        }

        WMSCapabilitiesReader wmsReader = new WMSCapabilitiesReader();
        ServiceProvider serviceProvider = wmsReader.getProvider(in);

        return serviceProvider;
    }

    public static synchronized void flushSPCache() {
        boolean cacheOnDisk = HibernateUtil.cacheOnDisk;

        if (cacheOnDisk) {
            try {
                String dir = HibernateUtil.cacheOnDiskPath;
                File directory = new File(dir);

                FileUtils.cleanDirectory(directory);
            } catch (IOException ex) {
                log.error("Fout tijdens verwijderen disk cache.", ex);
            }

            log.debug("Cache on DISK WMS leeggemaakt.");
        } else {
            perUserNameSPCache.clear();
            //perUserNameSPCache = null;
            //perUserNameSPCache = new HashMap();

            log.debug("Cache in MEMORY WMS leeggemaakt.");
        }
    }
}