001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022:
023: package org.jboss.ejb3.security;
024:
025: import java.lang.reflect.Method;
026: import java.lang.reflect.Modifier;
027: import java.security.CodeSource;
028: import java.security.Policy;
029: import java.security.Principal;
030: import java.security.ProtectionDomain;
031: import java.util.Set;
032:
033: import javax.annotation.security.DeclareRoles;
034: import javax.annotation.security.DenyAll;
035: import javax.annotation.security.PermitAll;
036: import javax.annotation.security.RolesAllowed;
037: import javax.ejb.EJBAccessException;
038: import javax.security.auth.Subject;
039: import javax.security.jacc.EJBMethodPermission;
040: import javax.security.jacc.EJBRoleRefPermission;
041: import javax.security.jacc.PolicyConfiguration;
042: import javax.security.jacc.PolicyConfigurationFactory;
043: import javax.security.jacc.PolicyContextException;
044: import org.jboss.annotation.security.SecurityDomain;
045: import org.jboss.aop.metadata.SimpleClassMetaDataBinding;
046: import org.jboss.aop.metadata.SimpleClassMetaDataLoader;
047: import org.jboss.deployment.DeploymentInfo;
048: import org.jboss.ejb3.EJBContainer;
049: import org.jboss.logging.Logger; //import org.jboss.deployers.spi.deployer.DeploymentUnit;
050: import org.jboss.security.RealmMapping;
051: import org.jboss.security.RunAsIdentity;
052:
053: /**
054: * JACC Helper class that created permissions as well as done the checks
055: * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
056: * @author Anil.Saldhana@jboss.com
057: * @version $Revision$
058: */
059: public class JaccHelper {
060: static Logger log = Logger.getLogger(JaccHelper.class);
061:
062: /**
063: * Creates a new policy configuration on (re)deployment. Context ID used is based on
064: * name of app, so we make sure we clean out any existing policy with that id.
065: */
066: public static PolicyConfiguration initialiseJacc(String contextID)
067: throws Exception {
068: log.trace("Initialising JACC Context for deployment: "
069: + contextID);
070: PolicyConfigurationFactory pcFactory = Ejb3PolicyConfigurationFactory
071: .getPolicyConfigurationFactory();
072: boolean removeExistingContext = true;
073: PolicyConfiguration pc = pcFactory.getPolicyConfiguration(
074: contextID, removeExistingContext);
075:
076: /*Set keys = PolicyContext.getHandlerKeys();
077: if (!keys.contains(EnterpriseBeanPolicyContextHandler.EJB_CONTEXT_KEY))
078: {
079: EnterpriseBeanPolicyContextHandler beanHandler = new EnterpriseBeanPolicyContextHandler();
080: PolicyContext.registerHandler(EnterpriseBeanPolicyContextHandler.EJB_CONTEXT_KEY,
081: beanHandler, false);
082: }
083: */
084: //Do I need this?
085: /*BeanMetaDataPolicyContextHandler metadataHandler = new BeanMetaDataPolicyContextHandler();
086: PolicyContext.registerHandler(BeanMetaDataPolicyContextHandler.METADATA_CONTEXT_KEY,
087: metadataHandler, false);*/
088: /*
089: if (!keys.contains(EJBArgsPolicyContextHandler.EJB_ARGS_KEY))
090: {
091: EJBArgsPolicyContextHandler argsHandler = new EJBArgsPolicyContextHandler();
092: PolicyContext.registerHandler(EJBArgsPolicyContextHandler.EJB_ARGS_KEY,
093: argsHandler, false);
094: }
095: */
096: return pc;
097: }
098:
099: // FIXME: NYI
100: // public static void putJaccInService(PolicyConfiguration pc, DeploymentUnit di) throws Exception
101: // {
102: // //TODO: How do we link this with the parent PC?
103: // pc.commit();
104: // }
105:
106: public static void putJaccInService(PolicyConfiguration pc,
107: DeploymentInfo di) throws Exception {
108: di.context.put("javax.security.jacc.PolicyConfiguration", pc);
109:
110: // Link this to the parent PC
111: DeploymentInfo current = di;
112: while (current.parent != null) {
113: current = current.parent;
114: }
115:
116: PolicyConfiguration parentPC = (PolicyConfiguration) current.context
117: .get("javax.security.jacc.PolicyConfiguration");
118:
119: if (parentPC != null && parentPC != pc) {
120: parentPC.linkConfiguration(pc);
121: }
122:
123: pc.commit();
124: log
125: .trace("JACC Policy Configuration for deployment has been put in service");
126: }
127:
128: public static void unregisterJacc(String contextID)
129: throws Exception {
130: PolicyConfigurationFactory pcFactory = Ejb3PolicyConfigurationFactory
131: .getPolicyConfigurationFactory();
132: PolicyConfiguration pc = pcFactory.getPolicyConfiguration(
133: contextID, true);
134: pc.delete();
135: }
136:
137: public static void configureContainer(String jaccContextId,
138: EJBContainer container) {
139: try {
140: addJaccContextToContainer(jaccContextId, container);
141: PolicyConfigurationFactory pcFactory = Ejb3PolicyConfigurationFactory
142: .getPolicyConfigurationFactory();
143: PolicyConfiguration pc = pcFactory.getPolicyConfiguration(
144: jaccContextId, false);
145:
146: addPermissions(container, pc);
147: } catch (Exception e) {
148: throw new RuntimeException(e);
149: }
150: }
151:
152: private static void addPermissions(EJBContainer container,
153: PolicyConfiguration pc) {
154: SecurityDomain sd = (SecurityDomain) container
155: .resolveAnnotation(SecurityDomain.class);
156:
157: PermitAll beanUnchecked = (PermitAll) container
158: .resolveAnnotation(PermitAll.class);
159: RolesAllowed beanPermissions = (RolesAllowed) container
160: .resolveAnnotation(RolesAllowed.class);
161:
162: DeclareRoles beanDeclareRolesPerms = (DeclareRoles) container
163: .resolveAnnotation(DeclareRoles.class);
164:
165: if (beanUnchecked != null && beanPermissions != null) {
166: throw new RuntimeException(
167: "Cannot annotate a bean with both @Unchecked and @MethodPermissions");
168: }
169:
170: String ejbName = container.getEjbName();
171:
172: //Add the security role references
173: if (beanDeclareRolesPerms != null) {
174: String[] rolerefs = beanDeclareRolesPerms.value();
175: int len = rolerefs != null ? rolerefs.length : 0;
176: for (int i = 0; i < len; i++) {
177: try {
178: pc.addToRole(rolerefs[i], new EJBRoleRefPermission(
179: ejbName, rolerefs[i]));
180: } catch (PolicyContextException e) {
181: throw new RuntimeException(e);
182: }
183: }
184: }
185:
186: //Am I iterating over the right thing here? Should I be using the stuff from
187: //Advisor.methodInterceptors instead?
188: Method[] methods = container.getBeanClass()
189: .getDeclaredMethods();
190: for (int i = 0; i < methods.length; i++) {
191: Method m = methods[i];
192: if (!Modifier.isPublic(m.getModifiers())) {
193: continue;
194: }
195:
196: EJBMethodPermission permission = new EJBMethodPermission(
197: ejbName, null, m);
198: log.trace("Creating permission: " + permission);
199:
200: PermitAll unchecked = (PermitAll) container
201: .resolveAnnotation(m, PermitAll.class);
202: RolesAllowed permissions = (RolesAllowed) container
203: .resolveAnnotation(m, RolesAllowed.class);
204: DenyAll exclude = (DenyAll) container.resolveAnnotation(m,
205: DenyAll.class);
206:
207: int annotationCount = getAnnotationCount(unchecked,
208: permissions, exclude);
209:
210: if (annotationCount == 0 && beanPermissions == null
211: && beanUnchecked == null) {
212: //continue;
213: //EJBTHREE-755:Add to unchecked if there are no annotations
214: try {
215: pc.addToUncheckedPolicy(permission);
216: } catch (PolicyContextException e) {
217: throw new RuntimeException(e);
218: }
219: } else if (annotationCount > 1) {
220: throw new RuntimeException(
221: "You can only use one of @PermitAll, @DenyAll or @RolesAllowed per method");
222: }
223:
224: try {
225: //Method level annotations override the bean level annotations
226: if (unchecked != null) {
227: pc.addToUncheckedPolicy(permission);
228: log.trace("Adding permission to unchecked policy");
229: continue;
230: }
231: if (permissions != null) {
232: addToRole(pc, permission, permissions);
233: continue;
234: }
235: if (exclude != null) {
236: pc.addToExcludedPolicy(permission);
237: log.trace("Adding permission to excluded policy");
238: continue;
239: }
240:
241: if (beanUnchecked != null) {
242: pc.addToUncheckedPolicy(permission);
243: log.trace("Adding permission to unchecked policy");
244: continue;
245: }
246: if (beanPermissions != null) {
247: addToRole(pc, permission, beanPermissions);
248: continue;
249: }
250:
251: //The default is unchecked
252: pc.addToUncheckedPolicy(permission);
253: log.trace("Adding permission to unchecked policy");
254: } catch (PolicyContextException e) {
255: throw new RuntimeException(e);
256: }
257: }
258: }
259:
260: private static int getAnnotationCount(PermitAll u, RolesAllowed mp,
261: DenyAll e) {
262: int annotations = 0;
263: if (u != null)
264: annotations++;
265: if (mp != null)
266: annotations++;
267: if (e != null)
268: annotations++;
269:
270: return annotations;
271: }
272:
273: private static void addToRole(PolicyConfiguration pc,
274: EJBMethodPermission p, RolesAllowed mp)
275: throws PolicyContextException {
276: String[] roles = mp.value();
277: for (int i = 0; i < roles.length; i++) {
278: pc.addToRole(roles[i], p);
279: log.trace("Adding permission to role: " + roles[i]);
280: }
281: }
282:
283: private static void addJaccContextToContainer(String jaccContextId,
284: EJBContainer container) {
285: SimpleClassMetaDataLoader loader = SimpleClassMetaDataLoader.singleton;
286: String name = container.getBeanClassName();
287: SimpleClassMetaDataBinding jaccCtx = new SimpleClassMetaDataBinding(
288: loader, name, JaccAuthorizationInterceptor.JACC,
289: container.getBeanClassName());
290:
291: jaccCtx.addDefaultMetaData(JaccAuthorizationInterceptor.JACC,
292: JaccAuthorizationInterceptor.CTX, jaccContextId);
293:
294: container.addClassMetaData(jaccCtx);
295: }
296:
297: public static void checkPermission(CodeSource ejbCS,
298: EJBMethodPermission methodPerm, RealmMapping realmMapping)
299: throws EJBAccessException {
300: try {
301: Policy policy = Policy.getPolicy();
302: // Get the caller
303: Subject caller = SecurityActions.getContextSubject();
304:
305: RunAsIdentity rai = SecurityActions.peekRunAsIdentity();
306:
307: Principal[] principals = null;
308: if (rai != null) {
309: Set runAsRoles = rai.getRunAsRoles();
310: principals = new Principal[runAsRoles.size()];
311: runAsRoles.toArray(principals);
312: } else {
313: /*if (caller != null)
314: {
315: // Get the caller principals
316: Set principalsSet = caller.getPrincipals();
317: principals = new Principal[principalsSet.size()];
318: principalsSet.toArray(principals);
319: }*/
320: //Get the current roles from the Authorization Manager
321: Principal callerP = SecurityActions
322: .getCallerPrincipal();
323: Set principalSet = realmMapping.getUserRoles(callerP);
324: principals = new Principal[principalSet.size()];
325: principalSet.toArray(principals);
326: }
327:
328: ProtectionDomain pd = new ProtectionDomain(ejbCS, null,
329: null, principals);
330: if (policy.implies(pd, methodPerm) == false) {
331: String msg = "Denied: " + methodPerm + ", caller="
332: + caller;
333: //SecurityException e = new SecurityException(msg);
334: EJBAccessException e = new EJBAccessException(msg);
335: throw e;
336: }
337: } catch (PolicyContextException e) {
338: throw new RuntimeException(e);
339: }
340: }
341: }
|