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: package org.jboss.ejb.plugins;
023:
024: import java.security.Principal;
025: import java.util.Map;
026: import java.util.Set;
027: import java.lang.reflect.Method;
028: import javax.security.auth.Subject;
029: import javax.ejb.TimedObject;
030: import javax.ejb.Timer;
031:
032: import org.jboss.ejb.Container;
033: import org.jboss.invocation.Invocation;
034: import org.jboss.invocation.PayloadKey;
035: import org.jboss.metadata.ApplicationMetaData;
036: import org.jboss.metadata.AssemblyDescriptorMetaData;
037: import org.jboss.metadata.BeanMetaData;
038: import org.jboss.metadata.SecurityIdentityMetaData;
039: import org.jboss.security.AuthenticationManager;
040: import org.jboss.security.RunAsIdentity;
041: import org.jboss.security.SecurityRolesAssociation;
042: import org.jboss.security.SecurityAssociation;
043:
044: /** This interceptor is where the EJB 2.1 authentication is performed
045: * along with the run-as identity establishment.
046: *
047: * @author <a href="mailto:Scott.Stark@jboss.org">Scott Stark</a>.
048: * @author <a href="mailto:Thomas.Diesler@jboss.org">Thomas Diesler</a>.
049: * @version $Revision: 57209 $
050: */
051: public class JaasAuthenticationInterceptor extends AbstractInterceptor {
052: /** The security domain authentication service
053: */
054: protected AuthenticationManager securityManager;
055:
056: /** A static map of SecurityRolesMetaData from jboss.xml */
057: protected Map securityRoles;
058: /** The run-as identity for the ejb from ejb-jar.xml */
059: protected RunAsIdentity runAsIdentity;
060: /** The TimedObject.ejbTimeout callback */
061: protected Method ejbTimeout;
062:
063: /** Called by the super class to set the container to which this interceptor
064: belongs. We obtain the security manager and runAs identity to use here.
065: */
066: public void setContainer(Container container) {
067: super .setContainer(container);
068: if (container != null) {
069: BeanMetaData beanMetaData = container.getBeanMetaData();
070: ApplicationMetaData applicationMetaData = beanMetaData
071: .getApplicationMetaData();
072: AssemblyDescriptorMetaData assemblyDescriptor = applicationMetaData
073: .getAssemblyDescriptor();
074:
075: SecurityIdentityMetaData secMetaData = beanMetaData
076: .getSecurityIdentityMetaData();
077: if (secMetaData != null
078: && secMetaData.getUseCallerIdentity() == false) {
079: String roleName = secMetaData.getRunAsRoleName();
080: String principalName = secMetaData
081: .getRunAsPrincipalName();
082:
083: // the run-as principal might have extra roles mapped in the assembly-descriptor
084: Set extraRoleNames = assemblyDescriptor
085: .getSecurityRoleNamesByPrincipal(principalName);
086: runAsIdentity = new RunAsIdentity(roleName,
087: principalName, extraRoleNames);
088: }
089:
090: securityManager = container.getSecurityManager();
091: try {
092: // Get the timeout method
093: ejbTimeout = TimedObject.class.getMethod("ejbTimeout",
094: new Class[] { Timer.class });
095: } catch (NoSuchMethodException ignore) {
096: }
097: }
098: }
099:
100: // Container implementation --------------------------------------
101: public void start() throws Exception {
102: super .start();
103: }
104:
105: public Object invokeHome(Invocation mi) throws Exception {
106: // Authenticate the subject and apply any declarative security checks
107: checkSecurityAssociation(mi);
108:
109: /* If a run-as role was specified, push it so that any calls made
110: by this bean will have the runAsRole available for declarative
111: security checks.
112: */
113: SecurityActions.pushRunAsIdentity(runAsIdentity);
114:
115: try {
116: Object returnValue = getNext().invokeHome(mi);
117: return returnValue;
118: } finally {
119: SecurityActions.popRunAsIdentity();
120: SecurityActions.popSubjectContext();
121: }
122: }
123:
124: public Object invoke(Invocation mi) throws Exception {
125: // Authenticate the subject and apply any declarative security checks
126: checkSecurityAssociation(mi);
127:
128: // Save any existing caller run-as in the invocation for other interceptors
129: RunAsIdentity callerRunAsIdentity = SecurityActions
130: .peekRunAsIdentity();
131: if (callerRunAsIdentity != null)
132: mi.setValue("RunAsIdentity", callerRunAsIdentity,
133: PayloadKey.TRANSIENT);
134:
135: /* If a run-as role was specified, push it so that any calls made
136: by this bean will have the runAsRole available for declarative
137: security checks.
138: */
139: SecurityActions.pushRunAsIdentity(runAsIdentity);
140:
141: try {
142: Object returnValue = getNext().invoke(mi);
143: return returnValue;
144: } finally {
145: SecurityActions.popRunAsIdentity();
146: SecurityActions.popSubjectContext();
147: }
148: }
149:
150: /** Authenticate the caller using the principal and credentials in the
151: * Invocation
152: */
153: private void checkSecurityAssociation(Invocation mi)
154: throws Exception {
155: Principal principal = mi.getPrincipal();
156: Object credential = mi.getCredential();
157: boolean trace = log.isTraceEnabled();
158:
159: // If there is not a security manager then there is no authentication required
160: Method m = mi.getMethod();
161: boolean containerMethod = m == null || m.equals(ejbTimeout);
162: if (containerMethod == true || securityManager == null
163: || container == null) {
164: // Allow for the progatation of caller info to other beans
165: SecurityActions.pushSubjectContext(principal, credential,
166: null);
167: return;
168: }
169:
170: // Authenticate the caller based on the method invocation credentials
171: RunAsIdentity callerRunAsIdentity = SecurityAssociation
172: .peekRunAsIdentity();
173: if (callerRunAsIdentity == null) {
174: /* This call associates the statically defined roles with the
175: SecurityRolesAssociation thread local for use by 3.2 style of
176: login modules which combined authentication and authorization.
177: */
178: SecurityRolesAssociation.setSecurityRoles(securityRoles);
179: Subject subject = new Subject();
180: if (securityManager.isValid(principal, credential, subject) == false) {
181: // Check for the security association exception
182: Exception ex = SecurityActions.getContextException();
183: if (ex != null)
184: throw ex;
185: // Else throw a generic SecurityException
186: String msg = "Authentication exception, principal="
187: + principal;
188: SecurityException e = new SecurityException(msg);
189: throw e;
190: } else {
191: SecurityActions.pushSubjectContext(principal,
192: credential, subject);
193: if (trace) {
194: log.trace("Authenticated principal=" + principal);
195: }
196: }
197: } else {
198: // Duplicate the current subject context on the stack since
199: SecurityActions.dupSubjectContext();
200: }
201:
202: }
203: }
|