KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > net > axis > security > handler > WSSRequestHandler


1 /*
2  * JBoss, the OpenSource J2EE webOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  *
7  * Created on Jan 20, 2004
8  */

9 package org.jboss.net.axis.security.handler;
10
11 import java.security.KeyStore JavaDoc;
12 import java.security.cert.X509Certificate JavaDoc;
13 import java.util.ArrayList JavaDoc;
14 import java.util.HashMap JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Map JavaDoc;
18 import java.util.Vector JavaDoc;
19
20 import javax.naming.InitialContext JavaDoc;
21 import javax.naming.NamingException JavaDoc;
22 import javax.security.auth.x500.X500Principal JavaDoc;
23 import javax.security.auth.Subject JavaDoc;
24 import javax.xml.namespace.QName JavaDoc;
25 import javax.xml.soap.SOAPHeader JavaDoc;
26 import javax.xml.soap.SOAPHeaderElement JavaDoc;
27
28 import org.apache.axis.AxisFault;
29 import org.apache.axis.Message;
30 import org.apache.axis.MessageContext;
31 import org.apache.log4j.Logger;
32 import org.apache.ws.axis.security.WSDoAllConstants;
33 import org.apache.ws.axis.security.WSDoAllReceiver;
34 import org.apache.ws.axis.security.WSDoAllReceiverResult;
35 import org.apache.ws.axis.security.util.AxisUtil;
36 import org.apache.ws.security.WSConstants;
37 import org.apache.ws.security.WSSecurityEngineResult;
38 import org.apache.ws.security.components.crypto.Crypto;
39 import org.jboss.net.axis.security.JBossCrypto;
40 import org.jboss.net.axis.security.JBossCryptoFactory;
41 import org.jboss.net.axis.security.SecurityConstants;
42 import org.jboss.security.SecurityAssociation;
43 import org.jboss.security.SecurityDomain;
44 import org.jboss.security.SimplePrincipal;
45
46 /**
47  * <dl>
48  * <dt><b>Title: </b><dd>Web Service Security Request Handler</dd>
49  * <p>
50  * <dt><b>Description: </b><dd>This handler processes the security header of incoming SOAP messages.
51  * </dd>
52  * <p>
53  * </dl>
54  * @author <a HREF="mailto:jasone@greenrivercomputing.com">Jason Essington</a>
55  * @version $Revision: 1.4.6.1 $
56  */

57 public class WSSRequestHandler extends WSDoAllReceiver
58 {
59    protected Logger log = Logger.getLogger(this.getClass());
60
61    /*
62     * hang on to a reference of the sigCrypto that we can reach so we can use it durring authentication.
63     */

64    JBossCrypto sigCrypto = null;
65
66    SecurityDomain domain = null;
67
68    // KeyStore keystore = null;
69
// KeyStore truststore = null;
70

71    /* (non-Javadoc)
72     * @see org.apache.axis.Handler#canHandleBlock(javax.xml.namespace.QName)
73     */

74    public boolean canHandleBlock(QName JavaDoc arg0)
75    {
76       return SecurityConstants.SECURITY_HEADER_QNAME.equals(arg0);
77    }
78
79    /* (non-Javadoc)
80     * @see org.apache.axis.Handler#getUnderstoodHeaders()
81     */

82    public List JavaDoc getUnderstoodHeaders()
83    {
84       ArrayList JavaDoc understood = new ArrayList JavaDoc();
85       understood.add(SecurityConstants.SECURITY_HEADER_QNAME);
86       return understood;
87    }
88
89    /* (non-Javadoc)
90     * @see org.apache.axis.Handler#invoke(org.apache.axis.MessageContext)
91     */

92    public void invoke(MessageContext mc) throws AxisFault
93    {
94       if (log.isDebugEnabled())
95          log.debug("Enter: invoke(MessageContext)");
96
97       String JavaDoc action = (String JavaDoc) getOption(WSDoAllConstants.ACTION);
98       if (action == null)
99          action = (String JavaDoc) mc.getProperty(WSDoAllConstants.ACTION);
100       if (action == null)
101          throw new AxisFault("WSSRequestHandler: No action defined");
102       int doAction = AxisUtil.decodeAction(action, new Vector JavaDoc());
103       try
104       {
105          super.invoke(mc);
106       }
107       catch (AxisFault e)
108       {
109          log.fatal("Failed to handle security header", e);
110          throw e;
111       }
112
113       Message sm = mc.getCurrentMessage();
114       SOAPHeader JavaDoc sHeader = null;
115       try
116       {
117          sHeader = sm.getSOAPEnvelope().getHeader();
118       }
119       catch (Exception JavaDoc ex)
120       {
121          // we already would have thrown this exception from the super class, but . . .
122
throw new AxisFault("WSRequestHandler: cannot get SOAP header", ex);
123       }
124
125       /*
126        * for some reason the message context doesn't remember that the Security header
127        * has been processed by the super class, so let's look it up and mark it as processed (again) here.
128        */

129       String JavaDoc actor = (String JavaDoc) getOption(WSDoAllConstants.ACTOR);
130       Iterator JavaDoc headers = sHeader.examineHeaderElements(actor);
131       SOAPHeaderElement JavaDoc headerElement = null;
132       while (headers.hasNext())
133       {
134          headerElement = (SOAPHeaderElement JavaDoc) headers.next();
135          if (headerElement.getLocalName().equals(WSConstants.WSSE_LN)
136             && headerElement.getNamespaceURI().equals(WSConstants.WSSE_NS))
137          {
138             break;
139          }
140       }
141       if (headerElement != null)
142          ((org.apache.axis.message.SOAPHeaderElementAxisImpl) headerElement).setProcessed(true);
143
144       // only authenticate if we expect a signature or username token
145
if (((doAction & WSConstants.SIGN) == WSConstants.SIGN) || ((doAction & WSConstants.UT) == WSConstants.UT))
146       {
147          // xDoclet will add a skipAuthentication=true to the handler parameters if the ejb has a
148
// "ejb.permission unchecked" tag
149
if (!"true".equals(getOption("skipAuthentication")))
150             authenticate(mc, actor);
151       }
152
153       if (log.isDebugEnabled())
154       {
155          log.debug("\n\tHeader Element: " + headerElement.getLocalName() + "\n\t\tisProcessed: "
156             + ((org.apache.axis.message.SOAPHeaderElementAxisImpl) headerElement).isProcessed() + "\n\t\tmustUnderstand: "
157             + ((org.apache.axis.message.SOAPHeaderElementAxisImpl) headerElement).getMustUnderstand());
158          log.debug("Exit: invoke(MessageContext)");
159       }
160    }
161
162    /**
163     * Hook to authenticate any principals found via JBossSX.
164     * Currently we just use the key alias as the user, and the certificate as the password.
165     * I am sure this scheme has some flaws, so I would love to have some input on a better method.
166     *
167     * @param mc
168     * @throws AxisFault
169     */

170    protected void authenticate(MessageContext mc, String JavaDoc actor) throws AxisFault
171    {
172       if (log.isDebugEnabled())
173          log.debug("Enter: authenticate(MessageContext)");
174       /*
175        * TODO figure out what to do with multiple principals
176        *
177        */

178       String JavaDoc alias = null;
179       X509Certificate JavaDoc[] certs = null;
180       SimplePrincipal sp = null;
181       Vector JavaDoc results = null;
182       if ((results = (Vector JavaDoc) mc.getProperty(WSDoAllConstants.RECV_RESULTS)) != null)
183       {
184          // first we need to find the results pertinent to this actor (role)
185
WSDoAllReceiverResult actorResult = null;
186          for (Iterator JavaDoc i = results.iterator(); i.hasNext();)
187          {
188             WSDoAllReceiverResult result = (WSDoAllReceiverResult) i.next();
189             if (result.getActor() == actor)
190             {
191                actorResult = result;
192                if (log.isDebugEnabled())
193                   log.debug("Found results for actor: " + actor);
194                break;
195             }
196          }
197          // Then we need to find the (a) signature action
198
// what if there is more than one signature for this actor? Is that even possible?
199

200          // OOPS api change !!!
201

202          X500Principal JavaDoc sigPrincipal = null;
203          X509Certificate JavaDoc credential = null;
204
205          Vector JavaDoc engineResults = actorResult.getResults();
206          for (Iterator JavaDoc iter = engineResults.iterator(); iter.hasNext();)
207          {
208             WSSecurityEngineResult result = (WSSecurityEngineResult) iter.next();
209             if (result.getAction() == WSConstants.SIGN)
210             {
211                // result.getPrincipal() only returns x500certificate.getSubjectDN() which isn't an x500Principal
212
// sigPrincipal = (X500Principal) result.getCertificate().getSubjectX500Principal();
213
credential = result.getCertificate();
214                sigPrincipal = credential.getSubjectX500Principal();
215             }
216          }
217
218          try
219          {
220             alias = sigCrypto.getAliasForX500Principal(sigPrincipal);
221             sp = new SimplePrincipal(alias);
222          }
223          catch (Exception JavaDoc e)
224          {
225             // this will just throw an exception later if the service actually requires authentication.
226
if (sigPrincipal == null)
227                log.warn("No Principal was found in the message.");
228             else
229                log.warn("Unable to determine alias for the principal: " + sigPrincipal.getName(), e);
230          }
231
232          if (log.isDebugEnabled())
233             log.debug("attempting to authenticate using " + alias + ":" + credential.getSubjectDN().getName());
234          Subject JavaDoc subject = new Subject JavaDoc();
235          if (!domain.isValid(new SimplePrincipal(alias), credential, subject))
236          {
237             throw new AxisFault("Server.Unauthenticated", org.apache.axis.utils.Messages.getMessage("cantAuth01", sp
238                .getName()), null, null);
239          }
240          else
241          {
242             mc.setProperty(MessageContext.AUTHUSER, subject);
243             // store the actor/alias used to sign this request.
244
Map JavaDoc signers = (Map JavaDoc) mc.getProperty(SecurityConstants.MC_REQ_SIGNERS);
245             if (signers == null)
246                signers = new HashMap JavaDoc(5);
247             signers.put(actor, alias);
248             mc.setProperty(SecurityConstants.MC_REQ_SIGNERS, signers);
249          }
250       }
251
252       if (log.isDebugEnabled())
253          log.debug("Exit: authenticate(MessageContext)");
254    }
255
256    protected Crypto loadSignatureCrypto() throws AxisFault
257    {
258       if (log.isDebugEnabled())
259          log.debug("Loading the Signature Crypto Class");
260       //TODO maybe the Signature Crypto should come from the truststore in the Security Domain?
261
// bah, just use the keystore for now
262
if (domain == null)
263          getSecurityDomain();
264       // no need to test for a null domain as it is handled by getSecurityDomain
265

266       KeyStore JavaDoc truststore = domain.getTrustStore();
267       if (truststore == null)
268          throw new AxisFault("WSSReceiverHandler: No truststore available.");
269       String JavaDoc cryptoClass;
270       if ((cryptoClass = (String JavaDoc) getOption(SecurityConstants.HANDLER_CRYPTO_CLASS)) == null)
271          cryptoClass = "org.jboss.net.axis.security.JBoss14Crypto";
272       sigCrypto = JBossCryptoFactory.getInstance(cryptoClass, truststore);
273       return sigCrypto;
274    }
275
276    protected Crypto loadDecryptionCrypto() throws AxisFault
277    {
278       if (log.isDebugEnabled())
279          log.debug("Loading the Decryption Crypto Class");
280       if (domain == null)
281          getSecurityDomain();
282       // npe is handled in getSecurityDomain
283
KeyStore JavaDoc keystore = domain.getKeyStore();
284       if (keystore == null)
285          throw new AxisFault("WSSReceiverHandler: No keystore available.");
286       String JavaDoc cryptoClass;
287       if ((cryptoClass = (String JavaDoc) getOption(SecurityConstants.HANDLER_CRYPTO_CLASS)) == null)
288          cryptoClass = "org.jboss.net.axis.security.JBoss14Crypto";
289       return JBossCryptoFactory.getInstance(cryptoClass, keystore);
290    }
291
292    private void getSecurityDomain() throws AxisFault
293    {
294       //String sd = "other"; // this is as good a default as any I suppose.
295
String JavaDoc sd;
296       if ((sd = (String JavaDoc) getOption(SecurityConstants.HANDLER_SEC_DOMAIN)) == null)
297          sd = "java:/jaas/other";
298       if (log.isDebugEnabled())
299          log.debug("WSSReceiveHandler, securityDomain=" + sd);
300       try
301       {
302          Object JavaDoc tempDomain = new InitialContext JavaDoc().lookup(sd);
303          if (tempDomain != null && tempDomain instanceof SecurityDomain)
304             domain = (SecurityDomain) tempDomain;
305          else
306          {
307             // oops, we will not be able to get our keystore in the login module.
308
log.fatal("The SecurityManager named " + sd + " is not a SecurityDomain");
309             throw new AxisFault("WSSReceiverHandler: No security domain is available.");
310          }
311       }
312       catch (NamingException JavaDoc e)
313       {
314          throw new AxisFault("Unable to find the securityDomain named: " + sd, e);
315       }
316    }
317 }
318
Popular Tags