001: /**
002: * EasyBeans
003: * Copyright (C) 2006 Bull S.A.S.
004: * Contact: easybeans@ow2.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: PermissionManager.java 1970 2007-10-16 11:49:25Z benoitf $
023: * --------------------------------------------------------------------------
024: */package org.ow2.easybeans.security.permissions;
025:
026: import java.net.URL;
027: import java.security.CodeSource;
028: import java.security.Principal;
029: import java.security.ProtectionDomain;
030: import java.security.cert.Certificate;
031: import java.util.List;
032:
033: import javax.security.jacc.EJBMethodPermission;
034: import javax.security.jacc.EJBRoleRefPermission;
035: import javax.security.jacc.PolicyContext;
036: import javax.security.jacc.PolicyContextException;
037:
038: import org.ow2.easybeans.api.EZBPermissionManager;
039: import org.ow2.easybeans.api.EasyBeansInvocationContext;
040: import org.ow2.easybeans.api.PermissionManagerException;
041: import org.ow2.easybeans.api.bean.info.IBeanInfo;
042: import org.ow2.easybeans.api.bean.info.IEJBJarInfo;
043: import org.ow2.easybeans.api.bean.info.IMethodSecurityInfo;
044: import org.ow2.easybeans.api.bean.info.ISecurityInfo;
045: import org.ow2.easybeans.security.propagation.context.SecurityCurrent;
046: import org.ow2.util.log.Log;
047: import org.ow2.util.log.LogFactory;
048:
049: /**
050: * Permission manager for EJB.
051: * @author Florent Benoit
052: */
053: public class PermissionManager extends AbsPermissionManager implements
054: EZBPermissionManager {
055:
056: /**
057: * Logger.
058: */
059: private Log logger = LogFactory.getLog(PermissionManager.class);
060:
061: /**
062: * CodeSource.
063: */
064: private CodeSource codeSource = null;
065:
066: /**
067: * EJB-jar Info.
068: */
069: private IEJBJarInfo ejbJarInfo;
070:
071: /**
072: * Default Constructor.
073: * @param contextIdURL context ID used for PolicyContext
074: * @param ejbJarInfo the metadata on all the beans (runtime info)
075: * @throws PermissionManagerException if permissions can't be set
076: */
077: public PermissionManager(final URL contextIdURL,
078: final IEJBJarInfo ejbJarInfo)
079: throws PermissionManagerException {
080: super (contextIdURL);
081: this .ejbJarInfo = ejbJarInfo;
082: this .codeSource = new CodeSource(contextIdURL,
083: (Certificate[]) null);
084:
085: }
086:
087: /**
088: * 3.1.5 Translating EJB Deployment Descriptors<br>
089: * A reference to a PolicyConfiguration object must be obtained by calling
090: * the getPolicyConfiguration method on the PolicyConfigurationFactory
091: * implementation class of the provider configured into the container. The
092: * policy context identifier used in the call to getPolicyConfiguration must
093: * be a String that satisfies the requirements described in Section 3.1.4,
094: * EJB Policy Context Identifiers, on page 28. The value true must be passed
095: * as the second parameter in the call to getPolicyConfiguration to ensure
096: * that any and all policy statements are removed from the policy context
097: * associated with the returned PolicyConfiguration. The method-permission,
098: * exclude-list, and security-role-ref elements appearing in the deployment
099: * descriptor must be translated into permissions and added to the
100: * PolicyConfiguration object to yield an equivalent translation as that
101: * defined in the following sections and such that every EJB method for
102: * which the container performs pre-dispatch access decisions is implied by
103: * at least one permission resulting from the translation.
104: * @throws PermissionManagerException if permissions can't be set
105: */
106: public void translateMetadata() throws PermissionManagerException {
107: List<IBeanInfo> beansInfo = ejbJarInfo.getBeanInfos();
108: if (beansInfo != null) {
109: for (IBeanInfo beanInfo : beansInfo) {
110: ISecurityInfo securityInfo = beanInfo.getSecurityInfo();
111: translateEjbMethodPermission(securityInfo);
112: translateEjbExcludeList(securityInfo);
113: translateEjbSecurityRoleRef(beanInfo, securityInfo);
114: }
115: }
116: }
117:
118: /**
119: * 3.1.5.1 Translating EJB method-permission Elements<br>
120: * For each method element of each method-permission element, an
121: * EJBMethodPermission object translated from the method element must be
122: * added to the policy statements of the PolicyConfiguration object. The
123: * name of each such EJBMethodPermission object must be the ejb-name from
124: * the corresponding method element, and the actions must be established by
125: * translating the method element into a method specification according to
126: * the methodSpec syntax defined in the documentation of the
127: * EJBMethodPermission class. The actions translation must preserve the
128: * degree of specificity with respect to method-name, method-intf, and
129: * method-params inherent in the method element. If the method-permission
130: * element contains the unchecked element, then the deployment tools must
131: * call the addToUncheckedPolicy method to add the permissions resulting
132: * from the translation to the PolicyConfiguration object. Alternatively, if
133: * the method-permission element contains one or more role-name elements,
134: * then the deployment tools must call the addToRole method to add the
135: * permissions resulting from the translation to the corresponding roles of
136: * the PolicyConfiguration object.
137: * @param securityInfo the security info for a given bean.
138: * @throws PermissionManagerException if permissions can't be set
139: */
140: protected void translateEjbMethodPermission(
141: final ISecurityInfo securityInfo)
142: throws PermissionManagerException {
143: List<IMethodSecurityInfo> methodSecurityInfos = securityInfo
144: .getMethodSecurityInfos();
145: if (methodSecurityInfos != null) {
146: for (IMethodSecurityInfo methodSecurityInfo : methodSecurityInfos) {
147: if (methodSecurityInfo.isUnchecked()) {
148: try {
149: logger.debug("Adding unchecked permission {0}",
150: methodSecurityInfo.getPermission());
151: getPolicyConfiguration().addToUncheckedPolicy(
152: methodSecurityInfo.getPermission());
153: } catch (PolicyContextException e) {
154: throw new PermissionManagerException(
155: "Cannot add unchecked policy for method '"
156: + methodSecurityInfo + "'.", e);
157: }
158: } else {
159: for (String roleName : methodSecurityInfo
160: .getRoles()) {
161: try {
162: logger
163: .debug(
164: "Adding permission {0} to role {1}",
165: methodSecurityInfo
166: .getPermission(),
167: roleName);
168: getPolicyConfiguration().addToRole(
169: roleName,
170: methodSecurityInfo.getPermission());
171: } catch (PolicyContextException e) {
172: throw new PermissionManagerException(
173: "Cannot add rolebase policy for method '"
174: + methodSecurityInfo
175: + "' and for role '"
176: + roleName + "'.", e);
177: }
178: }
179: }
180: }
181: }
182: }
183:
184: /**
185: * 3.1.5.2 Translating the EJB exclude-list<br>
186: * An EJBMethodPermission object must be created for each method element
187: * occurring in the exclude-list element of the deployment descriptor. The
188: * name and actions of each EJBMethodPermission must be established as
189: * described in Section 3.1.5.1, Translating EJB method-permission Elements.
190: * The deployment tools must use the addToExcludedPolicy method to add the
191: * EJBMethodPermission objects resulting from the translation of the
192: * exclude-list to the excluded policy statements of the PolicyConfiguration
193: * object.
194: * @param securityInfo the security info for a given bean.
195: * @throws PermissionManagerException if permissions can't be set
196: */
197: protected void translateEjbExcludeList(
198: final ISecurityInfo securityInfo)
199: throws PermissionManagerException {
200: List<IMethodSecurityInfo> methodSecurityInfos = securityInfo
201: .getMethodSecurityInfos();
202: if (methodSecurityInfos != null) {
203: for (IMethodSecurityInfo methodSecurityInfo : methodSecurityInfos) {
204: if (methodSecurityInfo.isExcluded()) {
205: try {
206: logger.debug("Adding excluded permission {0}",
207: methodSecurityInfo.getPermission());
208: getPolicyConfiguration().addToExcludedPolicy(
209: methodSecurityInfo.getPermission());
210: } catch (PolicyContextException e) {
211: throw new PermissionManagerException(
212: "Cannot add excluded policy for method '"
213: + methodSecurityInfo + "'.", e);
214: }
215: }
216: }
217: }
218: }
219:
220: /**
221: * 3.1.5.3 Translating EJB security-role-ref Elements<br>
222: * For each security-role-ref element appearing in the deployment
223: * descriptor, a corresponding EJBRoleRefPermission must be created. The
224: * name of each EJBRoleRefPermission must be obtained as described for
225: * EJBMethodPermission objects. The actions used to construct the permission
226: * must be the value of the role-name (that is the reference), appearing in
227: * the security-role-ref. The deployment tools must call the addToRole
228: * method on the PolicyConfiguration object to add a policy statement
229: * corresponding to the EJBRoleRefPermission to the role identified in the
230: * rolelink appearing in the security-role-ref.
231: * @param beanInfo info about the bean.
232: * @param securityInfo the security info for a given bean.
233: * @throws PermissionManagerException if permissions can't be set
234: */
235: public void translateEjbSecurityRoleRef(final IBeanInfo beanInfo,
236: final ISecurityInfo securityInfo)
237: throws PermissionManagerException {
238: List<String> declaredRoles = securityInfo.getDeclaredRoles();
239: if (declaredRoles != null) {
240: for (String role : declaredRoles) {
241: try {
242: getPolicyConfiguration().addToRole(
243: role,
244: new EJBRoleRefPermission(
245: beanInfo.getName(), role));
246: } catch (PolicyContextException e) {
247: throw new PermissionManagerException(
248: "Cannot add to role '" + role
249: + "' an EJBRoleRefPermission.", e);
250: }
251: }
252: }
253: }
254:
255: /**
256: * Checks the security for the given invocation context.
257: * @param invocationContext the context to check.
258: * @param runAsBean if true, the bean is a run-as bean.
259: * @return true if the access has been granted, else false.
260: */
261: public boolean checkSecurity(
262: final EasyBeansInvocationContext invocationContext,
263: final boolean runAsBean) {
264: PolicyContext.setContextID(getContextId());
265:
266: // Build Protection Domain with a codesource and array of principal
267: // Get roles.
268: Principal[] principals = SecurityCurrent.getCurrent()
269: .getSecurityContext().getCallerRoles(runAsBean);
270: ProtectionDomain protectionDomain = new ProtectionDomain(
271: codeSource, null, null, principals);
272:
273: boolean accessOK = getPolicy().implies(protectionDomain,
274: invocationContextToMethodPermission(invocationContext));
275: if (logger.isDebugEnabled()) {
276: logger.debug("Policy.implies result = {0} ", Boolean
277: .valueOf(accessOK));
278: }
279: return accessOK;
280: }
281:
282: /**
283: * Gets a EJBMethodPermission from an invocation context.
284: * @param invocationContext the context containing data on the current
285: * invocation.
286: * @return a Method Permission for the current method.
287: */
288: private static EJBMethodPermission invocationContextToMethodPermission(
289: final EasyBeansInvocationContext invocationContext) {
290: // TODO : cache ejbName/methodSignature to avoid creation of a new
291: // EJBMethodPermission each time
292: // See JACC 4.12
293:
294: // TODO: Fix Remote/Local method-itf parameter. (set to "" for now)
295: EJBMethodPermission ejbMethodPermission = new EJBMethodPermission(
296: invocationContext.getFactory().getBeanInfo().getName(),
297: "", invocationContext.getMethod());
298:
299: return ejbMethodPermission;
300: }
301:
302: /**
303: * Test if the caller has a given role. EJBRoleRefPermission object must be
304: * created with ejbName and actions equal to roleName<br/>
305: * See section 4.3.2 of JACC
306: * @param ejbName The name of the EJB on wich look role
307: * @param roleName The name of the security role. The role must be one of
308: * the security-role-ref that is defined in the deployment
309: * descriptor.
310: * @param inRunAs bean calling this method is running in run-as mode or not ?
311: * @return True if the caller has the specified role.
312: */
313: public boolean isCallerInRole(final String ejbName,
314: final String roleName, final boolean inRunAs) {
315: PolicyContext.setContextID(getContextId());
316: logger.debug("roleName = {0}", roleName);
317:
318: // Build Protection Domain with a codesource and array of principals
319: Principal[] principals = SecurityCurrent.getCurrent()
320: .getSecurityContext().getCallerRoles(inRunAs);
321: ProtectionDomain protectionDomain = new ProtectionDomain(
322: codeSource, null, null, principals);
323:
324: // TODO :add cache mechanism ?
325: // See JACC 4.12
326: EJBRoleRefPermission ejbRoleRefPermission = new EJBRoleRefPermission(
327: ejbName, roleName);
328: boolean isInRole = getPolicy().implies(protectionDomain,
329: ejbRoleRefPermission);
330: return isInRole;
331:
332: }
333:
334: }
|