alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Glassfish example source code file (EJBSecurityManager.java)

This example Glassfish source code file (EJBSecurityManager.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Glassfish tags/keywords

ejbmethodpermission, ejbmethodpermission, exception, iterator, jacc, jacc, log, logging, object, object, permissions, protectiondomain, reflection, security, securitycontext, string, string, throwable, util

The Glassfish EJBSecurityManager.java source code

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package org.glassfish.ejb.security.application;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.WeakHashMap;
import java.lang.reflect.Method;
import java.util.Collections;
import javax.security.auth.Subject;
import javax.security.auth.SubjectDomainCombiner;
import javax.security.jacc.EJBMethodPermission;
import javax.security.jacc.EJBRoleRefPermission;
import javax.security.jacc.PolicyConfigurationFactory;
import javax.security.jacc.PolicyConfiguration;
import javax.security.jacc.PolicyContext;
import javax.security.jacc.PolicyContextException;

import com.sun.enterprise.security.SecurityManager;
import com.sun.enterprise.deployment.*;
import com.sun.enterprise.deployment.interfaces.SecurityRoleMapperFactory;
import com.sun.enterprise.deployment.web.SecurityRoleReference;
import com.sun.enterprise.security.common.AppservAccessController;
import com.sun.enterprise.security.SecurityContext;
import com.sun.enterprise.security.auth.login.LoginContextDriver;
import com.sun.enterprise.security.authorize.PolicyContextHandlerImpl;
import com.sun.enterprise.security.audit.AuditManager;
import com.sun.enterprise.security.CachedPermission;
import com.sun.enterprise.security.CachedPermissionImpl;
import com.sun.enterprise.security.PermissionCache;
import com.sun.enterprise.security.PermissionCacheFactory;
import java.util.logging.*;

import com.sun.logging.LogDomains;
import com.sun.ejb.EjbInvocation;

import com.sun.enterprise.security.SecurityUtil;
import java.security.*;

import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.api.invocation.InvocationManager;
import org.glassfish.api.invocation.InvocationException;

import org.glassfish.ejb.security.factory.EJBSecurityManagerFactory;
import org.glassfish.internal.api.Globals;
import org.glassfish.external.probe.provider.PluginPoint;
import org.glassfish.external.probe.provider.StatsProviderManager;

/**
 * This class is used by the EJB server to manage security. All
 * the container object only call into this object for managing
 * security. This class cannot be subclassed.
 * <p/>
 * An instance of this class should be created per deployment unit.
 *
 * @author Harpreet Singh, monzillo
 */
public final class EJBSecurityManager
         /*extends SecurityManagerFactory*/ implements SecurityManager {

    private static final Logger _logger;

    static {
        _logger = LogDomains.getLogger(EJBSecurityManager.class, LogDomains.EJB_LOGGER);
    } 

    private  AuditManager auditManager;

    private static final PolicyContextHandlerImpl pcHandlerImpl =
            (PolicyContextHandlerImpl) PolicyContextHandlerImpl.getInstance();

    private final SecurityRoleMapperFactory roleMapperFactory;
            //SecurityRoleMapperFactoryMgr.getFactory();

    private final EjbDescriptor deploymentDescriptor;
    // Objects required for Run-AS
    private final RunAsIdentityDescriptor runAs;

    // jacc related
    private static PolicyConfigurationFactory pcf = null;
    private String ejbName = null;
    // contextId id is the same as an appname. This will be used to get
    // a PolicyConfiguration object per application.
    private String contextId = null;
    private String codebase = null;
    private CodeSource codesource = null;
    private String realmName = null;
    // this stores the role ref permissions. So will not need to spend runtime 
    // resources generating permissions.
    //private Hashtable cacheRoleToPerm = new Hashtable();

    // we use two protection domain caches until we decide how to 
    // set the codesource in the protection domain of system apps.
    // PD's in protectionDomainCache have the (privileged) codesource
    // of the EJBSecurityManager class. The PD used in pre-dispatch
    // authorization decisions MUST not be constructed using a privileged
    // codesource (or else all pre-distpatch access decisions will be granted).
    private final Map cacheProtectionDomain =
            Collections.synchronizedMap(new WeakHashMap());
    private final Map protectionDomainCache =
            Collections.synchronizedMap(new WeakHashMap());

    private final Map accessControlContextCache =
            Collections.synchronizedMap(new WeakHashMap());

    private PermissionCache uncheckedMethodPermissionCache = null;

    private final Policy policy;

    private static final CodeSource managerCodeSource =
            EJBSecurityManager.class.getProtectionDomain().getCodeSource();

    private final InvocationManager invMgr;
    private final EJBSecurityManagerFactory ejbSFM;
    private final EjbSecurityProbeProvider probeProvider = new EjbSecurityProbeProvider();
    private static EjbSecurityStatsProvider ejbStatsProvider = null;

    /**
     * This method iniitalizes the EJBSecurityManager
     */
    public EJBSecurityManager(EjbDescriptor ejbDescriptor, InvocationManager invMgr,
                                EJBSecurityManagerFactory fact) throws Exception {

        this.deploymentDescriptor = (EjbDescriptor) ejbDescriptor;
        this.invMgr = invMgr;
        roleMapperFactory = Globals.get(SecurityRoleMapperFactory.class);
        // get the default policy
        policy = Policy.getPolicy();
        ejbSFM = fact;

        boolean runas = !(deploymentDescriptor.getUsesCallerIdentity());
        if (runas) {
            runAs = deploymentDescriptor.getRunAsIdentity();

            // Note: runAs may be null even when runas==true if this EJB
            // is an MDB. 
            if (runAs != null) {
                if (_logger.isLoggable(Level.FINE)) {
                    _logger.log(Level.FINE, deploymentDescriptor.getEjbClassName() +
                            " will run-as: " + runAs.getPrincipal() +
                            " (" + runAs.getRoleName() + ")");
                }
            }
        } else {
            runAs = null;
        }

        initialize();
    }

    private static CodeSource getApplicationCodeSource(String pcid) throws Exception {
        CodeSource result = null;
        String archiveURI = "file:///" + pcid.replace(' ', '_');
        try {
            java.net.URI uri = null;
            try {
                uri = new java.net.URI(archiveURI);
                if (uri != null) {
                    result = new CodeSource(uri.toURL(),
                            (java.security.cert.Certificate[]) null);
                }
            } catch (java.net.URISyntaxException use) {
                // manually create the URL
                _logger.log(Level.SEVERE, "JACC_createurierror", use);
                throw new RuntimeException(use);
            }

        } catch (java.net.MalformedURLException mue) {
            // should never come here.
            _logger.log(Level.SEVERE, "JACC_ejbsm.codesourceerror", mue);
            throw new RuntimeException(mue);
        }
        return result;
    }

    // obtains PolicyConfigurationFactory once for class
    private static PolicyConfigurationFactory getPolicyFactory()
            throws PolicyContextException {
        synchronized (EJBSecurityManager.class) {
            if (pcf == null) {
                try {
                    pcf = PolicyConfigurationFactory.getPolicyConfigurationFactory();
                } catch (ClassNotFoundException cnfe) {
                    _logger.severe("jaccfactory.notfound");
                    throw new PolicyContextException(cnfe);
                } catch (PolicyContextException pce) {
                    _logger.severe("jaccfactory.notfound");
                    throw pce;
                }
            }
        }
        return pcf;
    }

    public boolean getUsesCallerIdentity() {
        return (runAs == null);
    }

    public void loadPolicyConfiguration(EjbDescriptor eDescriptor) throws Exception {
        
        boolean inService = getPolicyFactory().inService(contextId);

	// only load the policy configuration if it isn't already in service.
	// Consequently, all things that deploy modules (as apposed to
	// loading already deployed modules) must make sure pre-exiting
	// pc is either in deleted or open state before this method
	// is called. Note that policy statements are not
	// removed to allow multiple EJB's to be represented by same pc.

	if (!inService) {

	    // translate the deployment descriptor to configure the policy rules.

	    convertEJBMethodPermissions(eDescriptor,contextId);
	    convertEJBRoleReferences(eDescriptor,contextId);

	    if(_logger.isLoggable(Level.FINE)){
		_logger.fine("JACC: policy translated for policy context:" +contextId);
	    }
	} 
    }

    public static String getContextID(EjbDescriptor ejbDesc) {
        return SecurityUtil.getContextID(ejbDesc.getEjbBundleDescriptor());
    }

//    public static String getContextID(EjbBundleDescriptor ejbBundleDesc) {
//        String cid = null;
//        if (ejbBundleDesc != null) {
//            cid = ejbBundleDesc.getApplication().getRegistrationName() +
//                    '/' + ejbBundleDesc.getUniqueFriendlyId();
//        }
//        return cid;
//    }


    private void initialize() throws Exception {
        if (ejbStatsProvider == null) {
            synchronized (EjbSecurityStatsProvider.class) {
                if (ejbStatsProvider == null) {
                    ejbStatsProvider = new EjbSecurityStatsProvider();
                    StatsProviderManager.register("security", PluginPoint.SERVER, "security/ejb", ejbStatsProvider);
                }

            }
        }
           
        contextId = getContextID(deploymentDescriptor);
        String appName = deploymentDescriptor.getApplication().getRegistrationName();
        roleMapperFactory.setAppNameForContext(appName, contextId);
        codesource = getApplicationCodeSource(contextId);
        ejbName = deploymentDescriptor.getName();

        realmName = deploymentDescriptor.getApplication().getRealm();

        if (realmName == null) {
            Set iorConfigs = deploymentDescriptor.getIORConfigurationDescriptors();
            // iorConfigs is not null from implementation of EjbDescriptor
            Iterator iter = iorConfigs.iterator();
            if (iter != null) {
                // there should be at most one element in the loop from
                // definition of dtd
                while (iter.hasNext()) {
                    EjbIORConfigurationDescriptor iorConfig =
                            (EjbIORConfigurationDescriptor) iter.next();
                    realmName = iorConfig.getRealmName();
                }
            }
        }

        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("JACC: Context id (id under which all EJB's in application will be created) = " + contextId);
            _logger.fine("Codebase (module id for ejb " + ejbName + ") = " + codebase);
        }
	loadPolicyConfiguration(deploymentDescriptor);
        // translate the deployment descriptor to populate the role-ref permission cache
        //addEJBRoleReferenceToCache(deploymentDescriptor);

        // create and initialize the unchecked permission cache.
        uncheckedMethodPermissionCache =
                PermissionCacheFactory.createPermissionCache(
                        this.contextId, this.codesource,
                        EJBMethodPermission.class,
                        this.ejbName);
        
        auditManager = this.ejbSFM.getAuditManager();
        
    }

    /**
     * This method converts ejb role references to jacc permission objects
     * and adds them to the policy configuration object
     * It gets the list of role references from the ejb descriptor. For each
     * such role reference, create a EJBRoleRefPermission and add it to the
     * PolicyConfiguration object.
     *
     * @param eDescriptor the ejb descriptor
     * @param pcid,       the policy context identifier
     */
    private static void
    convertEJBRoleReferences(EjbDescriptor eDescriptor, String pcid)
            throws PolicyContextException {
        
        PolicyConfiguration pc =
                getPolicyFactory().getPolicyConfiguration(pcid, false);
        assert pc != null;
        if (pc != null) {
            String eName = eDescriptor.getName();
            Iterator iroleref = eDescriptor.getRoleReferences().iterator();
            while (iroleref.hasNext()) {
                SecurityRoleReference roleRef =
                        (SecurityRoleReference) iroleref.next();
                String rolename = roleRef.getRolename();
                EJBRoleRefPermission ejbrr =
                        new EJBRoleRefPermission(eName, rolename);
                String rolelink = roleRef.getSecurityRoleLink().getName();

                pc.addToRole(rolelink, ejbrr);

                if (_logger.isLoggable(Level.FINE)) {
                    _logger.fine("JACC: Converting role-ref -> " + roleRef.toString() +
                            " to permission with name(" + ejbrr.getName() +
                            ") and actions (" + ejbrr.getActions() +
                            ")" + "mapped to role (" + rolelink + ")");
                }
            }
        }
    }

    /**
     * This method converts ejb role references to jacc permission objects
     * and adds them to the corresponding permission cache.
     *
     * @param eDescriptor the ejb descriptor
     
    private void addEJBRoleReferenceToCache(EjbDescriptor eDescriptor) {

        String eName = eDescriptor.getName();

        Iterator iroleref = eDescriptor.getRoleReferences().iterator();
        while (iroleref.hasNext()) {
            SecurityRoleReference roleRef =
                    (SecurityRoleReference) iroleref.next();
            String rolename = roleRef.getRolename();
            EJBRoleRefPermission ejbrr =
                    new EJBRoleRefPermission(eName, rolename);
            String rolelink = roleRef.getSecurityRoleLink().getName();

            cacheRoleToPerm.put(eName + "_" + rolename, ejbrr);

            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("JACC: Converting role-ref -> " + roleRef.toString() +
                        " to permission with name(" + ejbrr.getName() +
                        ") and actions (" + ejbrr.getActions() +
                        ")" + "mapped to role (" + rolelink + ")");
            }
        }
    }*/

    // utility to collect role permisisions in table of collections
    private static HashMap addToRolePermissionsTable(HashMap table,
                                                     MethodPermission mp,
                                                     EJBMethodPermission ejbmp) {
        if (mp.isRoleBased()) {
            if (table == null) {
                table = new HashMap();
            }
            String roleName = mp.getRole().getName();
            Permissions rolePermissions =
                    (Permissions) table.get(roleName);
            if (rolePermissions == null) {
                rolePermissions = new Permissions();
                table.put(roleName, rolePermissions);
            }
            rolePermissions.add(ejbmp);
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("JACC DD conversion: EJBMethodPermission ->(" +
                        ejbmp.getName() + " " + ejbmp.getActions() +
                        ")protected by role -> " + roleName);
            }
        }
        return table;
    }

    // utility to collect unchecked permissions in collection
    private static Permissions addToUncheckedPermissions(Permissions permissions,
                                                         MethodPermission mp,
                                                         EJBMethodPermission ejbmp) {
        if (mp.isUnchecked()) {
            if (permissions == null) {
                permissions = new Permissions();
            }
            permissions.add(ejbmp);
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("JACC DD conversion: EJBMethodPermission ->("
                        + ejbmp.getName() + " " + ejbmp.getActions() +
                        ") is (unchecked)");
            }
        }
        return permissions;
    }

    // utility to collect excluded permissions in collection
    private static Permissions addToExcludedPermissions(Permissions permissions,
                                                        MethodPermission mp,
                                                        EJBMethodPermission ejbmp) {
        if (mp.isExcluded()) {
            if (permissions == null) {
                permissions = new Permissions();
            }
            permissions.add(ejbmp);
            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("JACC DD conversion: EJBMethodPermission ->("
                        + ejbmp.getName() + " " + ejbmp.getActions() +
                        ") is (excluded)");
            }
        }
        return permissions;
    }

    /**
     * This method converts the dd in two phases.
     * Phase 1:
     * gets a map representing the methodPermission elements exactly as they
     * occured for the ejb in the dd. The map is keyed by method-permission
     * element and each method-permission is mapped to a list of method
     * elements representing the method elements of the method permision
     * element. Each method element is converted to a corresponding
     * EJBMethodPermission and added, based on its associated method-permission,
     * to the policy configuration object.
     * phase 2:
     * configures additional EJBMethodPermission policy statements
     * for the purpose of optimizing Permissions.implies matching by the
     * policy provider. This phase also configures unchecked policy
     * statements for any uncovered methods. This method gets the list
     * of method descriptors for the ejb from the EjbDescriptor object.
     * For each method descriptor, it will get a list of MethodPermission
     * objects that signify the method permissions for the Method and
     * convert each to a corresponding EJBMethodPermission to be added
     * to the policy configuration object.
     *
     * @param eDescriptor the ejb descriptor for this EJB.
     * @param pcid,       the policy context identifier.
     */
    private static void
    convertEJBMethodPermissions(EjbDescriptor eDescriptor, String pcid)
            throws PolicyContextException {

        PolicyConfiguration pc =
                getPolicyFactory().getPolicyConfiguration(pcid, false);

        assert pc != null;

        if (pc != null) {

            String eName = eDescriptor.getName();

            Permissions uncheckedPermissions = null;
            Permissions excludedPermissions = null;
            HashMap rolePermissionsTable = null;

            EJBMethodPermission ejbmp = null;

            // phase 1
            Map mpMap = eDescriptor.getMethodPermissionsFromDD();
            if (mpMap != null) {

                Iterator mpIt = mpMap.keySet().iterator();

                while (mpIt.hasNext()) {

                    MethodPermission mp = (MethodPermission) mpIt.next();

                    Iterator mdIt = ((ArrayList) mpMap.get(mp)).iterator();

                    while (mdIt.hasNext()) {

                        MethodDescriptor md = (MethodDescriptor) mdIt.next();

                        String mthdName = md.getName();
                        String mthdIntf = md.getEjbClassSymbol();
                        String mthdParams[] = md.getStyle() == 3 ?
                                md.getParameterClassNames() : null;

                        ejbmp = new EJBMethodPermission(eName, mthdName.equals("*") ?
                                null : mthdName,
                                mthdIntf, mthdParams);
                        rolePermissionsTable =
                                addToRolePermissionsTable(rolePermissionsTable, mp, ejbmp);

                        uncheckedPermissions =
                                addToUncheckedPermissions(uncheckedPermissions, mp, ejbmp);

                        excludedPermissions =
                                addToExcludedPermissions(excludedPermissions, mp, ejbmp);
                    }
                }
            }

            // phase 2 - configures additional perms:
            //      . to optimize performance of Permissions.implies
            //      . to cause any uncovered methods to be unchecked

            Iterator mdIt = eDescriptor.getMethodDescriptors().iterator();
            while (mdIt.hasNext()) {

                MethodDescriptor md = (MethodDescriptor) mdIt.next();
                Method mthd = md.getMethod(eDescriptor);
                String mthdIntf = md.getEjbClassSymbol();

                if (mthd == null) {
                    continue;
                }

                if (mthdIntf == null || mthdIntf.equals("")) {
                    _logger.log(Level.SEVERE, "method_descriptor_not_defined" , new Object[] {eName,
                        md.getName(), md.getParameterClassNames()});

                    continue;
                }

                ejbmp = new EJBMethodPermission(eName, mthdIntf, mthd);

                Iterator mpIt = eDescriptor.getMethodPermissionsFor(md).iterator();

                while (mpIt.hasNext()) {

                    MethodPermission mp = (MethodPermission) mpIt.next();

                    rolePermissionsTable =
                            addToRolePermissionsTable(rolePermissionsTable, mp, ejbmp);

                    uncheckedPermissions =
                            addToUncheckedPermissions(uncheckedPermissions, mp, ejbmp);

                    excludedPermissions =
                            addToExcludedPermissions(excludedPermissions, mp, ejbmp);
                }
            }

            if (uncheckedPermissions != null) {
                pc.addToUncheckedPolicy(uncheckedPermissions);
            }
            if (excludedPermissions != null) {
                pc.addToExcludedPolicy(excludedPermissions);
            }
            if (rolePermissionsTable != null) {

                Iterator roleIt = rolePermissionsTable.keySet().iterator();

                while (roleIt.hasNext()) {
                    String roleName = (String) roleIt.next();
                    pc.addToRole(roleName,
                            (Permissions) rolePermissionsTable.get(roleName));
                }
            }
        }
    }

    private ProtectionDomain getCachedProtectionDomain(Set principalSet,
                                                       boolean applicationCodeSource) {

        ProtectionDomain prdm = null;
        Principal[] principals = null;

        /* Need to use the application codeSource for permission evaluations
       * as the manager codesource is granted all permissions in server.policy.
       * The manager codesource needs to be used for doPrivileged to allow system
       * apps to have all permissions, but we either need to revert to
       * real doAsPrivileged, or find a way to distinguish system apps.
       */

        CodeSource cs = null;

        if (applicationCodeSource) {
            prdm = (ProtectionDomain) cacheProtectionDomain.get(principalSet);
            cs = codesource;
        } else {
            prdm = (ProtectionDomain) protectionDomainCache.get(principalSet);
            cs = managerCodeSource;
        }

        if (prdm == null) {

            principals = (principalSet == null ? null :
                    (Principal[]) principalSet.toArray(new Principal[principalSet.size()]));

            prdm = new ProtectionDomain(cs, null, null, principals);

            // form a new key set so that it does not share with others
            Set newKeySet = ((principalSet != null) ? new HashSet(principalSet) : new HashSet());

            if (applicationCodeSource) {
                cacheProtectionDomain.put(newKeySet, prdm);
            } else {
                // form a new key set so that it does not share with others
                protectionDomainCache.put(newKeySet, prdm);
            }

            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("JACC: new ProtectionDomain added to cache");
            }

        }

        if (_logger.isLoggable(Level.FINE)) {
            if (principalSet == null) {
                _logger.fine("JACC: returning cached ProtectionDomain PrincipalSet: null");
            } else {
                StringBuffer pBuf = null;
                principals = (Principal[]) principalSet.toArray(new Principal[0]);
                for (int i = 0; i < principals.length; i++) {
                    if (i == 0) pBuf = new StringBuffer(principals[i].toString());
                    else pBuf.append(" " + principals[i].toString());
                }
                _logger.fine("JACC: returning cached ProtectionDomain - CodeSource: ("
                        + cs + ") PrincipalSet: " + pBuf);
            }
        }

        return prdm;
    }


    /**
     * This method is called by the EJB container to decide whether or not
     * a method specified in the Invocation should be allowed.
     *
     * @param compInv invocation object that contains all the details of the
     *                invocation.
     * @return A boolean value indicating if the client should be allowed
     *         to invoke the EJB.
     */
    public boolean authorize(ComponentInvocation compInv) {
        EjbInvocation inv = (EjbInvocation) compInv;    //FIXME: Param type should be EjbInvocation
        if (inv.getAuth() != null) {
            return inv.getAuth().booleanValue();
        }

        boolean ret = false;

        CachedPermission cp = null;
        Permission ejbmp = null;

        if (inv.invocationInfo == null || inv.invocationInfo.cachedPermission == null) {
            ejbmp = new EJBMethodPermission(ejbName, inv.getMethodInterface(), inv.method);
            cp = new CachedPermissionImpl(uncheckedMethodPermissionCache, ejbmp);
            if (inv.invocationInfo != null) {
                inv.invocationInfo.cachedPermission = cp;
                if (_logger.isLoggable(Level.FINE)) {
                    _logger.fine("JACC: permission initialized in InvocationInfo: EJBMethodPermission (Name) = " + ejbmp.getName() + " (Action) = " + ejbmp.getActions());
                }
            }
        } else {
            cp = inv.invocationInfo.cachedPermission;
            ejbmp = cp.getPermission();
        }

        String caller = null;
        SecurityContext sc = null;

        pcHandlerImpl.getHandlerData().setInvocation(inv);
        ret = cp.checkPermission();

        if (!ret) {

            sc = SecurityContext.getCurrent();
            Set principalSet = sc.getPrincipalSet();
            ProtectionDomain prdm = getCachedProtectionDomain(principalSet, true);
            try {
                // set the policy context in the TLS.
                String oldContextId = setPolicyContext(this.contextId);
                try {
                    ret = policy.implies(prdm, ejbmp);
                } catch (SecurityException se) {
                    _logger.log(Level.SEVERE, "jacc_access_exception", se);
                    ret = false;
                } catch (Throwable t) {
                    _logger.log(Level.SEVERE, "jacc_access_exception", t);
                    ret = false;
                } finally {
                    resetPolicyContext(oldContextId, this.contextId);
                }

            } catch (Throwable t) {
                _logger.log(Level.SEVERE, "jacc_policy_context_exception", t);
                ret = false;
            }
        }

        inv.setAuth((ret) ? Boolean.TRUE : Boolean.FALSE);

        if (auditManager.isAuditOn()) {
            if (sc == null) {
                sc = SecurityContext.getCurrent();
            }
            caller = sc.getCallerPrincipal().getName();
            auditManager.ejbInvocation(caller, ejbName, inv.method.toString(), ret);
        }

        if (ret && inv.isWebService && !inv.isPreInvokeDone()) {
            preInvoke(inv);
        }

        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("JACC: Access Control Decision Result: " + ret + " EJBMethodPermission (Name) = " + ejbmp.getName() + " (Action) = " + ejbmp.getActions() + " (Caller) = " + caller);
        }

        return ret;
    }

    /**
     * Checks if any method permissions are set. If they are not set
     * it is possible that this is a J2ee 1.2 APP and we would need to
     * grant permissions for execution anyways
     */
    private boolean areMethodPermissionsSet() {
        boolean empty =
                deploymentDescriptor.getPermissionedMethodsByPermission().isEmpty();
        return !empty;
    }

    /**
     * This method is used by MDB Container - Invocation Manager  to setup
     * the run-as identity information. It has to be coupled with
     * the postSetRunAsIdentity method.
     * This method is called for EJB/MDB Containers
     */
    public void preInvoke(ComponentInvocation inv) {

        //Optimization to avoid the expensive call

        if(runAs == null) {
            inv.setPreInvokeDone(true);
            return;
        }

        boolean isWebService = false;
        if (inv instanceof EjbInvocation) {
            isWebService = ((EjbInvocation) inv).isWebService;
        }

        // if it is not a webservice or successful authorization
        // and preInvoke is not call before
        if ((!isWebService || (inv.getAuth() != null && inv.getAuth().booleanValue()))
                && !inv.isPreInvokeDone()) {
            if (runAs != null) {
                inv.setOldSecurityContext(SecurityContext.getCurrent());
                loginForRunAs();
            }
            inv.setPreInvokeDone(true);
        }
    }

    /**
     * This method is used by Message Driven Bean Container to remove
     * the run-as identity information that was set up using the
     * preSetRunAsIdentity method
     */
    public void postInvoke(ComponentInvocation inv) {
        if (runAs != null && inv.isPreInvokeDone()) {
            final ComponentInvocation finv = inv;
            AppservAccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    SecurityContext.setCurrent(
                            (SecurityContext) finv.getOldSecurityContext());
                    return null;
                }
            });
        }
    }

    /**
     * Logs in a principal for run-as. This method is called if the
     * run-as principal is required. The user has already logged in -
     * now it needs to change to the new principal. In order that all
     * the correct permissions work - this method logs the new principal
     * with no password -generating valid credentials.
     */
    private void loginForRunAs() {
        AppservAccessController.doPrivileged(new PrivilegedAction() {
            public Object run() {
                LoginContextDriver.loginPrincipal(runAs.getPrincipal(), realmName);
                return null;
            }
        });
    }


    /**
     * This method returns a boolean value indicating whether or not the
     * caller is in the specified role.
     *
     * @param role role name in the form of java.lang.String
     * @return A boolean true/false depending on whether or not the caller
     *         has the specified role.
     */
    public boolean isCallerInRole(String role) {
        /* In case of Run As - Should check isCallerInRole with
       * respect to the old security context.
       */

        boolean ret = false;

        if (_logger.isLoggable(Level.FINE)) {
            _logger.entering("EJBSecurityManager", "isCallerInRole", role);

        }
        EJBRoleRefPermission ejbrr = new EJBRoleRefPermission(ejbName, role);
        
        SecurityContext sc;
        if (runAs != null) {
            ComponentInvocation ci = invMgr.getCurrentInvocation();
            sc = (SecurityContext) ci.getOldSecurityContext();
        } else {
            sc = SecurityContext.getCurrent();
        }
        Set principalSet = null;
        if (sc != null) principalSet = sc.getPrincipalSet();

        ProtectionDomain prdm = getCachedProtectionDomain(principalSet, true);

        String oldContextId = null;
        try {
            // set the policy context in the TLS.
            oldContextId = setPolicyContext(this.contextId);
            ret = policy.implies(prdm, ejbrr);
        } catch (SecurityException se) {
            _logger.log(Level.SEVERE, "jacc_is_caller_in_role_exception", se);
            ret = false;
        } catch (Throwable t) {
            _logger.log(Level.SEVERE, "jacc_is_caller_in_role_exception", t);
            ret = false;
        } finally {
            try {
                resetPolicyContext(oldContextId, this.contextId);
            } catch (Throwable ex) {
                _logger.log(Level.SEVERE, "jacc_policy_context_exception", ex);
                ret = false;
            }
        }

        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("JACC: isCallerInRole Result: " + ret + " EJBRoleRefPermission (Name) = " + ejbrr.getName() + " (Action) = " + ejbrr.getActions() + " (Codesource) = " + prdm.getCodeSource());
        }

        return ret;
    }

    /**
     * This method returns the Client Principal who initiated the current
     * Invocation.
     *
     * @return A Principal object of the client who made this invocation.
     *         or null if the SecurityContext has not been established by the client.
     */
    public Principal getCallerPrincipal() {
        SecurityContext sc = null;
        if (runAs != null) { // Run As
            /* return the principal associated with the old security
            * context
            */
            ComponentInvocation ci = invMgr.getCurrentInvocation();

            if (ci == null) {
                throw new InvocationException(); // 4646060
            }
            sc = (SecurityContext) ci.getOldSecurityContext();

        } else {
            // lets optimize a little. no need to look up oldsecctx
            // its the same as the new one
            sc = SecurityContext.getCurrent();
        }

        Principal prin;

        if (sc != null) {
            prin = sc.getCallerPrincipal();
        } else {
            prin = SecurityContext.getDefaultCallerPrincipal();
        }
        return prin;
    }

    public void destroy() {

        try {

            boolean wasInService = getPolicyFactory().inService(this.contextId);
            if (wasInService) {
                policy.refresh();
            }
	    /* 
	     * all ejbs of module share same policy context, but each has its own
	     * permission cache, which  must be unregistered from factory to
	     * avoid leak.
	     */
	    PermissionCacheFactory.removePermissionCache(uncheckedMethodPermissionCache);
            uncheckedMethodPermissionCache = null; 
            roleMapperFactory.removeAppNameForContext(this.contextId);

        } catch (PolicyContextException pce) {
            String msg = "ejbsm.could_not_delete";
            // Just log it.
            _logger.log(Level.WARNING, msg, pce);
        }
        probeProvider.securityManagerDestructionStartedEvent(ejbName);
        ejbSFM.getManager(contextId,ejbName,true);
        probeProvider.securityManagerDestructionEndedEvent(ejbName);
       
        probeProvider.securityManagerDestructionEvent(ejbName);
       
    }

    /**
     * This will return the subject associated with the current call. If the
     * run as subject is in effect. It will return that subject. This is done
     * to support the JACC specification which says if the runas principal is
     * in effect,  that principal should be used for making a component call.
     *
     * @return Subject the current subject. Null if this is not the run-as
     *         case
     */
    public Subject getCurrentSubject() {
        // just get the security context will return the empt subject
        // of the default securityContext when appropriate.
        return SecurityContext.getCurrent().getSubject();
    }

    /* This method is used by SecurityUtil runMethod to run the
     * action as the subject encapsulated in the current
     * SecurityContext.
     */
    public Object doAsPrivileged(PrivilegedExceptionAction pea)
            throws Throwable {

        SecurityContext sc = SecurityContext.getCurrent();
        Set principalSet = sc.getPrincipalSet();
        AccessControlContext acc =
                (AccessControlContext) accessControlContextCache.get(principalSet);

        if (acc == null) {
            final ProtectionDomain[] pdArray = new ProtectionDomain[1];
            pdArray[0] = getCachedProtectionDomain(principalSet, false);
            try {
                if (principalSet != null) {

                    final Subject s = sc.getSubject();

                    acc = (AccessControlContext)
                            AccessController.doPrivileged(new PrivilegedExceptionAction() {
                                public java.lang.Object run() throws Exception {
                                    return new AccessControlContext
                                            (new AccessControlContext(pdArray),
                                                    new SubjectDomainCombiner(s));
                                }
                            });
                } else {
                    acc = new AccessControlContext(pdArray);
                }

                // form a new key set so that it does not share with
                // cacheProtectionDomain and protectionDomainCache
                if (principalSet != null) {
                    accessControlContextCache.put(new HashSet(principalSet), acc);
                }

                _logger.fine("JACC: new AccessControlContext added to cache");

            } catch (Exception e) {
                _logger.log(Level.SEVERE,
                        "java_security.security_context_exception", e);
                acc = null;
                throw e;
            }
        }

        Object rvalue = null;
        String oldContextId = setPolicyContext(this.contextId);
        if (_logger.isLoggable(Level.FINE)) {
            _logger.fine("JACC: doAsPrivileged contextId(" + this.contextId + ")");
        }

        try {
            rvalue = AccessController.doPrivileged(pea, acc);
        } finally {
            resetPolicyContext(oldContextId, this.contextId);
        }
        return rvalue;
    }

    /**
     * Runs a business method of an EJB withint the bean's policy context.
     * The original policy context is restored after method execution.
     * This method should only be used by com.sun.enterprise.security.SecurityUtil.
     *
     * @param beanClassMethod the EJB business method
     * @param obj             the EJB bean instance
     * @param oa              parameters passed to beanClassMethod
     * @return return value from beanClassMethod
     * @throws java.lang.reflect.InvocationTargetException if the underlying method throws an exception
     * @throws Throwable                 other throwables in other cases
     */
    public Object runMethod(Method beanClassMethod, Object obj, Object[] oa)
            throws Throwable {
        String oldCtxID = setPolicyContext(this.contextId);
        Object ret = null;
        try {
            ret = beanClassMethod.invoke(obj, oa);
        } finally {
            resetPolicyContext(oldCtxID, this.contextId);
        }
        return ret;
    }

    private static void resetPolicyContext(final String newV, String oldV)
            throws Throwable {
        if (oldV != newV && newV != null && (oldV == null || !oldV.equals(newV))) {

            if (_logger.isLoggable(Level.FINE)) {
                _logger.fine("JACC: Changing Policy Context ID: oldV = "
                        + oldV + " newV = " + newV);
            }
            try {
                AppservAccessController.doPrivileged(new PrivilegedExceptionAction() {
                    public java.lang.Object run() throws Exception {
                        PolicyContext.setContextID(newV);
                        return null;
                    }
                });
            } catch (java.security.PrivilegedActionException pae) {
                Throwable cause = pae.getCause();
                if (cause instanceof java.security.AccessControlException) {
                    _logger.log(Level.SEVERE, "jacc_policy_context_security_exception", cause);
                } else {
                    _logger.log(Level.SEVERE, "jacc_policy_context_exception", cause);
                }
                throw cause;
            }
        }
    }

    private static String setPolicyContext(String newV) throws Throwable {
        String oldV = PolicyContext.getContextID();
        resetPolicyContext(newV, oldV);
        return oldV;
    }

    /**
     * This method is similiar to the runMethod, except it keeps the
     * semantics same as the one in reflection. On failure, if the
     * exception is caused due to reflection, it returns the
     * InvocationTargetException.  This method is called from the
     * containers for ejbTimeout, WebService and MDBs.
     *
     * @param beanClassMethod, the bean class method to be invoked
     * @param isLocal,         true if this invocation is through the local EJB view
     * @param o                the object on which this method is to be
     *                         invoked in this case the ejb,
     * @param oa               the parameters for the method,
     * @param c,               the container instance
     *                         can be a null value, where in the container will be queried to
     *                         find its security manager.
     * @return Object, the result of the execution of the method.
     */
    public Object invoke(Method beanClassMethod, boolean isLocal, Object o, Object[] oa)
            throws Throwable {

        final Method meth = beanClassMethod;
        final Object obj = o;
        final Object[] objArr = oa;
        Object ret = null;

        // Optimization.  Skip doAsPrivileged call if this is a local
        // invocation and the target ejb uses caller identity or the
        // System Security Manager is disabled.
        // Still need to execute it within the target bean's policy context.
        // see CR 6331550
        if ((isLocal && this.getUsesCallerIdentity()) ||
                System.getSecurityManager() == null) {
            ret = this.runMethod(meth, obj, objArr);
        } else {

            PrivilegedExceptionAction pea =
                    new PrivilegedExceptionAction() {
                        public java.lang.Object run() throws Exception {
                            return meth.invoke(obj, objArr);
                        }
                    };

            try {
                ret = this.doAsPrivileged(pea);
            } catch (PrivilegedActionException pae) {
                Throwable cause = pae.getCause();
                throw cause;
            }
        }
        return ret;
    }

    @Override
    public void resetPolicyContext() {
        if (System.getSecurityManager() == null) {
            ((PolicyContextHandlerImpl)PolicyContextHandlerImpl.getInstance()).reset();
            PolicyContext.setContextID(null);
            return;
        }
        
        try {
                AppservAccessController.doPrivileged(new PrivilegedExceptionAction() {
                    public java.lang.Object run() throws Exception {
                         ((PolicyContextHandlerImpl)PolicyContextHandlerImpl.getInstance()).
                                 reset();
                          PolicyContext.setContextID(null);
                        return null;
                    }
                });
            } catch (java.security.PrivilegedActionException pae) {
                Throwable cause = pae.getCause();
                if (cause instanceof java.security.AccessControlException) {
                    _logger.log(Level.SEVERE, "jacc_policy_context_security_exception", cause);
                } else {
                    _logger.log(Level.SEVERE, "jacc_policy_context_exception", cause);
                }
                throw new RuntimeException(cause);
            }
    }
   
}

Other Glassfish examples (source code examples)

Here is a short list of links related to this Glassfish EJBSecurityManager.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.