001/* 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: Request.java,v 1.2 2008/06/25 05:47:37 qcheng Exp $ 026 * 027 * Portions Copyrighted 2016 ForgeRock AS. 028 */ 029 030package com.sun.identity.saml.protocol; 031 032import static org.forgerock.openam.utils.Time.*; 033 034import com.sun.identity.common.SystemConfigurationUtil; 035import com.sun.identity.shared.xml.XMLUtils; 036import com.sun.identity.shared.DateUtils; 037import com.sun.identity.saml.assertion.AssertionIDReference; 038import com.sun.identity.saml.common.SAMLConstants; 039import com.sun.identity.saml.common.SAMLException; 040import com.sun.identity.saml.common.SAMLRequesterException; 041import com.sun.identity.saml.common.SAMLRequestVersionTooHighException; 042import com.sun.identity.saml.common.SAMLRequestVersionTooLowException; 043import com.sun.identity.saml.common.SAMLResponderException; 044import com.sun.identity.saml.common.SAMLUtils; 045 046import com.sun.identity.saml.xmlsig.XMLSignatureManager; 047 048import java.io.ByteArrayOutputStream; 049 050import java.text.ParseException; 051 052import java.util.ArrayList; 053import java.util.Collections; 054import java.util.Date; 055import java.util.Iterator; 056import java.util.List; 057import java.util.StringTokenizer; 058 059import org.w3c.dom.Attr; 060import org.w3c.dom.Document; 061import org.w3c.dom.Element; 062import org.w3c.dom.NamedNodeMap; 063import org.w3c.dom.Node; 064import org.w3c.dom.NodeList; 065 066/** 067 * This <code>Request</code> class represents a Request XML document. 068 * It extends from the abstract base class <code>AbstractRequest</code>. 069 * 070 * @supported.all.api 071 */ 072public class Request extends AbstractRequest { 073 /* 074 * data members 075 */ 076 077 protected Query query = null; 078 protected List assertionIDRefs = Collections.EMPTY_LIST; 079 protected List artifacts = Collections.EMPTY_LIST; 080 protected int contentType = NOT_SUPPORTED; 081 protected String xmlString = null; 082 protected String signatureString = null; 083 084 // Request ID attribute name 085 private static final String REQUEST_ID_ATTRIBUTE = "RequestID"; 086 087 /** 088 * The request is not supported. 089 */ 090 public final static int NOT_SUPPORTED = -1; 091 092 /** 093 * The request is an Authentication Query. 094 */ 095 public final static int AUTHENTICATION_QUERY = 0; 096 097 /** 098 * The request is an Authorization Decision Query. 099 */ 100 public final static int AUTHORIZATION_DECISION_QUERY = 1; 101 102 /** 103 * The request is an Assertion ID Reference. 104 */ 105 public final static int ASSERTION_ID_REFERENCE = 2; 106 107 /** 108 * The request is an Assertion Artifact. 109 */ 110 public final static int ASSERTION_ARTIFACT = 3; 111 112 /** 113 * The request is an Attribute Query. 114 */ 115 public final static int ATTRIBUTE_QUERY = 4; 116 117 /* 118 * Constructors 119 */ 120 protected Request() {} 121 122 /** 123 * Method to sign the Request. 124 * @exception SAMLException if could not sign the Request. 125 */ 126 public void signXML() throws SAMLException { 127 if (signed) { 128 if (SAMLUtils.debug.messageEnabled()) { 129 SAMLUtils.debug.message("Request.signXML: the request is " 130 + "already signed."); 131 } 132 throw new SAMLException( 133 SAMLUtils.bundle.getString("alreadySigned")); 134 } 135 String certAlias = 136 SystemConfigurationUtil.getProperty( 137 "com.sun.identity.saml.xmlsig.certalias"); 138 if (certAlias == null) { 139 if (SAMLUtils.debug.messageEnabled()) { 140 SAMLUtils.debug.message("Request.signXML: couldn't obtain " 141 + "this site's cert Alias."); 142 } 143 throw new SAMLResponderException( 144 SAMLUtils.bundle.getString("cannotFindCertAlias")); 145 } 146 XMLSignatureManager manager = XMLSignatureManager.getInstance(); 147 if ((majorVersion == 1) && (minorVersion == 0)) { 148 SAMLUtils.debug.message("Request.signXML: sign with version 1.0"); 149 signatureString = manager.signXML(this.toString(true, true), 150 certAlias); 151 // this block is used for later return of signature element by 152 // getSignature() method 153 signature = 154 XMLUtils.toDOMDocument(signatureString, SAMLUtils.debug) 155 .getDocumentElement(); 156 } else { 157 Document doc = XMLUtils.toDOMDocument(this.toString(true, true), 158 SAMLUtils.debug); 159 // sign with SAML 1.1 spec & include cert in KeyInfo 160 signature = manager.signXML(doc, certAlias, null, 161 REQUEST_ID_ATTRIBUTE, getRequestID(), true, null); 162 signatureString = XMLUtils.print(signature); 163 } 164 signed = true; 165 xmlString = this.toString(true, true); 166 } 167 168 /** 169 * This constructor shall only be used at the client side to construct a 170 * Request object. 171 * NOTE: The content here is just the body for the Request. The 172 * constructor will add <code>MajorVersion</code>, 173 * <code>MinorVersion</code>, etc. to form a complete Request. 174 * @param respondWiths A List of Strings representing 175 * <code>RespondWith</code> elements. It could be null when there is 176 * no <code><RespondWith></code>. Each string could be prefixed 177 * by <code>saml:</code>. If it is not prefixed, or prefixed by a 178 * prefix other than <code>saml:</code>, <code>saml:</code> will be 179 * used instead. 180 * @param requestId If it's null, the constructor will create one. 181 * @param contents A List of objects that are the contents of Request that 182 * the client wants to send to the server. It could be an 183 * <code>AuthenticationQuery</code>, 184 * <code>AuthorizationDecisionQuery</code>, 185 * <code>AttributeQuery</code>, 1 or more 186 * <code>AssertionIDReference</code>, or 1 or more of 187 * <code>AssertionArtifact</code>. 188 * @exception SAMLException if an error occurs. 189 */ 190 public Request(List respondWiths, 191 String requestId, 192 List contents) throws SAMLException { 193 Object temp = null; 194 195 if ((respondWiths != null) && 196 (respondWiths != Collections.EMPTY_LIST)) { 197 for (int i = 0, length = respondWiths.size(); i < length; i++) { 198 temp = respondWiths.get(i); 199 if (!(temp instanceof String)) { 200 if (SAMLUtils.debug.messageEnabled()) { 201 SAMLUtils.debug.message("Request: wrong input for " 202 + "RespondWith"); 203 } 204 throw new SAMLRequesterException( 205 SAMLUtils.bundle.getString("wrongInput")); 206 } 207 if ((this.respondWiths == null) || 208 (this.respondWiths.size() == 0)) { 209 this.respondWiths = new ArrayList(); 210 } 211 (this.respondWiths).add(checkAndGetRespondWith((String)temp)); 212 } 213 } 214 215 if ((requestId != null) && (requestId.length() != 0)) { 216 requestID = requestId; 217 } else { 218 // random generate one 219 requestID = SAMLUtils.generateID(); 220 if (requestID == null) { 221 SAMLUtils.debug.error("Request: couldn't generate RequestID."); 222 throw new SAMLRequesterException( 223 SAMLUtils.bundle.getString("errorGenerateID")); 224 } 225 } 226 227 parseContents(contents); 228 issueInstant = newDate(); 229 } 230 231 private String checkAndGetRespondWith(String respondWith) 232 throws SAMLException 233 { 234 if ((respondWith == null) || (respondWith.length() == 0)) { 235 SAMLUtils.debug.message("Request: empty RespondWith Value."); 236 throw new SAMLRequesterException( 237 SAMLUtils.bundle.getString("wrongInput")); 238 } 239 240 if (respondWith.indexOf(":") == -1) { 241 return (SAMLConstants.ASSERTION_PREFIX + respondWith); 242 } else { 243 StringTokenizer st = new StringTokenizer(respondWith, ":"); 244 if (st.countTokens() != 2) { 245 SAMLUtils.debug.message("Request: wrong RespondWith value."); 246 throw new SAMLRequesterException( 247 SAMLUtils.bundle.getString("wrongInput")); 248 } 249 st.nextToken(); 250 String temp = st.nextToken().trim(); 251 if (temp.length() == 0) { 252 SAMLUtils.debug.message("Request: wrong RespondWith value."); 253 throw new SAMLRequesterException( 254 SAMLUtils.bundle.getString("wrongInput")); 255 } 256 return (SAMLConstants.ASSERTION_PREFIX + temp); 257 } 258 } 259 260 /** 261 * Checks the contents of the Request and set the class members accordingly. 262 * 263 * Used by this class only. 264 * @param contents A List that contains the contents of the request. 265 * it could be a query, 1 or more <code>AssertionIDReference</code>, 266 * or 1 or more <code>AssertionArtifact</code>. 267 * @exception SAMLException when an error occurs during the process. 268 */ 269 private void parseContents(List contents) throws SAMLException { 270 // check contents and set the contentType appropriately 271 int length = 0; 272 int i = 0; 273 if ((contents == null) || 274 ((length = contents.size()) == 0)) { 275 SAMLUtils.debug.message("Request: empty content."); 276 throw new SAMLRequesterException( 277 SAMLUtils.bundle.getString("wrongInput")); 278 } 279 for (i = 0; i < length; i++) { 280 Object temp = contents.get(i); 281 if (temp instanceof AuthenticationQuery) { 282 // make sure this is the first one on the list 283 if ((contentType != NOT_SUPPORTED) || 284 // and make sure there is no other elements on the list 285 (i != (length - 1))) { 286 if (SAMLUtils.debug.messageEnabled()) { 287 SAMLUtils.debug.message("Request: should contain only" 288 + " one AuthenticationQuery."); 289 } 290 throw new SAMLRequesterException( 291 SAMLUtils.bundle.getString("wrongInput")); 292 } 293 contentType = AUTHENTICATION_QUERY; 294 query = (AuthenticationQuery) temp; 295 } else if (temp instanceof AuthorizationDecisionQuery) { 296 // make sure this is the first one on the list 297 if ((contentType != NOT_SUPPORTED) || 298 // and make sure there is no other elements on the list 299 (i != (length - 1))) { 300 if (SAMLUtils.debug.messageEnabled()) { 301 SAMLUtils.debug.message("Request: should contain only" 302 + " one AuthorizationDecisionQuery."); 303 } 304 throw new SAMLRequesterException( 305 SAMLUtils.bundle.getString("wrongInput")); 306 } 307 contentType = AUTHORIZATION_DECISION_QUERY; 308 query = (AuthorizationDecisionQuery) temp; 309 } else if (temp instanceof AttributeQuery) { 310 // make sure this is the first one on the list 311 if ((contentType != NOT_SUPPORTED) || 312 // and make sure there is no other elements on the list 313 (i != (length - 1))) { 314 if (SAMLUtils.debug.messageEnabled()) { 315 SAMLUtils.debug.message("Request: should contain only" 316 + " one AttributeQuery."); 317 } 318 throw new SAMLRequesterException( 319 SAMLUtils.bundle.getString("wrongInput")); 320 } 321 contentType = ATTRIBUTE_QUERY; 322 query = (AttributeQuery) temp; 323 } else if (temp instanceof AssertionIDReference) { 324 // if this is not the first element on the list , and if the 325 // the previously assigned elements are not AssertionIDReference 326 if ((contentType != NOT_SUPPORTED) && 327 (contentType != ASSERTION_ID_REFERENCE)) { 328 if (SAMLUtils.debug.messageEnabled()) { 329 SAMLUtils.debug.message("Request: should contain" 330 + " one or more AssertionIDReference."); 331 } 332 throw new SAMLRequesterException( 333 SAMLUtils.bundle.getString("wrongInput")); 334 } 335 contentType = ASSERTION_ID_REFERENCE; 336 if (assertionIDRefs == Collections.EMPTY_LIST) { 337 assertionIDRefs = new ArrayList(); 338 } 339 assertionIDRefs.add((AssertionIDReference) temp); 340 } else if (temp instanceof AssertionArtifact) { 341 // if this is not the first element on the list, and if the 342 // previously assigned elements are not AssertionArtifact: 343 if ((contentType != NOT_SUPPORTED) && 344 (contentType != ASSERTION_ARTIFACT)) { 345 if (SAMLUtils.debug.messageEnabled()) { 346 SAMLUtils.debug.message("Request: should contain " 347 + " one or more AssertionArtifact."); 348 } 349 throw new SAMLRequesterException( 350 SAMLUtils.bundle.getString("wrongInput")); 351 } 352 contentType = ASSERTION_ARTIFACT; 353 if (artifacts == Collections.EMPTY_LIST) { 354 artifacts = new ArrayList(); 355 } 356 artifacts.add((AssertionArtifact) temp); 357 } else { // everything else 358 SAMLUtils.debug.message("Request: wrong input."); 359 throw new SAMLRequesterException( 360 SAMLUtils.bundle.getString("wrongInput")); 361 } 362 } 363 } 364 365 /** 366 * This constructor shall only be used at the client side to construct a 367 * Request object. 368 * NOTE: The content here is just the body for the Request. The 369 * constructor will add <code>MajorVersion</code>, 370 * <code>MinorVersion</code>, etc. to form a complete Request. 371 * 372 * @param requestId If it's null, the constructor will create one. 373 * @param query A Query to be included in the Request. 374 * @throws SAMLException if an error occurs. 375 */ 376 public Request(String requestId, Query query) throws SAMLException { 377 if ((requestId != null) && (requestId.length() != 0)) { 378 requestID = requestId; 379 } else { 380 // random generate one 381 requestID = SAMLUtils.generateID(); 382 if (requestID == null) { 383 SAMLUtils.debug.error("Request: couldn't generate RequestID."); 384 throw new SAMLRequesterException( 385 SAMLUtils.bundle.getString("errorGenerateID")); 386 } 387 } 388 389 if (query == null) { 390 SAMLUtils.debug.message("Request: empty content."); 391 throw new SAMLRequesterException( 392 SAMLUtils.bundle.getString("nullInput")); 393 } 394 395 if (query instanceof AuthenticationQuery) { 396 contentType = AUTHENTICATION_QUERY; 397 } else if (query instanceof AuthorizationDecisionQuery) { 398 contentType = AUTHORIZATION_DECISION_QUERY; 399 } else if (query instanceof AttributeQuery) { 400 contentType = ATTRIBUTE_QUERY; 401 } else { 402 if (SAMLUtils.debug.messageEnabled()) { 403 SAMLUtils.debug.message("Request: this type of query is not" 404 + " supported."); 405 } 406 throw new SAMLResponderException( 407 SAMLUtils.bundle.getString("queryNotSupported")); 408 } 409 this.query = query; 410 issueInstant = newDate(); 411 } 412 413 /** 414 * This constructor shall only be used at the client side to construct a 415 * Request object. 416 * NOTE: The content here is just the body for the Request. The 417 * constructor will add <code>MajorVersion</code>, 418 * <code>MinorVersion</code>, etc. to form a complete Request. 419 * 420 * @param requestId If it's null, the constructor will create one. 421 * @param contents A List of objects that are the contents of Request that 422 * the client wants to send to the server. It could be an 423 * <code>AuthenticationQuery</code>, 424 * <code>AuthorizationDecisionQuery</code>, 425 * <code>AttributeQuery</code>, 1 or more 426 * <code>AssertionIDReference</code>, or 1 or more of 427 * <code>AssertionArtifact</code>. 428 * @throws SAMLException if an error occurs. 429 */ 430 public Request(String requestId, List contents) throws SAMLException { 431 if (requestId != null) { 432 requestID = requestId; 433 } else { 434 // random generate one 435 requestID = SAMLUtils.generateID(); 436 if (requestID == null) { 437 throw new SAMLRequesterException( 438 SAMLUtils.bundle.getString("errorGenerateID")); 439 } 440 } 441 parseContents(contents); 442 issueInstant = newDate(); 443 } 444 445 /** 446 * This method shall only be used at the server side to reconstruct 447 * a Request object based on the XML document received from client. 448 * The schema of this XML document is described above. 449 * 450 * @param xml The Request XML String. 451 * NOTE: this is a complete SAML request XML string with 452 * <code>RequestID</code>, <code>MajorVersion</code>, etc. 453 * @return Request object 454 * @exception SAMLException if an error occurs. 455 */ 456 public static Request parseXML(String xml) throws SAMLException { 457 // parse the xml string 458 Document doc = XMLUtils.toDOMDocument(xml, SAMLUtils.debug); 459 Element root = doc.getDocumentElement(); 460 461 return new Request(root); 462 } 463 464 /** 465 * Constructor. 466 * 467 * @param root <code>Request</code> element 468 * @throws SAMLException 469 */ 470 public Request(Element root) throws SAMLException { 471 // Make sure this is a Request 472 String tag = null; 473 if (root == null) { 474 SAMLUtils.debug.message("Request(Element): null input."); 475 throw new SAMLRequesterException( 476 SAMLUtils.bundle.getString("nullInput")); 477 } 478 if (((tag = root.getLocalName()) == null) || 479 (!tag.equals("Request"))) { 480 SAMLUtils.debug.message("Request(Element): wrong input"); 481 throw new SAMLRequesterException( 482 SAMLUtils.bundle.getString("wrongInput")); 483 } 484 485 List signs = XMLUtils.getElementsByTagNameNS1(root, 486 SAMLConstants.XMLSIG_NAMESPACE_URI, 487 SAMLConstants.XMLSIG_ELEMENT_NAME); 488 int signsSize = signs.size(); 489 if (signsSize == 1) { 490 XMLSignatureManager manager = XMLSignatureManager.getInstance(); 491 valid = manager.verifyXMLSignature(root, 492 REQUEST_ID_ATTRIBUTE, null); 493 if (!valid) { 494 if (SAMLUtils.debug.messageEnabled()) { 495 SAMLUtils.debug.message("Request(Element): couldn't verify" 496 + " Request's signature."); 497 } 498 } 499 xmlString = XMLUtils.print(root); 500 signed = true; 501 } else if (signsSize != 0) { 502 if (SAMLUtils.debug.messageEnabled()) { 503 SAMLUtils.debug.message("Request(Element): included more than" 504 + " one Signature element."); 505 } 506 throw new SAMLRequesterException( 507 SAMLUtils.bundle.getString("moreElement")); 508 } 509 510 // Attribute RequestID 511 requestID = root.getAttribute("RequestID"); 512 if ((requestID == null) || (requestID.length() == 0)) { 513 if (SAMLUtils.debug.messageEnabled()) { 514 SAMLUtils.debug.message("Request(Element): Request doesn't " 515 + "have a RequestID."); 516 } 517 throw new SAMLRequesterException( 518 SAMLUtils.bundle.getString("missingAttribute")); 519 } 520 521 // Attribute MajorVersion 522 parseMajorVersion(requestID, root.getAttribute("MajorVersion")); 523 524 // Attribute MinorVersion 525 parseMinorVersion(requestID, root.getAttribute("MinorVersion")); 526 527 // Attribute IssueInstant 528 String instantString = root.getAttribute("IssueInstant"); 529 if ((instantString == null) || (instantString.length() == 0)) { 530 SAMLUtils.debug.message("Request(Element): missing IssueInstant"); 531 throw new SAMLRequesterException( 532 SAMLUtils.bundle.getString("missingAttribute")); 533 } else { 534 try { 535 issueInstant = DateUtils.stringToDate(instantString); 536 } catch (ParseException e) { 537 SAMLUtils.debug.message( 538 "Request(Element): could not parse IssueInstant", e); 539 throw new SAMLRequesterException(SAMLUtils.bundle.getString( 540 "wrongInput")); 541 } 542 } 543 544 // get the contents of the request 545 NodeList contentnl = root.getChildNodes(); 546 Node child; 547 String nodeName; 548 String respondWith; 549 for (int i = 0, length = contentnl.getLength(); i < length; i++) { 550 child = contentnl.item(i); 551 if ((nodeName = child.getLocalName()) != null) { 552 if (nodeName.equals("RespondWith")) { 553 respondWith = XMLUtils.getElementValue((Element) child); 554 if (respondWith.length() == 0) { 555 if (SAMLUtils.debug.messageEnabled()) { 556 SAMLUtils.debug.message("Request(Element): wrong " 557 + "RespondWith value."); 558 } 559 throw new SAMLRequesterException( 560 SAMLUtils.bundle.getString("wrongInput")); 561 } 562 if (respondWiths == Collections.EMPTY_LIST) { 563 respondWiths = new ArrayList(); 564 } 565 respondWiths.add(respondWith); 566 } else if (nodeName.equals("Signature")) { 567 signature = (Element) child; 568 } else if (nodeName.equals("AuthenticationQuery")) { 569 // make sure the content is not assigned already 570 if (contentType != NOT_SUPPORTED) { 571 if (SAMLUtils.debug.messageEnabled()) { 572 SAMLUtils.debug.message("Request(Element): should" 573 + "contain only one AuthenticationQuery."); 574 } 575 throw new SAMLRequesterException( 576 SAMLUtils.bundle.getString("wrongInput")); 577 } 578 contentType = AUTHENTICATION_QUERY; 579 query = new AuthenticationQuery((Element) child); 580 } else if (nodeName.equals("AuthorizationDecisionQuery")) { 581 // make sure content is not assigned already 582 if (contentType != NOT_SUPPORTED) { 583 if (SAMLUtils.debug.messageEnabled()) { 584 SAMLUtils.debug.message("Request(Element): should" 585 + "contain only one " 586 + "AuthorizationDecisionQuery."); 587 } 588 throw new SAMLRequesterException( 589 SAMLUtils.bundle.getString("wrongInput")); 590 } 591 contentType = AUTHORIZATION_DECISION_QUERY; 592 query = new AuthorizationDecisionQuery((Element) child); 593 } else if (nodeName.equals("AttributeQuery")) { 594 // make sure content is not assigned already 595 if (contentType != NOT_SUPPORTED) { 596 if (SAMLUtils.debug.messageEnabled()) { 597 SAMLUtils.debug.message("Request(Element): should" 598 + "contain only one AttributeQuery."); 599 } 600 throw new SAMLRequesterException( 601 SAMLUtils.bundle.getString("wrongInput")); 602 } 603 contentType = ATTRIBUTE_QUERY; 604 query = new AttributeQuery((Element) child); 605 } else if (nodeName.equals("AssertionIDReference")) { 606 // make sure the content has no other elements assigned 607 if ((contentType != NOT_SUPPORTED) && 608 (contentType != ASSERTION_ID_REFERENCE)) { 609 if (SAMLUtils.debug.messageEnabled()) { 610 SAMLUtils.debug.message("Request(Element): " 611 + "contained mixed contents."); 612 } 613 throw new SAMLRequesterException( 614 SAMLUtils.bundle.getString("wrongInput")); 615 } 616 contentType = ASSERTION_ID_REFERENCE; 617 if (assertionIDRefs == Collections.EMPTY_LIST) { 618 assertionIDRefs = new ArrayList(); 619 } 620 assertionIDRefs.add(new AssertionIDReference( 621 XMLUtils.getElementValue((Element) child))); 622 } else if (nodeName.equals("AssertionArtifact")) { 623 // make sure the content has no other elements assigned 624 if ((contentType != NOT_SUPPORTED) && 625 (contentType != ASSERTION_ARTIFACT)) { 626 if (SAMLUtils.debug.messageEnabled()) { 627 SAMLUtils.debug.message("Request(Element): " 628 + "contained mixed contents."); 629 } 630 throw new SAMLRequesterException( 631 SAMLUtils.bundle.getString("wrongInput")); 632 } 633 contentType = ASSERTION_ARTIFACT; 634 if (artifacts == Collections.EMPTY_LIST) { 635 artifacts = new ArrayList(); 636 } 637 artifacts.add(new AssertionArtifact( 638 XMLUtils.getElementValue((Element) child))); 639 } else if (nodeName.equals("Query") || 640 nodeName.equals("SubjectQuery")) { 641 parseQuery(child); 642 } else { 643 if (SAMLUtils.debug.messageEnabled()) { 644 SAMLUtils.debug.message("Request(Element): invalid" 645 + " node" + nodeName); 646 } 647 throw new SAMLRequesterException( 648 SAMLUtils.bundle.getString("wrongInput")); 649 } // check nodeName 650 } // if nodeName != null 651 } // done for the nodelist loop 652 653 if (contentType == NOT_SUPPORTED) { 654 SAMLUtils.debug.message("Request: empty content."); 655 throw new SAMLRequesterException( 656 SAMLUtils.bundle.getString("wrongInput")); 657 } 658 } 659 660 /** 661 * Parse the input and set the majorVersion accordingly. 662 * @param majorVer a String representing the MajorVersion to be set. 663 * @exception SAMLException when the version mismatchs. 664 */ 665 private void parseMajorVersion(String reqID, String majorVer) 666 throws SAMLException { 667 try { 668 majorVersion = Integer.parseInt(majorVer); 669 } catch (NumberFormatException e) { 670 if (SAMLUtils.debug.messageEnabled()) { 671 SAMLUtils.debug.message("Request(Element): invalid " 672 + "MajorVersion", e); 673 } 674 throw new SAMLRequesterException( 675 SAMLUtils.bundle.getString("wrongInput")); 676 } 677 678 if (majorVersion != SAMLConstants.PROTOCOL_MAJOR_VERSION) { 679 if (majorVersion > SAMLConstants.PROTOCOL_MAJOR_VERSION) { 680 if (SAMLUtils.debug.messageEnabled()) { 681 SAMLUtils.debug.message("Request(Element):MajorVersion of " 682 + "the Request is too high."); 683 } 684 throw new SAMLRequestVersionTooHighException(reqID + "|"+ 685 SAMLUtils.bundle.getString("requestVersionTooHigh")); 686 } else { 687 if (SAMLUtils.debug.messageEnabled()) { 688 SAMLUtils.debug.message("Request(Element):MajorVersion of " 689 + "the Request is too low."); 690 } 691 throw new SAMLRequestVersionTooLowException(reqID + "|"+ 692 SAMLUtils.bundle.getString("requestVersionTooLow")); 693 } 694 } 695 696 } 697 698 /** 699 * Parse the input and set the minorVersion accordingly. 700 * @param minorVer a String representing the MinorVersion to be set. 701 * @exception SAMLException when the version mismatchs. 702 */ 703 private void parseMinorVersion(String reqID, String minorVer) 704 throws SAMLException { 705 try { 706 minorVersion = Integer.parseInt(minorVer); 707 } catch (NumberFormatException e) { 708 if (SAMLUtils.debug.messageEnabled()) { 709 SAMLUtils.debug.message("Request(Element): invalid " 710 + "MinorVersion", e); 711 } 712 throw new SAMLRequesterException( 713 SAMLUtils.bundle.getString("wrongInput")); 714 } 715 716 if (minorVersion > SAMLConstants.PROTOCOL_MINOR_VERSION_ONE) { 717 if (SAMLUtils.debug.messageEnabled()) { 718 SAMLUtils.debug.message("Request(Element): MinorVersion" 719 + " of the Request is too high."); 720 } 721 throw new SAMLRequestVersionTooHighException(reqID + "|"+ 722 SAMLUtils.bundle.getString("requestVersionTooHigh")); 723 } else if (minorVersion < SAMLConstants.PROTOCOL_MINOR_VERSION_ZERO) { 724 if (SAMLUtils.debug.messageEnabled()) { 725 SAMLUtils.debug.message("Request(Element): MinorVersion" 726 + " of the Request is too low."); 727 } 728 throw new SAMLRequestVersionTooLowException( reqID + "|"+ 729 SAMLUtils.bundle.getString("requestVersionTooLow")); 730 } 731 } 732 733 /** 734 * This method parses the Query or SubjectQuery represented by a DOM tree 735 * Node. It then checks and sets data members if it is a supported query, 736 * such as AuthenticationQuery, AttributeQeury, or 737 * <code>AuthorizationDecisionQuery</code>. 738 * @param child A DOM Node to be parsed. 739 * @exception SAMLException if it's not a supported query. 740 */ 741 private void parseQuery(Node child) throws SAMLException { 742 NamedNodeMap nm = child.getAttributes(); 743 int len = nm.getLength(); 744 String attrName; 745 String attrValue; 746 Attr attr; 747 boolean found = false; 748 for (int j = 0; j < len; j++) { 749 attr = (Attr) nm.item(j); 750 attrName = attr.getLocalName(); 751 if ((attrName != null) && (attrName.equals("type"))) { 752 attrValue = attr.getNodeValue(); 753 if (attrValue.equals("AuthenticationQueryType")) { 754 if (contentType != NOT_SUPPORTED) { 755 if (SAMLUtils.debug.messageEnabled()) { 756 SAMLUtils.debug.message("Request(Element): should" 757 + " contain only one AuthenticationQuery."); 758 } 759 throw new SAMLRequesterException( 760 SAMLUtils.bundle.getString("wrongInput")); 761 } 762 contentType = AUTHENTICATION_QUERY; 763 query = new AuthenticationQuery((Element) child); 764 } else if (attrValue.equals( 765 "AuthorizationDecisionQueryType")) { 766 if (contentType != NOT_SUPPORTED) { 767 if (SAMLUtils.debug.messageEnabled()) { 768 SAMLUtils.debug.message("Request(Element): should " 769 + "contain one AuthorizationDecisionQuery."); 770 } 771 throw new SAMLRequesterException(SAMLUtils. 772 bundle.getString("wrongInput")); 773 } 774 contentType = AUTHORIZATION_DECISION_QUERY; 775 query = new AuthorizationDecisionQuery((Element) child); 776 } else if (attrValue.equals("AttributeQueryType")) { 777 if (contentType != NOT_SUPPORTED) { 778 if (SAMLUtils.debug.messageEnabled()) { 779 SAMLUtils.debug.message("Request(Element): should " 780 + "contain one AttributeQuery."); 781 } 782 throw new SAMLRequesterException(SAMLUtils. 783 bundle.getString("wrongInput")); 784 } 785 contentType = ATTRIBUTE_QUERY; 786 query = new AttributeQuery((Element) child); 787 } else { 788 if (SAMLUtils.debug.messageEnabled()) { 789 SAMLUtils.debug.message("Request(Element): This type of" 790 + " " + attrName + " is not supported."); 791 } 792 throw new SAMLResponderException( 793 SAMLUtils.bundle.getString("queryNotSupported")); 794 } // check typevalue 795 found = true; 796 break; 797 } // if found type attribute 798 } // end attribute for loop 799 // if not found type 800 if (!found) { 801 if (SAMLUtils.debug.messageEnabled()) { 802 SAMLUtils.debug.message("Request(Element): missing" 803 + " xsi:type definition in " + child.getLocalName()); 804 } 805 throw new SAMLRequesterException( 806 SAMLUtils.bundle.getString("wrongInput")); 807 } 808 } 809 810 /** 811 * Gets the query of the Request. 812 * 813 * @return the query included in the request; or null if the 814 * <code>contentType</code> of the request is not 815 * <code>AUTHENTICATION_QUERY</code>, 816 * <code>AUTHORIZATION_DECISION_QUERY</code>, or 817 * <code>ATTRIBUTE_QUERY</code>. 818 */ 819 public Query getQuery() { 820 return query; 821 } 822 823 /** 824 * Gets the <code>AssertionIDReference</code>(s) of the Request. 825 * @return a List of <code>AssertionIDReference</code>s included in the 826 * request; or <code>Collections.EMPTY_LIST</code> if the 827 * <code>contentType</code> of the request is not 828 * <code>ASSERTION_ID_REFERENCE</code>. 829 */ 830 public List getAssertionIDReference() { 831 return assertionIDRefs; 832 } 833 834 /** 835 * Gets the <code>AssertionArtifact</code>(s) of the Request. 836 * @return a List of <code>AssertionArtifact</code>s included in the 837 * request; or <code>Collections.EMPTY_LIST</code> if the 838 * <code>contentType</code> of the request is not 839 * <code>ASSERTION_ARTIFACT</code>. 840 */ 841 public List getAssertionArtifact() { 842 return artifacts; 843 } 844 845 /** 846 * Returns the type of content this Request has. 847 * 848 * @return The type of the content. The possible values are defined in 849 * Request. 850 */ 851 public int getContentType() { 852 return contentType; 853 } 854 855 /** 856 * Set the signature for the Response. 857 * 858 * @param elem <code>ds:Signature</code> element 859 * @return true if the operation succeeds. 860 */ 861 public boolean setSignature(Element elem) { 862 signatureString = XMLUtils.print(elem); 863 return super.setSignature(elem); 864 } 865 866 /** 867 * This method translates the request to an XML document String based on 868 * the Request schema described above. 869 * NOTE: this is a complete SAML request XML string with 870 * <code>RequestID</code>, <code>MajorVersion</code>, etc. 871 * 872 * @return An XML String representing the request. 873 */ 874 public String toString() { 875 return toString(true, true); 876 } 877 878 /** 879 * Returns a String representation of the 880 * <code><samlp:Request></code> element. 881 * 882 * @param includeNS Determines whether or not the namespace qualifier 883 * is prepended to the Element when converted 884 * @param declareNS Determines whether or not the namespace is declared 885 * within the Element. 886 * @return A string containing the valid XML for this element 887 */ 888 public String toString(boolean includeNS, boolean declareNS) { 889 return toString(includeNS, declareNS, false); 890 } 891 892 /** 893 * Returns a String representation of the 894 * <code><samlp:Request></code> element. 895 * 896 * @param includeNS Determines whether or not the namespace qualifier 897 * is prepended to the Element when converted 898 * @param declareNS Determines whether or not the namespace is declared 899 * within the Element. 900 * @param includeHeader Determines whether the output include the XML 901 * declaration header. 902 * @return A string containing the valid XML for this element 903 */ 904 public String toString(boolean includeNS, 905 boolean declareNS, 906 boolean includeHeader) 907 { 908 if (signed && (xmlString != null)) { 909 return xmlString; 910 } 911 912 StringBuffer xml = new StringBuffer(300); 913 if (includeHeader) { 914 xml.append("<?xml version=\"1.0\" encoding=\""). 915 append(SAMLConstants.DEFAULT_ENCODING).append("\" ?>\n"); 916 } 917 String prefix = ""; 918 String uri = ""; 919 if (includeNS) { 920 prefix = SAMLConstants.PROTOCOL_PREFIX; 921 } 922 if (declareNS) { 923 uri = SAMLConstants.PROTOCOL_NAMESPACE_STRING; 924 } 925 String instantString = DateUtils.toUTCDateFormat(issueInstant); 926 927 xml.append("<").append(prefix).append("Request").append(uri). 928 append(" RequestID=\"").append(requestID).append("\""). 929 append(" MajorVersion=\"").append(majorVersion).append("\""). 930 append(" MinorVersion=\"").append(minorVersion).append("\""). 931 append(" IssueInstant=\"").append(instantString).append("\""). 932 append(">\n"); 933 if((respondWiths != null) && (respondWiths != Collections.EMPTY_LIST)){ 934 Iterator i = respondWiths.iterator(); 935 String respondWith = null; 936 while (i.hasNext()) { 937 respondWith = (String) i.next(); 938 xml.append("<").append(prefix).append("RespondWith>"); 939 if (respondWith.startsWith(SAMLConstants.ASSERTION_PREFIX)) { 940 xml.append(respondWith); 941 } else { 942 try { 943 xml.append(checkAndGetRespondWith(respondWith)); 944 } catch (SAMLException e) { 945 SAMLUtils.debug.error("Request.toString: ", e); 946 xml.append(respondWith); 947 } 948 } 949 xml.append("</").append(prefix).append("RespondWith>\n"); 950 } 951 } 952 953 if (signed) { 954 if (signatureString != null) { 955 xml.append(signatureString); 956 } else if (signature != null) { 957 signatureString = XMLUtils.print(signature); 958 xml.append(signatureString); 959 } 960 } 961 962 Iterator j; 963 switch (contentType) { 964 case AUTHENTICATION_QUERY: 965 xml.append(((AuthenticationQuery)query).toString(includeNS, false)); 966 break; 967 case AUTHORIZATION_DECISION_QUERY: 968 xml.append(((AuthorizationDecisionQuery)query).toString(includeNS, 969 false)); 970 break; 971 case ATTRIBUTE_QUERY: 972 xml.append(((AttributeQuery)query).toString(includeNS, false)); 973 break; 974 case ASSERTION_ID_REFERENCE: 975 j = assertionIDRefs.iterator(); 976 while (j.hasNext()) { 977 xml.append(((AssertionIDReference) j.next()). 978 toString(true, true)); 979 } 980 break; 981 case ASSERTION_ARTIFACT: 982 j = artifacts.iterator(); 983 while (j.hasNext()) { 984 xml.append(((AssertionArtifact) 985 j.next()).toString(includeNS, false)); 986 } 987 break; 988 default: 989 break; 990 } 991 992 xml.append("</").append(prefix).append("Request>\n"); 993 return xml.toString(); 994 } 995}