Package org.apache.geronimo.jetty

Source Code of org.apache.geronimo.jetty.JettyWebAppJACCContext

/**
*
* Copyright 2003-2004 The Apache Software Foundation
*
*  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 org.apache.geronimo.jetty;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.security.AccessControlContext;
import java.security.AccessControlException;
import java.security.Principal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.jacc.PolicyConfiguration;
import javax.security.jacc.PolicyConfigurationFactory;
import javax.security.jacc.PolicyContext;
import javax.security.jacc.PolicyContextException;
import javax.security.jacc.WebResourcePermission;
import javax.security.jacc.WebUserDataPermission;
import javax.management.ObjectName;
import javax.management.MalformedObjectNameException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.geronimo.gbean.GBeanInfo;
import org.apache.geronimo.gbean.GBeanInfoBuilder;
import org.apache.geronimo.gbean.WaitingException;
import org.apache.geronimo.naming.java.ReadOnlyContext;
import org.apache.geronimo.security.ContextManager;
import org.apache.geronimo.security.GeronimoSecurityException;
import org.apache.geronimo.security.IdentificationPrincipal;
import org.apache.geronimo.security.PrimaryRealmPrincipal;
import org.apache.geronimo.security.RealmPrincipal;
import org.apache.geronimo.security.SubjectId;
import org.apache.geronimo.security.realm.SecurityRealm;
import org.apache.geronimo.security.deploy.DefaultPrincipal;
import org.apache.geronimo.security.deploy.Security;
import org.apache.geronimo.security.deploy.AutoMapAssistant;
import org.apache.geronimo.security.util.ConfigurationUtil;
import org.apache.geronimo.transaction.TrackedConnectionAssociator;
import org.apache.geronimo.transaction.OnlineUserTransaction;
import org.apache.geronimo.transaction.context.TransactionContextManager;
import org.apache.geronimo.kernel.Kernel;

import org.mortbay.http.Authenticator;
import org.mortbay.http.HttpException;
import org.mortbay.http.HttpRequest;
import org.mortbay.http.HttpResponse;
import org.mortbay.http.PathMap;
import org.mortbay.http.SecurityConstraint;
import org.mortbay.http.UserRealm;
import org.mortbay.jetty.servlet.FormAuthenticator;
import org.mortbay.jetty.servlet.ServletHttpRequest;
import org.mortbay.util.LazyList;


/**
* A class extension to <code>JettyWebAppContext</code> whose purpose is to
* provide JACC security checks.
*
* @version $Rev: 57351 $ $Date: 2004-11-10 13:39:50 -0600 (Wed, 10 Nov 2004) $
* @see org.mortbay.jetty.servlet.WebApplicationContext#checkSecurityConstraints(java.lang.String, org.mortbay.http.HttpRequest, org.mortbay.http.HttpResponse)
*/
public class JettyWebAppJACCContext extends JettyWebAppContext {
    private static Log log = LogFactory.getLog(JettyWebAppJACCContext.class);

    private final Kernel kernel;
    private final String policyContextID;
    private final Security securityConfig;
    private final JAASJettyPrincipal defaultPrincipal;

    private PolicyConfigurationFactory factory;
    private PolicyConfiguration policyConfiguration;

    private final Map roleDesignates = new HashMap();
    private final PathMap constraintMap = new PathMap();

    private String formLoginPath;

    public JettyWebAppJACCContext() {
        kernel = null;
        policyContextID = null;
        securityConfig = null;
        defaultPrincipal = null;
    }

    public JettyWebAppJACCContext(
            Kernel kernel,
            URI uri,
            ReadOnlyContext componentContext,
            OnlineUserTransaction userTransaction,
            ClassLoader classLoader,
            URI[] webClassPath,
            boolean contextPriorityClassLoader,
            URL configurationBaseUrl,
            Set unshareableResources,
            Set applicationManagedSecurityResources,
            String policyContextID,
            Security securityConfig,
            TransactionContextManager transactionContextManager,
            TrackedConnectionAssociator trackedConnectionAssociator,
            JettyContainer jettyContainer) throws MalformedURLException {

        super(uri,
                componentContext,
                userTransaction,
                classLoader,
                webClassPath,
                contextPriorityClassLoader,
                configurationBaseUrl,
                unshareableResources,
                applicationManagedSecurityResources,
                transactionContextManager,
                trackedConnectionAssociator,
                jettyContainer);

        this.kernel = kernel;
        this.policyContextID = policyContextID;
        this.securityConfig = securityConfig;
        defaultPrincipal = generateDefaultPrincipal(securityConfig);

        /**
         * We want to use our own web-app handler.
         */
        addHandler(new JettyWebAppHandler());
    }

    public Kernel getKernel() {
        return kernel;
    }

    public String getPolicyContextID() {
        return policyContextID;
    }

    public Security getSecurityConfig() {
        return securityConfig;
    }

    public Subject getRoleDesignate(String roleName) {
        return (Subject) roleDesignates.get(roleName);
    }

    void setRoleDesignate(String roleName, Subject subject) {
        roleDesignates.put(roleName, subject);
    }

    /**
     * Handler request.
     * Call each HttpHandler until request is handled.
     *
     * @param pathInContext path in context
     * @param pathParams path parameters such as encoded Session ID
     * @param httpRequest the request object
     * @param httpResponse the response object
     */
    public void handle(String pathInContext,
            String pathParams,
            HttpRequest httpRequest,
            HttpResponse httpResponse)
            throws HttpException, IOException {

        String savedPolicyContextID = PolicyContext.getContextID();
        JettyWebAppJACCContext savedContext = JettyServer.getCurrentWebAppContext();

        try {
            PolicyContext.setContextID(policyContextID);
            JettyServer.setCurrentWebAppContext(this);

            super.handle(pathInContext, pathParams, httpRequest, httpResponse);
        } finally {
            JettyServer.setCurrentWebAppContext(savedContext);
            PolicyContext.setContextID(savedPolicyContextID);
        }
    }

    /**
     * Keep our own copy of security constraints.<p/>
     * <p/>
     * We keep our own copy of security constraints because Jetty's copy is
     * private.  We use these constraints not for any authorization descitions
     * but, to decide whether we should attempt to authenticate the request.
     *
     * @param pathSpec The path spec to which the secuiryt cosntraint applies
     * @param sc the security constraint
     * TODO Jetty to provide access to this map so we can remove this method
     * @see org.mortbay.http.HttpContext#addSecurityConstraint(java.lang.String, org.mortbay.http.SecurityConstraint)
     */
    public void addSecurityConstraint(String pathSpec, SecurityConstraint sc) {
        super.addSecurityConstraint(pathSpec, sc);

        Object scs = constraintMap.get(pathSpec);
        scs = LazyList.add(scs, sc);
        constraintMap.put(pathSpec, scs);

        if (log.isDebugEnabled()) {
            log.debug("added " + sc + " at " + pathSpec);
        }
    }

    /**
     * Check the security constraints using JACC.
     *
     * @param pathInContext path in context
     * @param request HTTP request
     * @param response HTTP response
     * @return true if the path in context passes the security check,
     *         false if it fails or a redirection has occured during authentication.
     */
    public boolean checkSecurityConstraints(String pathInContext, HttpRequest request, HttpResponse response) throws HttpException, IOException {
        if (formLoginPath != null) {
            String pathToBeTested = (pathInContext.indexOf('?') > 0 ? pathInContext.substring(0, pathInContext.indexOf('?')) : pathInContext);

            if (pathToBeTested.equals(formLoginPath)) {
                return true;
            }
        }

        try {
            Principal user = obtainUser(pathInContext, request, response);

            if (user == null) {
                return false;
            }
            if (user == SecurityConstraint.__NOBODY) {
                return true;
            }

            AccessControlContext acc = ContextManager.getCurrentContext();
            ServletHttpRequest servletHttpRequest = (ServletHttpRequest) request.getWrapper();

            /**
             * JACC v1.0 secion 4.1.1
             */
            acc.checkPermission(new WebUserDataPermission(servletHttpRequest));

            /**
             * JACC v1.0 secion 4.1.2
             */
            acc.checkPermission(new WebResourcePermission(servletHttpRequest));
        } catch (HttpException he) {
            response.sendError(he.getCode(), he.getReason());
            return false;
        } catch (AccessControlException ace) {
            response.sendError(HttpResponse.__403_Forbidden);
            return false;
        }
        return true;
    }

    /**
     * Obtain an authenticated user, if one is required.  Otherwise return the
     * default principal.
     * <p/>
     * Also set the current caller for JACC security checks for the default
     * principal.  This is automatically done by <code>JAASJettyRealm</code>.
     *
     * @param pathInContext path in context
     * @param request HTTP request
     * @param response HTTP response
     * @return <code>null</code> if there is no authenticated user at the moment
     *         and security checking should not proceed and servlet handling should also
     *         not proceed, e.g. redirect. <code>SecurityConstraint.__NOBODY</code> if
     *         security checking should not proceed and servlet handling should proceed,
     *         e.g. login page.
     */
    public Principal obtainUser(String pathInContext, HttpRequest request, HttpResponse response) throws HttpException, IOException {
        List scss = constraintMap.getMatches(pathInContext);
        String pattern = null;
        boolean unauthenticated = false;
        boolean forbidden = false;

        if (scss != null && scss.size() > 0) {

            // for each path match
            // Add only constraints that have the correct method
            // break if the matching pattern changes.  This allows only
            // constraints with matching pattern and method to be combined.
            loop:
            for (int m = 0; m < scss.size(); m++) {
                Map.Entry entry = (Map.Entry) scss.get(m);
                Object scs = entry.getValue();
                String p = (String) entry.getKey();
                for (int c = 0; c < LazyList.size(scs); c++) {
                    SecurityConstraint sc = (SecurityConstraint) LazyList.get(scs, c);
                    if (!sc.forMethod(request.getMethod())) continue;

                    if (pattern != null && !pattern.equals(p)) break loop;
                    pattern = p;

                    // Check the method applies
                    if (!sc.forMethod(request.getMethod())) continue;

                    // Combine auth constraints.
                    if (sc.getAuthenticate()) {
                        if (!sc.isAnyRole()) {
                            List scr = sc.getRoles();
                            if (scr == null || scr.size() == 0) {
                                forbidden = true;
                                break loop;
                            }
                        }
                    } else {
                        unauthenticated = true;
                    }
                }
            }
        } else {
            unauthenticated = true;
        }

        UserRealm realm = getRealm();
        Authenticator authenticator = getAuthenticator();
        Principal user = null;
        if (!unauthenticated && !forbidden) {
            if (realm == null) {
                log.warn("Realm Not Configured");
                throw new HttpException(HttpResponse.__500_Internal_Server_Error, "Realm Not Configured");
            }


            // Handle pre-authenticated request
            if (authenticator != null) {
                // User authenticator.
                user = authenticator.authenticate(realm, pathInContext, request, response);
            } else {
                // don't know how authenticate
                log.warn("Mis-configured Authenticator for " + request.getPath());
                throw new HttpException(HttpResponse.__500_Internal_Server_Error, "Mis-configured Authenticator for " + request.getPath());
            }

            return user;
        } else if (authenticator instanceof FormAuthenticator && pathInContext.endsWith(FormAuthenticator.__J_SECURITY_CHECK)) {
            /**
             * This could be a post request to __J_SECURITY_CHECK.
             */
            if (realm == null) {
                log.warn("Realm Not Configured");
                throw new HttpException(HttpResponse.__500_Internal_Server_Error, "Realm Not Configured");
            }
            return authenticator.authenticate(realm, pathInContext, request, response);
        }

        /**
         * No authentication is required.  Return the defaultPrincipal.
         */
        ContextManager.setCurrentCaller(defaultPrincipal.getSubject());
        return defaultPrincipal;
    }

    /**
     * Generate the default principal from the security config.
     *
     * @param securityConfig The Geronimo security configuration.
     * @return the default principal
     */
    protected JAASJettyPrincipal generateDefaultPrincipal(Security securityConfig) throws GeronimoSecurityException {

        DefaultPrincipal defaultPrincipal = securityConfig.getDefaultPrincipal();
        if (defaultPrincipal == null) {
            AutoMapAssistant config = securityConfig.getAssistant();
            try {
                if (config != null) {
                    Set assistants = kernel.listGBeans(new ObjectName("geronimo.security:type=SecurityRealm,realm=" + config.getSecurityRealm()));
                    if (assistants.size() < 1 || assistants.size() > 1) throw new GeronimoSecurityException("Only one auto mapping assistant should match " + config.getSecurityRealm());

                    org.apache.geronimo.security.realm.AutoMapAssistant assistant = (org.apache.geronimo.security.realm.AutoMapAssistant) assistants.iterator().next();
                    org.apache.geronimo.security.deploy.Principal principal = assistant.obtainDefaultPrincipal();
                    defaultPrincipal = new DefaultPrincipal();
                    defaultPrincipal.setPrincipal(principal);
                    defaultPrincipal.setRealmName(((SecurityRealm)assistant).getRealmName());
                }
            } catch (MalformedObjectNameException e) {
                throw new GeronimoSecurityException("Bad object name geronimo.security:type=SecurityRealm,realm=" + config.getSecurityRealm());
            }

        }
        if (defaultPrincipal == null) throw new GeronimoSecurityException("Unable to generate default principal");

        return generateDefaultPrincipal(securityConfig, defaultPrincipal);
    }

    protected JAASJettyPrincipal generateDefaultPrincipal(Security securityConfig, DefaultPrincipal defaultPrincipal) throws GeronimoSecurityException {
        JAASJettyPrincipal result = new JAASJettyPrincipal("default");
        Subject defaultSubject = new Subject();

        RealmPrincipal realmPrincipal = ConfigurationUtil.generateRealmPrincipal(defaultPrincipal.getPrincipal(), defaultPrincipal.getRealmName());
        if (realmPrincipal == null) {
            throw new GeronimoSecurityException("Unable to create realm principal");
        }
        PrimaryRealmPrincipal primaryRealmPrincipal = ConfigurationUtil.generatePrimaryRealmPrincipal(defaultPrincipal.getPrincipal(), defaultPrincipal.getRealmName());
        if (primaryRealmPrincipal == null) {
            throw new GeronimoSecurityException("Unable to create primary realm principal");
        }

        defaultSubject.getPrincipals().add(realmPrincipal);
        defaultSubject.getPrincipals().add(primaryRealmPrincipal);

        result.setSubject(defaultSubject);

        return result;
    }

    public void doStart() throws WaitingException, Exception {
        super.doStart();

        Authenticator authenticator = getAuthenticator();
        if (authenticator instanceof FormAuthenticator) {
            formLoginPath = ((FormAuthenticator) authenticator).getLoginPage();
            if (formLoginPath.indexOf('?') > 0) {
                formLoginPath = formLoginPath.substring(0, formLoginPath.indexOf('?'));
            }
        }

        /**
         * Register our default principal with the ContextManager
         */
        Subject defaultSubject = defaultPrincipal.getSubject();
        ContextManager.registerSubject(defaultSubject);
        SubjectId id = ContextManager.getSubjectId(defaultSubject);
        defaultSubject.getPrincipals().add(new IdentificationPrincipal(id));

        log.debug("Default subject " + id + " for JACC policy '" + ((JettyWebAppJACCContext) getHttpContext()).getPolicyContextID() + "' registered.");

        /**
         * Get the JACC policy configuration that's associated with this
         * web application and configure it with the geronimo security
         * configuration.  The work for this is done by the class
         * JettyXMLConfiguration.
         */
        try {
            factory = PolicyConfigurationFactory.getPolicyConfigurationFactory();

            policyConfiguration = factory.getPolicyConfiguration(policyContextID, true);
            Configuration[] configurations = getConfigurations();
            for (int i = 0; i < configurations.length; i++) {
                if (configurations[i] instanceof JettyXMLConfiguration) {
                    ((JettyXMLConfiguration) configurations[i]).configure(policyConfiguration, securityConfig);
                }
            }
            policyConfiguration.commit();
        } catch (ClassNotFoundException e) {
            // do nothing
        } catch (PolicyContextException e) {
            // do nothing
        } catch (GeronimoSecurityException e) {
            // do nothing
        }

        /**
         * Register the role designates with the context manager.
         *
         * THIS MUST BE RUN AFTER JettyXMLConfiguration.configure()
         */
        Iterator iter = roleDesignates.keySet().iterator();
        while (iter.hasNext()) {
            String roleName = (String) iter.next();
            Subject roleDesignate = (Subject) roleDesignates.get(roleName);

            ContextManager.registerSubject(roleDesignate);
            id = ContextManager.getSubjectId(roleDesignate);
            roleDesignate.getPrincipals().add(new IdentificationPrincipal(id));

            log.debug("Role designate " + id + " for role '" + roleName + "' for JACC policy '" + ((JettyWebAppJACCContext) getHttpContext()).getPolicyContextID() + "' registered.");
        }

        log.info("JettyWebAppJACCContext started with JACC policy '" + policyContextID + "'");
    }

    public void doStop() throws WaitingException, Exception {
        super.doStop();

        /**
         * Unregister the default principal and role designates
         */
        log.debug("Default subject " + ContextManager.getSubjectId(defaultPrincipal.getSubject()) + " for JACC policy " + ((JettyWebAppJACCContext) getHttpContext()).getPolicyContextID() + "' unregistered.");

        ContextManager.unregisterSubject(defaultPrincipal.getSubject());

        Iterator iter = roleDesignates.keySet().iterator();
        while (iter.hasNext()) {
            String roleName = (String) iter.next();
            Subject roleDesignate = (Subject) roleDesignates.get(roleName);

            ContextManager.unregisterSubject(roleDesignate);
            log.debug("Role designate " + ContextManager.getSubjectId(roleDesignate) + " for role '" + roleName + "' for JACC policy '" + ((JettyWebAppJACCContext) getHttpContext()).getPolicyContextID() + "' unregistered.");
        }

        /**
         * Delete the policy configuration for this web application
         */
        if (policyConfiguration != null) {
            policyConfiguration.delete();
        }

        log.info("JettyWebAppJACCContext with JACC policy '" + policyContextID + "' stopped");
    }

    public void doFail() {
        super.doFail();

        try {
            if (policyConfiguration != null) {
                policyConfiguration.delete();
            }
        } catch (PolicyContextException e) {
            // do nothing
        }

        log.info("JettyWebAppJACCContext failed");
    }

    public static final GBeanInfo GBEAN_INFO;

    static {
        GBeanInfoBuilder infoFactory = new GBeanInfoBuilder("Jetty JACC WebApplication Context", JettyWebAppJACCContext.class, JettyWebAppContext.GBEAN_INFO);

        infoFactory.addAttribute("kernel", Kernel.class, false);
        infoFactory.addAttribute("policyContextID", String.class, true);
        infoFactory.addAttribute("securityConfig", Security.class, true);

        infoFactory.setConstructor(new String[]{
            "kernel",
            "uri",
            "componentContext",
            "userTransaction",
            "classLoader",
            "webClassPath",
            "contextPriorityClassLoader",
            "configurationBaseUrl",
            "unshareableResources",
            "applicationManagedSecurityResources",
            "policyContextID",
            "securityConfig",
            "TransactionContextManager",
            "TrackedConnectionAssociator",
            "JettyContainer",
        });

        GBEAN_INFO = infoFactory.getBeanInfo();
    }

    public static GBeanInfo getGBeanInfo() {
        return GBEAN_INFO;
    }

}
TOP

Related Classes of org.apache.geronimo.jetty.JettyWebAppJACCContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.