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