001: /*
002: * The contents of this file are subject to the terms
003: * of the Common Development and Distribution License
004: * (the License). You may not use this file except in
005: * compliance with the License.
006: *
007: * You can obtain a copy of the license at
008: * https://glassfish.dev.java.net/public/CDDLv1.0.html.
009: * See the License for the specific language governing
010: * permissions and limitations under the License.
011: *
012: * When distributing Covered Code, include this CDDL
013: * Header Notice in each file and include the License file
014: * at https://glassfish.dev.java.net/public/CDDLv1.0.html.
015: * If applicable, add the following below the CDDL Header,
016: * with the fields enclosed by brackets [] replaced by
017: * you own identifying information:
018: * "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
021: */
022:
023: package com.sun.xml.ws.security.opt.impl.outgoing;
024:
025: import com.sun.istack.NotNull;
026: import com.sun.istack.Nullable;
027: import com.sun.xml.ws.security.opt.api.SecurityElementWriter;
028: import com.sun.xml.ws.security.opt.api.SecurityHeaderElement;
029: import com.sun.xml.ws.security.opt.impl.enc.JAXBEncryptedData;
030: import com.sun.xml.ws.security.opt.impl.enc.JAXBEncryptedKey;
031: import com.sun.xml.ws.security.opt.impl.message.GSHeaderElement;
032: import com.sun.xml.ws.security.opt.impl.tokens.Timestamp;
033: import com.sun.xml.wss.impl.MessageConstants;
034: import java.util.ArrayList;
035: import java.util.Iterator;
036: import java.util.NoSuchElementException;
037: import javax.xml.namespace.QName;
038: import javax.xml.soap.SOAPException;
039: import javax.xml.soap.SOAPMessage;
040: import javax.xml.stream.XMLStreamException;
041: import javax.xml.stream.XMLStreamWriter;
042: import org.xml.sax.ContentHandler;
043: import org.xml.sax.ErrorHandler;
044: import org.xml.sax.SAXException;
045:
046: /**
047: *
048: * @author K.Venugopal@sun.com
049: */
050: public class SecurityHeader {
051: public static final int LAYOUT_LAX = 0;
052: public static final int LAYOUT_STRICT = 1;
053: public static final int LAYOUT_LAX_TS_FIRST = 2;
054: public static final int LAYOUT_LAX_TS_LAST = 3;
055:
056: protected ArrayList<SecurityHeaderElement> secHeaderContent = new ArrayList<SecurityHeaderElement>();
057: protected int headerLayout = LAYOUT_STRICT;
058: protected String soapVersion = MessageConstants.SOAP_1_1_NS;
059: private boolean debug = false;
060:
061: /**
062: * Default constructor
063: * uses Lax Message Layout and SOAP 1.1 version
064: */
065: public SecurityHeader() {
066:
067: }
068:
069: public SecurityHeader(int layout, String soapVersion) {
070: this .headerLayout = layout;
071: this .soapVersion = soapVersion;
072: }
073:
074: public int getHeaderLayout() {
075: return this .headerLayout;
076: }
077:
078: public void setHeaderLayout(int headerLayout) {
079: this .headerLayout = headerLayout;
080: }
081:
082: public String getSOAPVersion() {
083: return this .soapVersion;
084: }
085:
086: public void setSOAPVersion(String soapVersion) {
087: this .soapVersion = soapVersion;
088: }
089:
090: public SecurityHeaderElement getChildElement(String localName,
091: String uri) {
092: for (SecurityHeaderElement she : secHeaderContent) {
093: if (localName.equals(she.getLocalPart())
094: && uri.equals(she.getNamespaceURI())) {
095: return she;
096: }
097: }
098: return null;
099: }
100:
101: public Iterator getHeaders(final String localName, final String uri) {
102: return new Iterator() {
103: int idx = 0;
104: Object next;
105:
106: public boolean hasNext() {
107: if (next == null)
108: fetch();
109: return next != null;
110: }
111:
112: public Object next() {
113: if (next == null) {
114: fetch();
115: if (next == null) {
116: throw new NoSuchElementException();
117: }
118: }
119:
120: Object r = next;
121: next = null;
122: return r;
123: }
124:
125: private void fetch() {
126: while (idx < secHeaderContent.size()) {
127: SecurityHeaderElement she = secHeaderContent
128: .get(idx++);
129: if ((uri == null && localName.equals(she
130: .getLocalPart()))
131: || (localName.equals(she.getLocalPart()) && uri
132: .equals(she.getNamespaceURI()))) {
133: next = she;
134: break;
135: }
136: }
137: }
138:
139: public void remove() {
140: throw new UnsupportedOperationException();
141: }
142: };
143: }
144:
145: public SecurityHeaderElement getChildElement(String id) {
146: for (SecurityHeaderElement she : secHeaderContent) {
147: if (id.equals(she.getId()))
148: return she;
149: }
150: return null;
151: }
152:
153: public void add(SecurityHeaderElement header) {
154: prepend(header);
155: }
156:
157: public boolean replace(SecurityHeaderElement replaceThis,
158: SecurityHeaderElement withThis) {
159: int index = secHeaderContent.indexOf(replaceThis);
160: if (index != -1) {
161: secHeaderContent.set(index, withThis);
162: return true;
163: }
164: return false;
165: }
166:
167: public void prepend(SecurityHeaderElement element) {
168: secHeaderContent.add(0, element);
169: }
170:
171: public void append(SecurityHeaderElement element) {
172: secHeaderContent.add(element);
173: }
174:
175: /**
176: * Gets the namespace URI of this header element.
177: *
178: * @return
179: * this string must be interned.
180: */
181: public @NotNull
182: String getNamespaceURI() {
183: return MessageConstants.WSSE_NS;
184: }
185:
186: /**
187: * Gets the local name of this header element.
188: *
189: * @return
190: * this string must be interned.
191: */
192: public @NotNull
193: String getLocalPart() {
194: return "Security";
195: }
196:
197: /**
198: * Gets the attribute value on the header element.
199: *
200: * @param nsUri
201: * The namespace URI of the attribute. Can be empty.
202: * @param localName
203: * The local name of the attribute.
204: *
205: * @return
206: * if the attribute is found, return the whitespace normalized value.
207: * (meaning no leading/trailing space, no consequtive whitespaces in-between.)
208: * Otherwise null. Note that the XML parsers are responsible for
209: * whitespace-normalizing attributes, so {@link Header} implementation
210: * doesn't have to do anything.
211: */
212: public @Nullable
213: String getAttribute(@NotNull
214: String nsUri, @NotNull
215: String localName) {
216: throw new UnsupportedOperationException();
217: }
218:
219: /**
220: * Gets the attribute value on the header element.
221: *
222: * <p>
223: * This is a convenience method that calls into {@link #getAttribute(String, String)}
224: *
225: * @param name
226: * Never null.
227: *
228: * @see #getAttribute(String, String)
229: */
230: public @Nullable
231: String getAttribute(@NotNull
232: QName name) {
233: throw new UnsupportedOperationException();
234: }
235:
236: /**
237: * Writes out the header.
238: *
239: * @throws XMLStreamException
240: * if the operation fails for some reason. This leaves the
241: * writer to an undefined state.
242: */
243: public void writeTo(XMLStreamWriter streamWriter)
244: throws XMLStreamException {
245: orderHeaders();
246: if (secHeaderContent.size() > 0) {
247: streamWriter.writeStartElement(
248: MessageConstants.WSSE_PREFIX, "Security",
249: MessageConstants.WSSE_NS);
250: writeMustunderstand(streamWriter);
251: for (SecurityHeaderElement el : secHeaderContent) {
252: ((SecurityElementWriter) el).writeTo(streamWriter);
253: }
254:
255: streamWriter.writeEndElement();
256: }
257: }
258:
259: /**
260: * Writes out the header to the given SOAPMessage.
261: *
262: * <p>
263: * Sometimes a {@link Message} needs to produce itself
264: * as {@link SOAPMessage}, in which case each header needs
265: * to turn itself into a header.
266: *
267: * @throws SOAPException
268: * if the operation fails for some reason. This leaves the
269: * writer to an undefined state.
270: */
271: public void writeTo(SOAPMessage saaj) throws SOAPException {
272: throw new UnsupportedOperationException();
273: }
274:
275: /**
276: * Writes out the header as SAX events.
277: *
278: * <p>
279: * Sometimes a {@link Message} needs to produce SAX events,
280: * and this method is necessary for headers to participate to it.
281: *
282: * <p>
283: * A header is responsible for producing the SAX events for its part,
284: * including <tt>startPrefixMapping</tt> and <tt>endPrefixMapping</tt>,
285: * but not startDocument/endDocument.
286: *
287: * <p>
288: * Note that SAX contract requires that any error that does NOT originate
289: * from {@link ContentHandler} (meaning any parsing error and etc) must
290: * be first reported to {@link ErrorHandler}. If the SAX event production
291: * cannot be continued and the processing needs to abort, the code may
292: * then throw the same {@link SAXParseException} reported to {@link ErrorHandler}.
293: *
294: * @param contentHandler
295: * The {@link ContentHandler} that receives SAX events.
296: *
297: * @param errorHandler
298: * The {@link ErrorHandler} that receives parsing errors.
299: */
300: public void writeTo(ContentHandler contentHandler,
301: ErrorHandler errorHandler) throws SAXException {
302: throw new UnsupportedOperationException();
303: }
304:
305: private void writeMustunderstand(XMLStreamWriter writer)
306: throws XMLStreamException {
307: if (soapVersion == MessageConstants.SOAP_1_1_NS) {
308: writer.writeAttribute("S", MessageConstants.SOAP_1_1_NS,
309: MessageConstants.MUST_UNDERSTAND, "1");
310: } else if (soapVersion == MessageConstants.SOAP_1_2_NS) {
311: writer.writeAttribute("S", MessageConstants.SOAP_1_2_NS,
312: MessageConstants.MUST_UNDERSTAND, "true");
313: }
314: }
315:
316: private void orderHeaders() {
317: if (headerLayout == LAYOUT_LAX_TS_LAST) {
318: laxTimestampLast();
319: } else if (headerLayout == LAYOUT_LAX_TS_FIRST) {
320: laxTimestampFirst();
321: } else if (headerLayout == LAYOUT_STRICT) {
322: strict();
323: } else {
324: strict();
325: }
326: }
327:
328: private void laxTimestampLast() {
329: strict();
330: SecurityHeaderElement timestamp = this .secHeaderContent.get(0);
331: if (timestamp != null && (timestamp instanceof Timestamp)) {
332: this .secHeaderContent.remove(0);
333: this .secHeaderContent.add(timestamp);
334: }
335: }
336:
337: private void laxTimestampFirst() {
338: strict();
339: }
340:
341: private void print(ArrayList<SecurityHeaderElement> list) {
342: if (!debug) {
343: return;
344: }
345: System.out
346: .println("++++++++++++++++++++++++++++++++++++++++++++++++");
347: for (int j = 0; j < list.size(); j++) {
348: System.out.println(list.get(j));
349: }
350: System.out
351: .println("++++++++++++++++++++++++++++++++++++++++++++++++");
352: }
353:
354: private void strict() {
355: ArrayList<SecurityHeaderElement> primaryElementList = new ArrayList<SecurityHeaderElement>();
356: ArrayList<SecurityHeaderElement> topElementList = new ArrayList<SecurityHeaderElement>();
357: int len = secHeaderContent.size();
358: print(secHeaderContent);
359:
360: SecurityHeaderElement timeStamp = null;
361: for (int i = 0; i < len; i++) {
362: SecurityHeaderElement she = secHeaderContent.get(i);
363: if (she.getLocalPart() == MessageConstants.TIMESTAMP_LNAME) {
364: timeStamp = she;
365: continue;
366: }
367: if (isTopLevelElement(she)) {
368: topElementList.add(she);
369: } else {
370: primaryElementList.add(0, she);
371: }
372: }
373:
374: print(topElementList);
375: topElementList = orderList(topElementList);
376:
377: print(primaryElementList);
378: primaryElementList = orderList(primaryElementList);
379:
380: ArrayList<SecurityHeaderElement> tmpList = new ArrayList<SecurityHeaderElement>();
381: for (int i = 0; i < primaryElementList.size(); i++) {
382: SecurityHeaderElement she = primaryElementList.get(i);
383: if (she.getLocalPart() == MessageConstants.XENC_REFERENCE_LIST_LNAME
384: || she.getLocalPart() == MessageConstants.ENCRYPTEDKEY_LNAME) {
385: int tLen = topElementList.size();
386: for (int j = tLen - 1; j >= 0; j--) {
387: SecurityHeaderElement tk = topElementList.get(j);
388: if (she.refersToSecHdrWithId(tk.getId())) {
389: topElementList.add(j + 1, she);
390: //topElementList.add(j,she);
391: tmpList.add(she);
392: break;
393: }
394: }
395: }
396: }
397: primaryElementList.removeAll(tmpList);
398:
399: secHeaderContent.clear();
400: for (int i = topElementList.size() - 1; i >= 0; i--) {
401: secHeaderContent.add(topElementList.get(i));
402: }
403:
404: for (int i = primaryElementList.size() - 1; i >= 0; i--) {
405: secHeaderContent.add(primaryElementList.get(i));
406: }
407:
408: if (timeStamp != null) {
409: secHeaderContent.add(0, timeStamp);
410: }
411: }
412:
413: private ArrayList<SecurityHeaderElement> orderList(
414: ArrayList<SecurityHeaderElement> list) {
415: ArrayList<SecurityHeaderElement> tmp = new ArrayList<SecurityHeaderElement>();
416: for (int i = 0; i < list.size(); i++) {
417: SecurityHeaderElement securityElementOne = list.get(i);
418:
419: int wLen = tmp.size();
420: int index = 0;
421: if (wLen == 0) {
422: tmp.add(securityElementOne);
423: continue;
424: }
425:
426: int setIndex = -1;
427: for (int j = 0; j < wLen; j++) {
428: SecurityHeaderElement securityElementTwo = tmp.get(j);
429: if (securityElementOne
430: .refersToSecHdrWithId(securityElementTwo
431: .getId())) {
432: if (securityElementTwo instanceof JAXBEncryptedData) {
433: if (securityElementOne instanceof JAXBEncryptedKey
434: || securityElementOne.getLocalPart() == MessageConstants.XENC_REFERENCE_LIST_LNAME) {
435: setIndex = j + 1;
436: } else {
437: setIndex = j;
438: }
439: } else {
440: setIndex = j;
441: }
442: } else if (securityElementTwo instanceof JAXBEncryptedData
443: && refersToEncryptedElement(securityElementOne,
444: securityElementTwo)) {
445: setIndex = j;
446: } else if (securityElementTwo
447: .refersToSecHdrWithId(securityElementOne
448: .getId())) {
449: if (securityElementTwo instanceof JAXBEncryptedKey
450: && securityElementOne instanceof JAXBEncryptedData) {
451: setIndex = j;
452: } else {
453: setIndex = j + 1;
454: }
455:
456: }
457: }
458: if (tmp.contains(securityElementOne)) {
459: continue;
460: }
461: if (setIndex == -1) {
462: tmp.add(securityElementOne);
463: } else {
464: tmp.add(setIndex, securityElementOne);
465: }
466: print(tmp);
467: }
468: return tmp;
469: }
470:
471: private boolean refersToEncryptedElement(
472: SecurityHeaderElement securityElementOne,
473: SecurityHeaderElement securityElementTwo) {
474: if (securityElementOne
475: .refersToSecHdrWithId(((JAXBEncryptedData) securityElementTwo)
476: .getEncryptedId())) {
477: return true;
478: }
479: return false;
480: }
481:
482: private void movePrevHeader(SecurityHeaderElement toBeMoved,
483: int index) {
484: int prevIndex = secHeaderContent.indexOf(toBeMoved);
485: SecurityHeaderElement prev = (SecurityHeaderElement) secHeaderContent
486: .get(prevIndex - 1);
487: String prevId = prev.getId();
488: secHeaderContent.remove(toBeMoved);
489: secHeaderContent.add(index, toBeMoved);
490: if (toBeMoved.refersToSecHdrWithId(prevId)) {
491: movePrevHeader(prev, index);
492: }
493: }
494:
495: //move tokens to top.
496: private boolean isTopLevelElement(SecurityHeaderElement she) {
497: String localPart = she.getLocalPart();
498: //String uri = she.getNamespaceURI();
499: if (localPart.equals(MessageConstants.ENCRYPTED_DATA_LNAME)) {
500: if (she instanceof GSHeaderElement) {
501: return true;//Issued token encrypted.
502: }
503: localPart = ((JAXBEncryptedData) she)
504: .getEncryptedLocalName();
505:
506: }
507: if (localPart == MessageConstants.WSSE_BINARY_SECURITY_TOKEN_LNAME) {
508: return true;
509: }
510: if (localPart == MessageConstants.SECURITY_CONTEXT_TOKEN_LNAME) {
511: return true;
512: }
513: if (localPart == MessageConstants.ENCRYPTEDKEY_LNAME) {
514: if (((JAXBEncryptedKey) she).hasReferenceList()) {
515: return false;
516: }
517: return true;
518: }
519: if (localPart == MessageConstants.DERIVEDKEY_TOKEN_LNAME) {
520: return true;
521: }
522: if (localPart == MessageConstants.SIGNATURE_CONFIRMATION_LNAME) {
523: return true;
524: }
525:
526: if (localPart == MessageConstants.TIMESTAMP_LNAME) {
527: return true;
528: }
529: if (localPart.equals(MessageConstants.SAML_ASSERTION_LNAME)) {
530: return true;
531: }
532: if (localPart == MessageConstants.USERNAME_TOKEN_LNAME) {
533: return true;
534: }
535: return false;
536: }
537: }
|