org.jcvi.ometa.hibernate.dao.SecurityDAO.java Source code

Java tutorial

Introduction

Here is the source code for org.jcvi.ometa.hibernate.dao.SecurityDAO.java

Source

/*
 * Copyright J. Craig Venter Institute, 2013
 *
 * The creation of this program was supported by J. Craig Venter Institute
 * and National Institute for Allergy and Infectious Diseases (NIAID),
 * Contract number HHSN272200900007C.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

package org.jcvi.ometa.hibernate.dao;

import org.apache.log4j.Logger;
import org.hibernate.Hibernate;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.jcvi.ometa.configuration.AccessLevel;
import org.jcvi.ometa.configuration.QueryEntityType;
import org.jcvi.ometa.configuration.ResponseToFailedAuthorization;
import org.jcvi.ometa.model.Project;
import org.jcvi.ometa.stateless_session_bean.ForbiddenResourceException;

import java.util.*;

/**
 * Created by IntelliJ IDEA.
 * User: lfoster
 * Date: 6/2/11
 * Time: 4:16 PM
 *
 * Data access object to handle auth/auth problems.
 */
public class SecurityDAO extends HibernateDAO {
    public static final String UNLOGGED_IN_USER = "guest";

    private static final String PROJ_GRP_SUBST_STR = "{project_group_field}";
    private static final String USERNAME_PARAM = "userName";

    private static final String VIEW_PROJECT_GROUP_FIELD = "P.projet_view_group_id";
    private static final String EDIT_PROJECT_GROUP_FIELD = "P.projet_edit_group_id";

    private static final String RTN_PROJECT_VAR_NAME = "projectReturn";
    private static final String OPEN_PROJECTS_PARAM_LIST_NAME = "openProject";
    private static final String SECURE_PROJECTS_PARAM_LIST_NAME = "securedProject";

    private static final String RTN_SAMPLE_VAR_NAME = "sampleReturn";
    private static final String OPEN_SAMPLES_PARAM_LIST_NAME = "openSample";
    private static final String SECURE_SAMPLES_PARAM_LIST_NAME = "securedSample";

    //-------------------------------PROJECT SECTION
    private static final String SECURED_PROJECTS_SQL_QUERY = "select P.projet_name as " + RTN_PROJECT_VAR_NAME
            + " from actor A, actor_group AG, groups G, project P "
            + "where  A.actor_id=AG.actgrp_actor_id and AG.actgrp_group_id=G.group_id and G.group_id="
            + PROJ_GRP_SUBST_STR + " and A.actor_username=:" + USERNAME_PARAM
            + " and P.projet_name in (:securedProject)";

    private static final String SECURED_PROJECT_IDS_SQL_QUERY = "select P.projet_id as " + RTN_PROJECT_VAR_NAME
            + " from actor A, actor_group AG, groups G, project P "
            + "where  A.actor_id=AG.actgrp_actor_id and AG.actgrp_group_id=G.group_id and G.group_id="
            + PROJ_GRP_SUBST_STR + " and A.actor_username=:" + USERNAME_PARAM
            + " and P.projet_id in (:securedProject)";

    private static final String OPEN_AND_SECURED_PROJECTS_SQL_QUERY = "select P.projet_name as "
            + RTN_PROJECT_VAR_NAME + " from project P where P.projet_is_secure=0 "
            + "and P.projet_name in (:openProject) " + "union " + SECURED_PROJECTS_SQL_QUERY;

    private static final String OPEN_AND_SECURED_PROJECT_IDS_SQL_QUERY = "select P.projet_id as "
            + RTN_PROJECT_VAR_NAME + " from project P where P.projet_is_secure=0 "
            + "and P.projet_id in (:openProject) " + "union " + SECURED_PROJECT_IDS_SQL_QUERY;

    private static final String AUTHORIZED_FOR_USER_SQL_QUERY = "select P.* from project P" + " where "
            + PROJ_GRP_SUBST_STR + " is null" + " or " + PROJ_GRP_SUBST_STR + " in ("
            + "   select actgrp_group_id from actor a, actor_group ag where a.actor_username =:" + USERNAME_PARAM
            + "   and a.actor_id = ag.actgrp_actor_id)" + " order by P.projet_name";

    //-------------------------------SAMPLE SECTION
    private static final String SECURED_SAMPLES_SQL_QUERY = "select S.sample_name as " + RTN_SAMPLE_VAR_NAME
            + " from actor A, actor_group AG, groups G, sample S, " + "project P "
            + "where  A.actor_id=AG.actgrp_actor_id and AG.actgrp_group_id=G.group_id and G.group_id="
            + PROJ_GRP_SUBST_STR + " and A.actor_username=:" + USERNAME_PARAM + " and "
            + "S.sample_projet_id=S.sample_id and " + "S.sample_name in (:securedSample)";

    private static final String SECURED_SAMPLE_IDS_SQL_QUERY = "select S.sample_id as " + RTN_SAMPLE_VAR_NAME
            + " from actor A, actor_group AG, groups G, project P, " + "sample S "
            + "where  A.actor_id=AG.actgrp_actor_id and AG.actgrp_group_id=G.group_id and G.group_id="
            + PROJ_GRP_SUBST_STR + " and A.actor_username=:" + USERNAME_PARAM
            + " and S.sample_projet_id=P.projet_id and S.sample_id in (:securedSample)";

    private static final String OPEN_AND_SECURED_SAMPLES_SQL_QUERY = "select S.sample_name as "
            + RTN_SAMPLE_VAR_NAME + " from project P, sample S where P.projet_is_secure=0 "
            + "and S.sample_projet_id=P.projet_id " + "and S.sample_name in (:openSample) " + "union "
            + SECURED_SAMPLES_SQL_QUERY;

    private static final String OPEN_AND_SECURED_SAMPLE_IDS_SQL_QUERY = "select S.sample_id as "
            + RTN_SAMPLE_VAR_NAME + " from project P, sample S "
            + "where S.sample_projet_id=P.projet_id and P.projet_is_secure=0 " + "and S.sample_id in (:openSample) "
            + "union " + SECURED_SAMPLE_IDS_SQL_QUERY;

    private Logger logger = Logger.getLogger(SecurityDAO.class);

    //-------------------------------------------------NAME SECTION
    /**
     * Helper to enforce authorization by name, to users.
     */
    public List<String> getListOfAuthorizedByName(List<String> names, String username,
            ResponseToFailedAuthorization failureResponse, AccessLevel accessLevel, Session session,
            QueryEntityType queryEntityType) throws Exception {

        if (queryEntityType == QueryEntityType.Project) {
            logger.debug("Getting list of authorized projects for user " + username);
            return getListOfAuthorizedByName(names, username, failureResponse, accessLevel, session,
                    SECURED_PROJECTS_SQL_QUERY, OPEN_AND_SECURED_PROJECTS_SQL_QUERY, RTN_PROJECT_VAR_NAME,
                    SECURE_PROJECTS_PARAM_LIST_NAME, OPEN_PROJECTS_PARAM_LIST_NAME);
        } else {
            logger.debug("Getting list of authorized samples for user " + username);
            return getListOfAuthorizedByName(names, username, failureResponse, accessLevel, session,
                    SECURED_SAMPLES_SQL_QUERY, OPEN_AND_SECURED_SAMPLES_SQL_QUERY, RTN_SAMPLE_VAR_NAME,
                    SECURE_SAMPLES_PARAM_LIST_NAME, OPEN_SAMPLES_PARAM_LIST_NAME);
        }
    }

    //-------------------------------------------------ID SECTION
    /**
     * Helper to enforce authorization of values known by their IDs, to users.
     */
    public List<Long> getListOfAuthorizedById(List<Long> ids, String username,
            ResponseToFailedAuthorization failureResponse, AccessLevel accessLevel, Session session,
            QueryEntityType queryEntityType) throws Exception {

        if (queryEntityType == QueryEntityType.Project) {
            logger.debug("Getting list of authorized projects for user " + username);
            return getListOfAuthorizedById(ids, username, failureResponse, accessLevel, session,
                    SECURED_PROJECT_IDS_SQL_QUERY, OPEN_AND_SECURED_PROJECT_IDS_SQL_QUERY,
                    SECURE_PROJECTS_PARAM_LIST_NAME, OPEN_PROJECTS_PARAM_LIST_NAME, RTN_PROJECT_VAR_NAME);

        } else {
            logger.debug("Getting list of authorized samples for user " + username);
            return getListOfAuthorizedById(ids, username, failureResponse, accessLevel, session,
                    SECURED_SAMPLE_IDS_SQL_QUERY, OPEN_AND_SECURED_SAMPLE_IDS_SQL_QUERY,
                    SECURE_SAMPLES_PARAM_LIST_NAME, OPEN_SAMPLES_PARAM_LIST_NAME, RTN_SAMPLE_VAR_NAME);

        }
    }

    //---------------------------------------------HELPERS

    /**
     * Common code for both project and sample, to get data by list-of-identifiers.
     * @throws Exception if anything requested is left out, iff failureResponse == throw ex.
     */
    private List<Long> getListOfAuthorizedById(List<Long> ids, String username,
            ResponseToFailedAuthorization failureResponse, AccessLevel accessLevel, Session session,
            String securedIdsQuery, String openAndSecuredIdsQuery, String securedParamListName,
            String openParamListName, String returnVarName) throws Exception {

        ids = uniquifyIds(ids);

        String queryStr = null;
        if (accessLevel == AccessLevel.View) {
            queryStr = openAndSecuredIdsQuery.replace(PROJ_GRP_SUBST_STR, VIEW_PROJECT_GROUP_FIELD);
        } else {
            queryStr = securedIdsQuery.replace(PROJ_GRP_SUBST_STR, EDIT_PROJECT_GROUP_FIELD);
        }
        SQLQuery query = session.createSQLQuery(queryStr);
        query.addScalar(returnVarName, Hibernate.STRING);
        if (accessLevel == AccessLevel.View) {
            query.setParameterList(openParamListName, ids);
        }
        query.setParameterList(securedParamListName, ids);
        String queryUsername = username == null ? UNLOGGED_IN_USER : username;
        query.setParameter(USERNAME_PARAM, queryUsername);

        logger.debug(query.getQueryString());
        List<Long> rtnVal = query.list();
        if (failureResponse == ResponseToFailedAuthorization.ThrowException && rtnVal.size() < ids.size()) {
            String idStr = joinIdList(ids);
            String message = makeUserReadableMessage(username, idStr);
            logger.error(message);
            throw new ForbiddenResourceException(message);
        }

        return rtnVal;
    }

    /**
     * Common code for both project and sample, to get data by list-of-names.
     * @throws Exception if anything requested is left out, iff failureResponse == throw ex.
     */
    private List<String> getListOfAuthorizedByName(List<String> names, String username,
            ResponseToFailedAuthorization failureResponse, AccessLevel accessLevel, Session session,
            String securedQuery, String openAndSecuredQuery, String returnVarName, String securedParamListName,
            String openParamListName) throws Exception {

        // Need to avoid sending same name multiple times.
        names = uniquifyNames(names);

        String queryStr = null;
        if (accessLevel == AccessLevel.View) {
            queryStr = openAndSecuredQuery.replace(PROJ_GRP_SUBST_STR, VIEW_PROJECT_GROUP_FIELD);
        } else {
            queryStr = securedQuery.replace(PROJ_GRP_SUBST_STR, EDIT_PROJECT_GROUP_FIELD);
        }
        SQLQuery query = session.createSQLQuery(queryStr);
        query.addScalar(returnVarName, Hibernate.STRING);

        query.setParameterList(securedParamListName, names);
        if (accessLevel == AccessLevel.View) {
            query.setParameterList(openParamListName, names);
        }
        String queryUsername = username == null ? UNLOGGED_IN_USER : username;
        query.setParameter(USERNAME_PARAM, queryUsername);

        List<String> rtnVal = query.list();
        if (failureResponse == ResponseToFailedAuthorization.ThrowException && rtnVal.size() < names.size()) {

            String nameStr = joinNameList(names);
            String message = makeUserReadableMessage(username, nameStr);
            logger.error(message);
            throw new ForbiddenResourceException(message);
        }

        return rtnVal;
    }

    //-------------------------------------------------PROJECT SECTION
    /**
     * Helper to enforce authorization by name, to users.
     */
    public List<Project> getListOfAuthorizedProjects(String username, AccessLevel accessLevel, Session session)
            throws Exception {

        String queryStr = AUTHORIZED_FOR_USER_SQL_QUERY;

        if (accessLevel == AccessLevel.View) {
            queryStr = queryStr.replace(PROJ_GRP_SUBST_STR, VIEW_PROJECT_GROUP_FIELD);
        } else {
            queryStr = queryStr.replace(PROJ_GRP_SUBST_STR, EDIT_PROJECT_GROUP_FIELD);
        }

        SQLQuery query = session.createSQLQuery(queryStr);
        String queryUsername = username == null ? UNLOGGED_IN_USER : username;
        query.setParameter(USERNAME_PARAM, queryUsername);
        query.addEntity("P", Project.class);
        List<Project> rtnVal = query.list();
        return rtnVal;

    }

    /** Messages generated here can wind up before the user.  Take care to make them readable! */
    private String makeUserReadableMessage(String username, String projectStr) {
        //return "User " + username + " requested projects '" + projectStr + "' but may not access one or more of them.";
        return "You do not have permission to view or edit the project.";
    }

    private List<String> uniquifyNames(List<String> names) {
        Set<String> uniqueNames = new HashSet<String>();
        uniqueNames.addAll(names);
        List<String> rtnList = new ArrayList<String>();
        rtnList.addAll(uniqueNames);
        return rtnList;
    }

    private List<Long> uniquifyIds(List<Long> names) {
        Set<Long> uniqueNames = new HashSet<Long>();
        uniqueNames.addAll(names);
        List<Long> rtnList = new ArrayList<Long>();
        rtnList.addAll(uniqueNames);
        return rtnList;
    }

    /** Roll a list of strings into a comma-separated single string. */
    private String joinNameList(List<String> names) {
        StringBuilder bldr = new StringBuilder();

        if (names != null) {
            for (String nextName : uniquifyNames(names)) {
                if (bldr.length() > 0) {
                    bldr.append("','");
                }
                bldr.append(nextName);
            }
        } else {
            throw new IllegalArgumentException("Null name list not allowed.");
        }

        return bldr.toString();
    }

    /** Roll a list of numeric IDs into a comma-separated single string. */
    private String joinIdList(List<Long> projects) {
        StringBuilder bldr = new StringBuilder();
        if (projects != null) {
            for (Long nextProject : projects) {
                if (bldr.length() > 0) {
                    bldr.append(",");
                }
                bldr.append(nextProject);
            }
        } else {
            throw new IllegalArgumentException("Null id list not allowed.");
        }

        return bldr.toString();
    }

}