ddf.security.pep.interceptor.PEPAuthorizingInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for ddf.security.pep.interceptor.PEPAuthorizingInterceptor.java

Source

/**
 * Copyright (c) Codice Foundation
 * <p/>
 * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or any later version.
 * <p/>
 * 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
 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License
 * is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package ddf.security.pep.interceptor;

import javax.xml.namespace.QName;
import javax.xml.ws.handler.MessageContext;

import org.apache.commons.lang.StringUtils;
import org.apache.cxf.binding.soap.model.SoapOperationInfo;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.security.AccessDeniedException;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.service.model.MessageInfo;
import org.apache.cxf.ws.addressing.JAXWSAConstants;
import org.apache.cxf.ws.addressing.Names;
import org.apache.shiro.util.ThreadContext;
import org.codice.ddf.platform.util.XMLUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

import ddf.security.SecurityConstants;
import ddf.security.Subject;
import ddf.security.assertion.SecurityAssertion;
import ddf.security.common.audit.SecurityLogger;
import ddf.security.permission.CollectionPermission;
import ddf.security.permission.KeyValueCollectionPermission;
import ddf.security.service.SecurityManager;
import ddf.security.service.SecurityServiceException;
import ddf.security.service.impl.SecurityAssertionStore;

/**
 * Interceptor used to perform service authentication.
 */
public class PEPAuthorizingInterceptor extends AbstractPhaseInterceptor<Message> {

    private Logger logger = LoggerFactory.getLogger(PEPAuthorizingInterceptor.class);

    private SecurityManager securityManager;

    public PEPAuthorizingInterceptor() {
        super(Phase.PRE_INVOKE);
        addAfter(org.apache.cxf.ws.policy.PolicyVerificationInInterceptor.class.getName());
    }

    /**
     * Sets the security manager that will be used to create a subject.
     *
     * @param securityManager
     */
    public void setSecurityManager(SecurityManager securityManager) {
        logger.trace("Setting the security manager");
        this.securityManager = securityManager;
    }

    /**
     * Intercepts a message. Interceptors should NOT invoke handleMessage or handleFault on the next
     * interceptor - the interceptor chain will take care of this.
     *
     * @param message
     */
    @Override
    public void handleMessage(Message message) throws Fault {
        if (message != null) {
            // grab the SAML assertion associated with this Message from the
            // token store
            SecurityAssertion assertion = SecurityAssertionStore.getSecurityAssertion(message);
            boolean isPermitted = false;

            if ((assertion != null) && (assertion.getSecurityToken() != null)) {
                Subject user = null;
                CollectionPermission action = null;

                String actionURI = getActionUri(message);

                try {
                    user = securityManager.getSubject(assertion.getSecurityToken());
                    if (user == null) {
                        throw new AccessDeniedException("Unauthorized");
                    }
                    if (logger.isTraceEnabled()) {
                        logger.trace(format(assertion.getSecurityToken().getToken()));
                    }

                    logger.debug("Is user authenticated: {}", user.isAuthenticated());

                    logger.debug("Checking for permission");
                    SecurityLogger.audit("Is Subject authenticated? " + user.isAuthenticated(), user);

                    if (StringUtils.isEmpty(actionURI)) {
                        SecurityLogger.audit("Denying access to Subject for unknown action.", user);
                        throw new AccessDeniedException("Unauthorized");
                    }

                    action = new KeyValueCollectionPermission(actionURI);
                    logger.debug("Permission: {}", action);

                    isPermitted = user.isPermitted(action);

                    logger.debug("Result of permission: {}", isPermitted);
                    SecurityLogger.audit("Is Subject  permitted? " + isPermitted, user);
                    // store the subject so the DDF framework can use it later
                    ThreadContext.bind(user);
                    message.put(SecurityConstants.SAML_ASSERTION, user);
                    logger.debug("Added assertion information to message at key {}",
                            SecurityConstants.SAML_ASSERTION);
                } catch (SecurityServiceException e) {
                    SecurityLogger.audit(
                            "Denying access : Caught exception when trying to authenticate user for service ["
                                    + actionURI + "]",
                            e);
                    throw new AccessDeniedException("Unauthorized");
                }
                if (!isPermitted) {
                    SecurityLogger.audit("Denying access to Subject for service: " + action.getAction(), user);
                    throw new AccessDeniedException("Unauthorized");
                }
            } else {
                SecurityLogger
                        .audit("Unable to retrieve the security assertion associated with the web service call.");
                throw new AccessDeniedException("Unauthorized");
            }
        } else {
            SecurityLogger.audit("Unable to retrieve the current message associated with the web service call.");
            throw new AccessDeniedException("Unauthorized");
        }
    }

    /**
     * This method is an implementation of the WSA-M and WSA-W specs for determining the action URI.<br>
     * <ul>
     * <li>http://www.w3.org/TR/ws-addr-metadata/#actioninwsdl</li>
     * <li>http://www.w3.org/TR/ws-addr-wsdl/#actioninwsdl</li>
     * </ul>
     * Adapted from {@link org.apache.cxf.ws.addressing.impl.MAPAggregatorImpl} and
     * {@link org.apache.cxf.ws.addressing.impl.InternalContextUtils}
     *
     * @param message
     * @return
     */
    private String getActionUri(Message message) {
        String actionURI = null;

        /**
         * See if the action is explicitly defined in the WSDL message service model. Retrieves one
         * of the Action attribute in the wsdl:input message.
         */
        MessageInfo msgInfo = (MessageInfo) message.get(MessageInfo.class.getName());
        if (msgInfo != null && msgInfo.getExtensionAttributes() != null) {
            // wsaw:Action
            Object attr = msgInfo.getExtensionAttribute(JAXWSAConstants.WSAW_ACTION_QNAME);
            // wsam:Action
            if (attr == null) {
                attr = msgInfo.getExtensionAttribute(JAXWSAConstants.WSAM_ACTION_QNAME);
            }
            // support for older usages
            if (attr == null) {
                attr = msgInfo.getExtensionAttributes()
                        .get(new QName(JAXWSAConstants.NS_WSA, Names.WSAW_ACTION_NAME));
            }
            if (attr == null) {
                attr = msgInfo.getExtensionAttributes()
                        .get(new QName(Names.WSA_NAMESPACE_WSDL_NAME_OLD, Names.WSAW_ACTION_NAME));
            }
            if (attr instanceof QName) {
                actionURI = ((QName) attr).getLocalPart();
            } else {
                actionURI = attr == null ? null : attr.toString();
            }
        }

        /**
         * See if the action is explicitly defined in the WSDL operation service model. Retrieves
         * the operation soap:soapAction property.
         */
        if (StringUtils.isEmpty(actionURI)) {
            BindingOperationInfo bindingOpInfo = message.getExchange().get(BindingOperationInfo.class);
            SoapOperationInfo soi = bindingOpInfo.getExtensor(SoapOperationInfo.class);
            if (soi == null && bindingOpInfo.isUnwrapped()) {
                soi = bindingOpInfo.getWrappedOperation().getExtensor(SoapOperationInfo.class);
            }
            actionURI = soi == null ? null : soi.getAction();
            actionURI = StringUtils.isEmpty(actionURI) ? null : actionURI;
        }

        /**
         * If the service model doesn't explicitly defines the action, we'll construct the default
         * URI string.
         */
        if (StringUtils.isEmpty(actionURI)) {
            QName op = (QName) message.get(MessageContext.WSDL_OPERATION);
            QName port = (QName) message.get(MessageContext.WSDL_PORT);
            if (op != null && port != null) {
                actionURI = port.getNamespaceURI();
                actionURI = addPath(actionURI, port.getLocalPart());
                actionURI = addPath(actionURI, op.getLocalPart() + "Request");
            }
        }

        return actionURI;
    }

    private String addPath(String uri, String path) {
        StringBuilder builder = new StringBuilder(uri);
        String delimiter = uri.startsWith("urn") ? ":" : "/";
        if (!uri.endsWith(delimiter) && !path.startsWith(delimiter)) {
            builder.append(delimiter);
        }
        builder.append(path);
        return builder.toString();
    }

    private String format(Element unformattedXml) {
        if (unformattedXml == null) {
            logger.error("Unable to transform xml: null");
            return null;
        }

        return XMLUtils.prettyFormat(unformattedXml);
    }
}