001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 2004 Bull S.A.
004: * Contact: jonas-team@objectweb.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: * Initial developer: Florent BENOIT
022: * --------------------------------------------------------------------------
023: * $Id: PermissionManager.java 7111 2005-07-26 17:17:46Z benoitf $
024: * --------------------------------------------------------------------------
025: */package org.objectweb.jonas_ejb.container;
026:
027: import java.net.URI;
028: import java.net.URL;
029: import java.security.CodeSource;
030: import java.security.PermissionCollection;
031: import java.security.Principal;
032: import java.security.ProtectionDomain;
033: import java.security.cert.Certificate;
034: import java.util.Iterator;
035:
036: import javax.security.jacc.EJBMethodPermission;
037: import javax.security.jacc.EJBRoleRefPermission;
038: import javax.security.jacc.PolicyContext;
039: import javax.security.jacc.PolicyContextException;
040:
041: import org.objectweb.jonas_ejb.deployment.api.BeanDesc;
042: import org.objectweb.jonas_ejb.deployment.api.DeploymentDesc;
043: import org.objectweb.jonas_ejb.deployment.api.ExcludeListDesc;
044: import org.objectweb.jonas_ejb.deployment.api.MethodPermissionDesc;
045: import org.objectweb.jonas_ejb.lib.EJBInvocation;
046:
047: import org.objectweb.jonas_lib.deployment.api.SecurityRoleRefDesc;
048: import org.objectweb.jonas_lib.security.AbsPermissionManager;
049: import org.objectweb.jonas_lib.security.PermissionManagerException;
050:
051: import org.objectweb.jonas.security.jacc.JPolicyContextHandlerCurrent;
052: import org.objectweb.jonas.security.jacc.JPolicyContextHandlerData;
053:
054: import org.objectweb.security.context.SecurityContext;
055: import org.objectweb.security.context.SecurityCurrent;
056:
057: import org.objectweb.util.monolog.api.BasicLevel;
058:
059: /**
060: * Defines a PermissionManager class which will manage JACC permissions for an
061: * ejbjar
062: * @author Florent Benoit
063: */
064: public class PermissionManager extends AbsPermissionManager {
065:
066: /**
067: * Deployment desc of the module
068: */
069: private DeploymentDesc ejbDeploymentDesc = null;
070:
071: /**
072: * Default Constructor
073: * @param ejbDeploymentDesc EJB deployment Descriptor
074: * @param contextId context ID used for PolicyContext
075: * @throws PermissionManagerException if permissions can't be set
076: */
077: public PermissionManager(DeploymentDesc ejbDeploymentDesc,
078: String contextId) throws PermissionManagerException {
079: super (contextId);
080: this .ejbDeploymentDesc = ejbDeploymentDesc;
081: }
082:
083: /**
084: * 3.1.5 Translating EJB Deployment Descriptors A reference to a
085: * PolicyConfiguration object must be obtained by calling the
086: * getPolicyConfiguration method on the PolicyConfigurationFactory
087: * implementation class of the provider configured into the container. The
088: * policy context identifier used in the call to getPolicyConfiguration must
089: * be a String that satisfies the requirements described in Section 3.1.4,
090: * EJB Policy Context Identifiers, on page 28. The value true must be passed
091: * as the second parameter in the call to getPolicyConfiguration to ensure
092: * that any and all policy statements are removed from the policy context
093: * associated with the returned PolicyConfiguration. The method-permission,
094: * exclude-list, and security-role-ref elements appearing in the deployment
095: * descriptor must be translated into permissions and added to the
096: * PolicyConfiguration object to yield an equivalent translation as that
097: * defined in the following sections and such that every EJB method for
098: * which the container performs pre-dispatch access decisions is implied by
099: * at least one permission resulting from the translation.
100: * @throws PermissionManagerException if permissions can't be set
101: */
102: public void translateEjbDeploymentDescriptor()
103: throws PermissionManagerException {
104: translateEjbMethodPermission();
105: translateEjbExcludeList();
106: translateEjbSecurityRoleRef();
107: }
108:
109: /**
110: * 3.1.5.1 Translating EJB method-permission Elements For each method
111: * element of each method-permission element, an EJBMethodPermission object
112: * translated from the method element must be added to the policy statements
113: * of the PolicyConfiguration object. The name of each such
114: * EJBMethodPermission object must be the ejb-name from the corresponding
115: * method element, and the actions must be established by translating the
116: * method element into a method specification according to the methodSpec
117: * syntax defined in the documentation of the EJBMethodPermission class. The
118: * actions translation must preserve the degree of specificity with respect
119: * to method-name, method-intf, and method-params inherent in the method
120: * element. If the method-permission element contains the unchecked element,
121: * then the deployment tools must call the addToUncheckedPolicy method to
122: * add the permissions resulting from the translation to the
123: * PolicyConfiguration object. Alternatively, if the method-permission
124: * element contains one or more role-name elements, then the deployment
125: * tools must call the addToRole method to add the permissions resulting
126: * from the translation to the corresponding roles of the
127: * PolicyConfiguration object.
128: * @throws PermissionManagerException if permissions can't be set
129: */
130: protected void translateEjbMethodPermission()
131: throws PermissionManagerException {
132: if (ejbDeploymentDesc == null
133: || getPolicyConfiguration() == null) {
134: throw new PermissionManagerException(
135: "PolicyConfiguration or ejbDeploymentDesc is null");
136: }
137:
138: MethodPermissionDesc methodPermissionDesc = null;
139: PermissionCollection permissionCollection = null;
140:
141: for (Iterator it = ejbDeploymentDesc
142: .getMethodPermissionsDescList().iterator(); it
143: .hasNext();) {
144: methodPermissionDesc = (MethodPermissionDesc) it.next();
145: permissionCollection = methodPermissionDesc
146: .getEJBMethodPermissions();
147: try {
148: // unchecked or roles
149: if (methodPermissionDesc.isUnchecked()) {
150: getPolicyConfiguration().addToUncheckedPolicy(
151: permissionCollection);
152: } else {
153: for (Iterator rolesIt = methodPermissionDesc
154: .getRoleNameList().iterator(); rolesIt
155: .hasNext();) {
156: getPolicyConfiguration().addToRole(
157: (String) rolesIt.next(),
158: permissionCollection);
159: }
160: }
161: } catch (PolicyContextException pce) {
162: throw new PermissionManagerException(
163: "Can not add add excluded policy", pce);
164: }
165: }
166: }
167:
168: /**
169: * 3.1.5.2 Translating the EJB exclude-list An EJBMethodPermission object
170: * must be created for each method element occurring in the exclude-list
171: * element of the deployment descriptor. The name and actions of each
172: * EJBMethodPermission must be established as described in Section 3.1.5.1,
173: * Translating EJB method-permission Elements. The deployment tools must use
174: * the addToExcludedPolicy method to add the EJBMethodPermission objects
175: * resulting from the translation of the exclude-list to the excluded policy
176: * statements of the PolicyConfiguration object.
177: * @throws PermissionManagerException if permissions can't be set
178: */
179: protected void translateEjbExcludeList()
180: throws PermissionManagerException {
181: if (ejbDeploymentDesc == null
182: || getPolicyConfiguration() == null) {
183: throw new PermissionManagerException(
184: "PolicyConfiguration or ejbDeploymentDesc is null");
185: }
186: ExcludeListDesc excludeListDesc = ejbDeploymentDesc
187: .getExcludeListDesc();
188: if (excludeListDesc != null) {
189: try {
190: getPolicyConfiguration().addToExcludedPolicy(
191: excludeListDesc.getEJBMethodPermissions());
192: } catch (PolicyContextException pce) {
193: throw new PermissionManagerException(
194: "Can not add add excluded policy", pce);
195: }
196: }
197: }
198:
199: /**
200: * 3.1.5.3 Translating EJB security-role-ref Elements For each
201: * security-role-ref element appearing in the deployment descriptor, a
202: * corresponding EJBRoleRefPermission must be created. The name of each
203: * EJBRoleRefPermission must be obtained as described for
204: * EJBMethodPermission objects. The actions used to construct the permission
205: * must be the value of the role-name (that is the reference), appearing in
206: * the security-role-ref. The deployment tools must call the addToRole
207: * method on the PolicyConfiguration object to add a policy statement
208: * corresponding to the EJBRoleRefPermission to the role identified in the
209: * rolelink appearing in the security-role-ref.
210: * @throws PermissionManagerException if permissions can't be set
211: */
212: public void translateEjbSecurityRoleRef()
213: throws PermissionManagerException {
214: if (ejbDeploymentDesc == null
215: || getPolicyConfiguration() == null) {
216: throw new PermissionManagerException(
217: "PolicyConfiguration or ejbDeploymentDesc is null");
218: }
219:
220: SecurityRoleRefDesc securityRoleRefDesc = null;
221: BeanDesc beanDesc = null;
222:
223: // Add EJBRoleRefPermission for each bean
224: for (Iterator itEjb = ejbDeploymentDesc.getBeanDescIterator(); itEjb
225: .hasNext();) {
226: beanDesc = (BeanDesc) itEjb.next();
227: for (Iterator it = beanDesc.getSecurityRoleRefDescList()
228: .iterator(); it.hasNext();) {
229: securityRoleRefDesc = (SecurityRoleRefDesc) it.next();
230: try {
231: getPolicyConfiguration().addToRole(
232: securityRoleRefDesc.getRoleLink(),
233: securityRoleRefDesc
234: .getEJBRoleRefPermission());
235: } catch (PolicyContextException pce) {
236: throw new PermissionManagerException(
237: "Can not add add excluded policy", pce);
238: }
239: }
240: }
241: }
242:
243: /**
244: * Check the security for a given EJB signature method and for an EJB
245: * @param ejbName name of the EJB
246: * @param ejbInv object containing security signature of the method, args of
247: * method, etc
248: * @param inRunAs bean calling this method is running in run-as mode or not ?
249: * @return true if access to specific method is granted, else false.
250: */
251: public boolean checkSecurity(String ejbName, EJBInvocation ejbInv,
252: boolean inRunAs) {
253: try {
254: PolicyContext.setContextID(getContextId());
255: String methodSignature = ejbInv.methodPermissionSignature;
256:
257: if (TraceEjb.isDebugSecurity()) {
258: TraceEjb.security.log(BasicLevel.DEBUG, "EjbName = "
259: + ejbName + ", methodSignature = "
260: + methodSignature);
261: }
262:
263: // Set the information for the Policy provider
264: JPolicyContextHandlerData jPolicyContextHandlerData = JPolicyContextHandlerCurrent
265: .getCurrent().getJPolicyContextHandlerData();
266: if (jPolicyContextHandlerData == null) {
267: TraceEjb.security.log(BasicLevel.ERROR,
268: "The Handler data retrieved is null !");
269: return false;
270: }
271: jPolicyContextHandlerData.setEjbArguments(ejbInv.arguments);
272: jPolicyContextHandlerData.setProcessingBean(ejbInv.bean);
273:
274: PolicyContext.setHandlerData(jPolicyContextHandlerData);
275:
276: // Build Protection Domain with a codesource and array of principal
277: URI uri = new URI("file://" + getContextId());
278: CodeSource codesource = new CodeSource(new URL(uri
279: .toString()), (Certificate[]) null);
280: SecurityCurrent current = SecurityCurrent.getCurrent();
281: final SecurityContext sctx = current.getSecurityContext();
282: if (TraceEjb.isDebugSecurity()) {
283: TraceEjb.security.log(BasicLevel.DEBUG,
284: "Security Context = " + sctx);
285: if (sctx != null) {
286: TraceEjb.security
287: .log(
288: BasicLevel.DEBUG,
289: "sctx.getCallerPrincipalRoles() = "
290: + sctx
291: .getCallerPrincipalRoles(inRunAs));
292: }
293: }
294:
295: String runAsRole = null;
296:
297: Principal[] principals = null;
298: String[] runAsPrincipalRoles = null;
299: String[] principalRoles = null;
300: if (sctx != null) {
301: synchronized (sctx) {
302: runAsRole = sctx.peekRunAsRole();
303: runAsPrincipalRoles = sctx
304: .peekRunAsPrincipalRoles();
305: principalRoles = sctx
306: .getCallerPrincipalRoles(inRunAs);
307: }
308: if (runAsRole != null) {
309: principals = new Principal[runAsPrincipalRoles.length];
310: for (int k = 0; k < runAsPrincipalRoles.length; k++) {
311: principals[k] = new org.objectweb.jonas.security.auth.JPrincipal(
312: runAsPrincipalRoles[k]);
313: }
314: } else {
315: principals = new Principal[principalRoles.length];
316: for (int k = 0; k < principalRoles.length; k++) {
317: principals[k] = new org.objectweb.jonas.security.auth.JPrincipal(
318: principalRoles[k]);
319: }
320: }
321: } else {
322: if (TraceEjb.isDebugSecurity()) {
323: TraceEjb.security.log(BasicLevel.DEBUG,
324: "Security context is null");
325: }
326: }
327: ProtectionDomain protectionDomain = new ProtectionDomain(
328: codesource, null, null, principals);
329:
330: //TODO : cache ejbName/methodSignature to avoid creation of a new
331: // EJBMethodPermission each time
332: // See JACC 4.12
333: EJBMethodPermission ejbMethodPermission = new EJBMethodPermission(
334: ejbName, methodSignature);
335: boolean accessOK = getPolicy().implies(protectionDomain,
336: ejbMethodPermission);
337: if (TraceEjb.isDebugSecurity()) {
338: TraceEjb.security.log(BasicLevel.DEBUG,
339: "Policy.implies result = " + accessOK);
340: }
341: jPolicyContextHandlerData = null;
342: return accessOK;
343:
344: } catch (Exception e) {
345: TraceEjb.security.log(BasicLevel.ERROR,
346: "Cannot check security", e);
347: return false;
348: }
349:
350: }
351:
352: /**
353: * Test if the caller has a given role. EJBRoleRefPermission object must be
354: * created with ejbName and actions equal to roleName
355: * @see section 4.3.2 of JACC
356: * @param ejbName The name of the EJB on wich look role
357: * @param roleName The name of the security role. The role must be one of
358: * the security-role-ref that is defined in the deployment
359: * descriptor.
360: * @param inRunAs bean calling this method is running in run-as mode or not ?
361: * @return True if the caller has the specified role.
362: */
363: public boolean isCallerInRole(String ejbName, String roleName,
364: boolean inRunAs) {
365: try {
366: PolicyContext.setContextID(getContextId());
367: if (TraceEjb.isDebugSecurity()) {
368: TraceEjb.security.log(BasicLevel.DEBUG, "roleName = "
369: + roleName);
370: }
371:
372: // Build Protection Domain with a codesource and array of principal
373: URI uri = new URI("file://" + getContextId());
374: CodeSource codesource = new CodeSource(new URL(uri
375: .toString()), (Certificate[]) null);
376: SecurityCurrent current = SecurityCurrent.getCurrent();
377: final SecurityContext sctx = current.getSecurityContext();
378: if (TraceEjb.isDebugSecurity()) {
379: TraceEjb.security.log(BasicLevel.DEBUG,
380: "Security Context = " + sctx);
381: TraceEjb.security
382: .log(
383: BasicLevel.DEBUG,
384: "sctx.getCallerPrincipalRoles() = "
385: + sctx
386: .getCallerPrincipalRoles(inRunAs));
387: }
388: Principal[] principals = null;
389: if (sctx != null) {
390: principals = new Principal[sctx
391: .getCallerPrincipalRoles(inRunAs).length];
392: for (int k = 0; k < sctx
393: .getCallerPrincipalRoles(inRunAs).length; k++) {
394: principals[k] = new org.objectweb.jonas.security.auth.JPrincipal(
395: sctx.getCallerPrincipalRoles(inRunAs)[k]);
396: }
397: } else {
398: if (TraceEjb.isDebugSecurity()) {
399: TraceEjb.security.log(BasicLevel.DEBUG,
400: "Security context is null");
401: }
402: }
403: ProtectionDomain protectionDomain = new ProtectionDomain(
404: codesource, null, null, principals);
405:
406: //TODO :add cache mechanism
407: // See JACC 4.12
408: EJBRoleRefPermission ejbRoleRefPermission = new EJBRoleRefPermission(
409: ejbName, roleName);
410: boolean isInRole = getPolicy().implies(protectionDomain,
411: ejbRoleRefPermission);
412: if (TraceEjb.isDebugSecurity()) {
413: TraceEjb.security.log(BasicLevel.DEBUG,
414: "Policy.implies result = " + isInRole);
415: }
416: return isInRole;
417:
418: } catch (Exception e) {
419: TraceEjb.security.log(BasicLevel.ERROR,
420: "Cannot check isCallerInRole", e);
421: return false;
422: }
423:
424: }
425:
426: /**
427: * Reset Deployment Descriptor
428: */
429: protected void resetDeploymentDesc() {
430: ejbDeploymentDesc = null;
431: }
432:
433: }
|