com.novartis.opensource.yada.plugin.AbstractPreprocessor.java Source code

Java tutorial

Introduction

Here is the source code for com.novartis.opensource.yada.plugin.AbstractPreprocessor.java

Source

/**
 * Copyright 2016 Novartis Institutes for BioMedical Research Inc.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * 
 */
package com.novartis.opensource.yada.plugin;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.Cookie;

import org.json.JSONArray;

import com.novartis.opensource.yada.ConnectionFactory;
import com.novartis.opensource.yada.YADAConnectionException;
import com.novartis.opensource.yada.YADAQuery;
import com.novartis.opensource.yada.YADARequest;
import com.novartis.opensource.yada.YADASQLException;
import com.novartis.opensource.yada.util.YADAUtils;

/**
 * @author David Varon
 * @since 0.4.2
 */
public abstract class AbstractPreprocessor
        implements Preprocess, Validation, TokenValidator, ExecutionPolicy, ContentPolicy {
    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String AUTH_PATH_RX = "auth.path.rx";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String AUTH_TOKEN = "auth.token";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String TOKEN_VALIDATOR = "token.validator";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String CONTENT_POLICY = "content.policy";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String EXECUTION_POLICY = "execution.policy";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String EXECUTION_POLICY_INTERFACE = "ExecutionPolicy";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String CONTENT_POLICY_INTERFACE = "ContentPolicy";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    protected static final String VOID = "void";

    /**
     * Constant equal to {@value}.
     * The query executed to evaluate authorization.
     */
    protected static final String YADA_A11N_QUERY = "SELECT DISTINCT a.target, a.policy, a.type, a.qname "
            + "FROM YADA_A11N a " // join YADA_QUERY b on  (a.target = b.qname OR a.target = b.app) "
            + "WHERE a.target = ?";

    /**
     * Constant equal to {@value}
     * @since 7.0.0
     */
    private static final String PLUGIN_PKG = "com.novartis.opensource.yada.plugin.";

    /**
     * The {@link YADARequest} object processed by the plugin 
     */
    private YADARequest yadaReq;

    /**
     * The {@link YADAQuery} object processed by the plugin
     */
    private YADAQuery yq;

    /**
     * The authentication token, e.g., user id
     * @since 7.0.0
     */
    private Object token = null;

    /**
     * The {@link TokenValidator}
     * @since 7.0.0 
     */
    private TokenValidator tokenValidator;

    /**
     * The {@link SecurityPolicy}
     * @since 7.0.0 
     */
    private SecurityPolicy securityPolicy;

    /**
     * The {@code args} {@link List} from {@link YADARequest#getArgs}
     * @since 7.0.0
     */
    private List<String> args;

    /**
     * The {@code preArgs} {@link List} from {@link YADARequest#getPreargs}
     * @since 7.0.0
     * @deprecated as of 7.1.0
     */
    @Deprecated
    private List<String> preargs;

    /**
     * Base implementation, calls {@link #setYADARequest(YADARequest)}, 
     * {@link #setArgs(List)}, {@link #setPreargs(List)}
     * and returns {@link #getYADARequest()}
     * @see com.novartis.opensource.yada.plugin.Preprocess#engage(com.novartis.opensource.yada.YADARequest)
     */
    @Override
    public YADARequest engage(YADARequest yadaReq) throws YADAPluginException {
        setYADARequest(yadaReq);
        String[] args = getYADAQuery().getYADAQueryParamValue(YADARequest.PS_ARGLIST);
        if (args != null && args.length > 0)
            setArgs(Arrays.asList(args));
        else {
            args = getYADAQuery().getYADAQueryParamValue(YADARequest.PS_PREARGS);
            if (args != null && args.length > 0)
                setPreargs(Arrays.asList(args));
        }

        return getYADARequest();
    }

    /**
     * Base implementation, calls {@link #setYADARequest(YADARequest)},
     *  {@link #setArgs(List)}, {@link #setPreargs(List)} using {@link YADAQuery#getYADAQueryParamValue(String)}
     *  for the last two.
     * @see com.novartis.opensource.yada.plugin.Preprocess#engage(com.novartis.opensource.yada.YADARequest, com.novartis.opensource.yada.YADAQuery)
     */
    @Override
    public void engage(YADARequest yadaReq, YADAQuery yq) throws YADAPluginException, YADASecurityException {
        setYADARequest(yadaReq);
        setYADAQuery(yq);
        String[] args = getYADAQuery().getYADAQueryParamValue(YADARequest.PS_ARGLIST);
        if (args != null && args.length > 0)
            setArgs(Arrays.asList(args[0].split(",")));
        else {
            args = getYADAQuery().getYADAQueryParamValue(YADARequest.PS_ARGS);
            if (args != null && args.length > 0)
                setArgs(Arrays.asList(args[0].split(",")));
            else {
                args = getYADAQuery().getYADAQueryParamValue(YADARequest.PS_PREARGS);
                if (args != null && args.length > 0)
                    setPreargs(Arrays.asList(args[0].split(",")));
            }
        }
    }

    /**
     * Convenience method with calls {@link #validateURL()}, {@link #validateToken()}, {@link #applyExecutionPolicy()}, and {@link #applyContentPolicy()}
     * @since 7.0.0
     */
    @Override
    public void validateYADARequest() throws YADASecurityException {
        // default impl here looks for arg/prearg
        validateURL();

        // will use argument to inject, if present, or return gracefully 
        setTokenValidator();
        // default impl does nothing
        validateToken();

        // will use argument to inject, if present, or use current class,
        // if interface is implemented, or return gracefully
        setSecurityPolicy(EXECUTION_POLICY);
        SecurityPolicy policy = getSecurityPolicy();
        if (policy != null)
            ((ExecutionPolicy) policy).applyExecutionPolicy();

        // will use argument to inject, if present, or use current class,
        // if interface is implemented, or return gracefully
        setSecurityPolicy(CONTENT_POLICY);
        policy = getSecurityPolicy();
        if (policy != null)
            ((ContentPolicy) policy).applyContentPolicy();

    }

    /**
      * Throws an exception if the URL returned by {@code req.getRequestURL()} is unauthenticated.
      * @throws YADAPluginException if a non-authenticated URL is requested
      * @since 7.0.0
      */
    @Override
    public void validateURL() throws YADASecurityException {
        String pathRx = getArgumentValue(AUTH_PATH_RX);
        if (pathRx != null && pathRx.length() > 0) {
            String reqUrl = getYADARequest().getRequest().getRequestURL().toString();
            if (!reqUrl.matches("^(http://)*" + pathRx + "$")) {
                String msg = "Unauthorized.  This query requires use of an authenticated address.";
                throw new YADASecurityException(msg);
            }
        }
    }

    /**
    * Default implementation calls {@link TokenValidator#validate()} via injection
    * @since 7.0.0
    */
    @Override
    public void validateToken() throws YADASecurityException {
        // nothing to do
    }

    /**
     * Returns the value of {@code key} from {@link #args} 
     * or {@link #preargs} or from {@link System#getProperty(String)}
     * @param key
     * @return the value mapped to {@code key} or an empty {@link String}
     * @since 7.0.0
     */
    protected String getArgumentValue(String key) {
        String val = "";

        if (this.args == null || this.args.size() == 0) {
            if (this.preargs == null || this.preargs.size() == 0) {
                val = System.getProperty(key);
            } else {
                val = getListEntry(this.preargs, key);
            }
        } else {
            val = getListEntry(this.args, key);
        }
        return val;
    }

    /**
    * Retrieves the value part of an argument passed as a name=value pair
    * 
    * @param list the argument parameter list to review
    * @param key the name in the argument name=value pair to retrieve
    * @return the value mapped to {@code key} or {@code null}
    * @since 7.0.0
    */
    private String getListEntry(List<String> list, String key) {
        for (int i = 0; i < list.size(); i++) {
            Pattern rxKeyVal = Pattern.compile("^" + key + "=(.+)$");
            Matcher m = rxKeyVal.matcher(list.get(i));
            if (m.matches()) {
                return m.group(1);
            }
        }
        return null;
    }

    /**
     * Standard mutator for variable
     * @param yadaReq the currently executing {@link YADARequest}
     */
    public void setYADARequest(YADARequest yadaReq) {
        this.yadaReq = yadaReq;
    }

    /**
     * Standard accessor for variable
     * @return the {@link YADARequest} being executed
     */
    public YADARequest getYADARequest() {
        return this.yadaReq;
    }

    /**
     * Standard mutator for variable
     * @param yq the {@link YADAQuery} to which this preprocessor is attached
     */
    public void setYADAQuery(YADAQuery yq) {
        this.yq = yq;
    }

    /**
     * Standard accessor for variable
     * @return the {@link YADAQuery} to which this preprocessor is attached
     */
    public YADAQuery getYADAQuery() {
        return this.yq;
    }

    /**
     * Default implementation intended for override
     * @since 7.0.0
     */
    @Override
    public void setToken() {
        // nothing to do
    }

    /**
     * Standard mutator for variable
     * @param token 
     * @since 7.0.0
     */
    @Override
    public void setToken(Object token) {
        this.token = token;
    }

    /**
     * Standard accessor for variable
     * @return the value of the validated {@code NIBR521} header
     * @throws YADASecurityException when token retrieval fails
     * @since 7.0.0
     */
    @Override
    public Object getToken() throws YADASecurityException {
        return this.token;
    }

    @Override
    public String getHeader(String header) {
        return getYADARequest().getRequest().getHeader(header);
    }

    @Override
    public Object getSessionAttribute(String attribute) {
        return getYADARequest().getRequest().getSession().getAttribute(attribute);
    }

    /**
     * Convenience method to get the user id value for security policy application
     * @return a json string containing the {@code UID} value stored in the {@code YADA.user.privs} session attribute
     */
    public String getLoggedUser() {
        return ((JSONArray) getSessionAttribute("YADA.user.privs")).getJSONObject(0).getString("UID");
    }

    @Override
    public String getValue(String key) {
        return getYADAQuery().getDataRow(0).get(key)[0];
    }

    @Override
    public String getValue(int index) {
        return getYADAQuery().getDataRow(0).get("YADA_" + index)[0];
    }

    @Override
    public String getCookie(String cookie) {
        Cookie[] cookies = getYADARequest().getRequest().getCookies();
        if (cookies != null) {
            for (Cookie c : cookies) {
                if (c.getName().equals(cookie)) {
                    return c.getValue();
                }
            }
        }
        return null;
    }

    /**
     * No arg mutator for variable, gets FQCN from args or properties 
     * @throws YADASecurityException 
     * @since 7.0.0
     */
    public void setTokenValidator() throws YADASecurityException {
        if (getTokenValidator() == null) {
            Class<?> clazz = null;
            String name = "";
            try {
                name = getArgumentValue(TOKEN_VALIDATOR);
                if (name.length() == 0)
                    return;
                clazz = Class.forName(name);
            } catch (ClassNotFoundException e) {
                try {
                    name = PLUGIN_PKG + getArgumentValue(TOKEN_VALIDATOR);
                    clazz = Class.forName(name);
                } catch (ClassNotFoundException e1) {
                    String msg = "Could not find the specified TokenValidator class: " + name + ".";
                    throw new YADASecurityException(msg, e1);
                }
            }

            try {
                setTokenValidator((TokenValidator) clazz.newInstance());
            } catch (InstantiationException | IllegalAccessException e) {
                String msg = "Could not instantiate the specified TokenValidator class: " + name;
                throw new YADASecurityException(msg, e);
            }
        }
    }

    /**
     * Standard mutator for variable
     * @param tokenValidator the {@link TokenValidator} instance
     * @since 7.0.0
     */
    public void setTokenValidator(TokenValidator tokenValidator) {
        this.tokenValidator = tokenValidator;
    }

    /**
     * Standard accessor for variable
     * @return the {@link TokenValidotor} instance
     * @since 7.0.0
     */
    public TokenValidator getTokenValidator() {
        return this.tokenValidator;
    }

    /**
     * Default implementation of {@link SecurityPolicy#applyPolicy()}, intended for override
     * @throws YADASecurityException
     * @since 7.0.0
     */
    @Override
    public void applyPolicy() throws YADASecurityException {
        // nothing to do
    }

    /**
     * Default implementation of {@link SecurityPolicy#applyPolicy(SecurityPolicy)}, intended for override
     * @throws YADASecurityException
     * @since 7.0.0
     */
    @Override
    public void applyPolicy(SecurityPolicy securityPolicy) throws YADASecurityException {
        // nothing to do
    }

    /**
     * Default implementation of does nothing, intended for override
     * @throws YADASecurityException
     * @since 7.0.0
     */
    @Override
    public void applyExecutionPolicy() throws YADASecurityException {
        // nothing to do
    }

    /**
     * Default implementation of does nothing, intended for override
     * @throws YADASecurityException
     * @since 7.0.0
     */
    @Override
    public void applyContentPolicy() throws YADASecurityException {
        // nothing to do
    }

    /**
     * @return the args
     * @since 7.0.0
     */
    public List<String> getArgs() {
        return this.args;
    }

    /**
     * @param args the args to set
     * @since 7.0.0
     */
    public void setArgs(List<String> args) {
        this.args = args;
    }

    /**
     * @return the preargs
     * @since 7.0.0
     */
    public List<String> getPreArgs() {
        return this.preargs;
    }

    /**
     * @param preargs the preargs to set
     * @since 7.0.0
     * @deprecated as of 7.1.0
     */
    @Deprecated
    public void setPreargs(List<String> preargs) {
        this.preargs = preargs;
    }

    /**
     * @return the securityPolicy
     */
    public SecurityPolicy getSecurityPolicy() {
        return this.securityPolicy;
    }

    /**
     * @param securityPolicy the securityPolicy to set
     */
    public void setSecurityPolicy(SecurityPolicy securityPolicy) {
        this.securityPolicy = securityPolicy;
    }

    /**
     * No arg mutator for variable, gets FQCN from args or properties 
     * @param policyType either {@link #CONTENT_POLICY} or {@link #EXECUTION_POLICY}
     * @throws YADASecurityException 
     * @since 7.0.0
     */
    public void setSecurityPolicy(String policyType) throws YADASecurityException {
        if (getSecurityPolicy() == null) {
            Class<?> clazz = null;
            String name = "";
            try {
                // this is where the default query param implementation meets execution.
                // the value of the 'execution.policy' or 'content.policy' argument is the 
                // implementing class name
                name = getArgumentValue(policyType);
                if (name != null && name.equals(VOID)) {
                    return;
                } else if (name == null || name.length() == 0) {
                    try {
                        String iface = policyType.equals(EXECUTION_POLICY) ? EXECUTION_POLICY_INTERFACE
                                : CONTENT_POLICY_INTERFACE;
                        Class<?> plugin = Class.forName(PLUGIN_PKG + iface);
                        if (plugin.isAssignableFrom(getClass()))
                            setSecurityPolicy(this);
                    } catch (ClassNotFoundException e) {
                        return;
                    }
                    return;
                }
                clazz = Class.forName(name);
            } catch (ClassNotFoundException e) {
                try {
                    name = PLUGIN_PKG + getArgumentValue(policyType);
                    clazz = Class.forName(name);
                } catch (ClassNotFoundException e1) {
                    String msg = "Could not find the specified SecurityPolicy class: " + name + ".";
                    throw new YADASecurityException(msg, e1);
                }
            }

            try {
                setSecurityPolicy((SecurityPolicy) clazz.newInstance());
            } catch (InstantiationException | IllegalAccessException e) {
                String msg = "Could not instantiate the specified SecurityPolicy class: " + name;
                throw new YADASecurityException(msg, e);
            }
        }
    }

    /**
     * Returns {@code true} if the security target is 
     * associated to a {@link #BLACKLIST} policy type in the 
     * {@code YADA_A11N} table
     * 
     * @param type
     *          the value of the {@code TYPE} field in the
     *          {@code YADA_A11N} table
     * @return {@code true} if {@code TYPE} is {@link #BLACKLIST}
     */
    @Override
    public boolean isBlacklist(String type) {
        return BLACKLIST.equals(type);
    }

    /**
     * Returns {@code true} if the security target is 
     * associated to a {@link #WHITELIST} policy type in the 
     * {@code YADA_A11N} table
     * 
     * @param type
     *          the value of the {@code TYPE} field in the
     *          {@code YADA_A11N} table
     * @return {@code true} if {@code TYPE} is {@link #WHITELIST}
     */
    @Override
    public boolean isWhitelist(String type) {
        return WHITELIST.equals(type);
    }

    /**
     * Retrieves the row from {@code YADA_A11N} for the desired query.
     * @param qname the request query
     * @return a {@link HashMap} contaning the security config for the {@code qname}
     * @throws YADASecurityException when the security query can't be retrieved
     */
    @Override
    public List<SecurityPolicyRecord> getSecurityPolicyRecords(String securityPolicyCode)
            throws YADASecurityException {
        List<SecurityPolicyRecord> policy = new ArrayList<>();

        // get the security params associated to the query
        String qname = getYADAQuery().getQname();
        try (ResultSet rs = YADAUtils.executePreparedStatement(YADA_A11N_QUERY, new Object[] { qname });) {
            while (rs.next()) {
                String tgt = rs.getString(1); // YADA_A11N.TARGET  (this is in the query paramaters, no need to pass)
                String policyCode = rs.getString(2); // YADA_A11N.POLICY  (this is in the method parameters, no need to pass`)
                String type = rs.getString(3); // YADA_A11N.TYPE
                String a11nQname = rs.getString(4); // YADA_A11N.QNAME (a query name)

                if (qname.equals(tgt) && policyCode.equals(securityPolicyCode)) {
                    policy.add(new SecurityPolicyRecord(tgt, policyCode, type, a11nQname));
                }
            }
            ConnectionFactory.releaseResources(rs);
        } catch (SQLException | YADAConnectionException | YADASQLException e) {
            String msg = "Unauthorized. Could not obtain security query. This could be a temporary problem.";
            throw new YADASecurityException(msg, e);
        }

        if (policy.size() == 0) {
            String msg = "Unauthorized. A security check was configured by has no policy associated to it.";
            throw new YADASecurityException(msg);
        }

        return policy;
    }

    /**
     * Sets the current security policy to {@code null}.
     * @since 7.0.0
     */
    public void clearSecurityPolicy() {
        this.securityPolicy = null;
    }
}