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: FSNameIdentifierMappingResponse.java,v 1.2 2008/06/25 05:46:44 qcheng Exp $ 026 * 027 * Portions Copyrighted 2014-2016 ForgeRock AS. 028 */ 029 030package com.sun.identity.federation.message; 031 032import static org.forgerock.openam.utils.Time.*; 033 034import com.sun.identity.shared.xml.XMLUtils; 035 036import com.sun.identity.shared.Constants; 037import com.sun.identity.shared.DateUtils; 038import com.sun.identity.common.SystemConfigurationUtil; 039 040import com.sun.identity.federation.common.FSUtils; 041import com.sun.identity.federation.common.IFSConstants; 042import com.sun.identity.federation.message.common.FSMsgException; 043 044import com.sun.identity.saml.assertion.NameIdentifier; 045 046import com.sun.identity.saml.common.SAMLConstants; 047import com.sun.identity.saml.common.SAMLException; 048import com.sun.identity.saml.common.SAMLResponderException; 049 050import com.sun.identity.saml.protocol.AbstractResponse; 051import com.sun.identity.saml.protocol.Status; 052 053import com.sun.identity.saml.xmlsig.XMLSignatureManager; 054 055import java.util.Date; 056import java.util.List; 057 058import org.w3c.dom.Document; 059import org.w3c.dom.Element; 060import org.w3c.dom.Node; 061import org.w3c.dom.NodeList; 062 063/** 064 * The class <code>FSNameIdentifierMappingResponse</code> is used to 065 * create , parse the <code>NameIdentifierMappingResponse</code>. 066 * 067 * @supported.all.api 068 * @deprecated since 12.0.0 069 */ 070@Deprecated 071public class FSNameIdentifierMappingResponse extends AbstractResponse { 072 073 private String providerID; 074 private Status status; 075 private NameIdentifier nameIdentifier; 076 private int minorVersion = IFSConstants.FF_12_PROTOCOL_MINOR_VERSION; 077 private String signatureString; 078 079 /** 080 * Constructor to create <code>FSNameIdentifierMappingResponse</code> object. 081 * 082 * @param providerID the value of <code>ProviderID</code> attribute. 083 * @param inResponseTo the value of <code>InResponseTo</code> attribute. 084 * @param status the <code>Status</code> object. 085 * @param nameIdentifier the resulting mapped identifier, 086 * <code>NameIdentifier</code>, for the desired identity federation. 087 * @throws FSMsgException on an error. 088 */ 089 public FSNameIdentifierMappingResponse(String providerID, 090 String inResponseTo, Status status, 091 NameIdentifier nameIdentifier) throws FSMsgException { 092 this.providerID = providerID; 093 this.inResponseTo = inResponseTo; 094 this.status = status; 095 this.nameIdentifier = nameIdentifier; 096 this.responseID = FSUtils.generateID(); 097 setIssueInstant(newDate()); 098 } 099 100 /** 101 * Constructor to create <code>FSNameIdentifierMappingResponse</code> object. 102 * This object is created from the Document Element. 103 * 104 * @param root the <code>NameIdentifierMappingResponse</code> Document 105 * Element 106 * @throws FSMsgException if there is an error creating the object. 107 */ 108 public FSNameIdentifierMappingResponse(Element root) 109 throws FSMsgException { 110 if (root == null) { 111 FSUtils.debug.message( 112 "FSNameIdentifierMappingResponse: null element input"); 113 throw new FSMsgException("nullInputParameter",null); 114 } 115 String tag = null; 116 if (((tag = root.getLocalName()) == null) || 117 (!tag.equals("NameIdentifierMappingResponse"))) { 118 FSUtils.debug.message( 119 "FSNameIdentifierMappingRequest: wrong input"); 120 throw new FSMsgException("wrongInput",null); 121 } 122 123 // get IssueInstant 124 String instantString = root.getAttribute(IFSConstants.ISSUE_INSTANT); 125 if (instantString==null || instantString.length()==0) { 126 FSUtils.debug.error("FSNameIdentifierMappingResponse: " + 127 "missing IssueInstant"); 128 String[] args = { IFSConstants.ISSUE_INSTANT }; 129 throw new FSMsgException("missingAttribute",args); 130 } else { 131 try { 132 issueInstant = DateUtils.stringToDate(instantString); 133 } catch (Exception e) { 134 FSUtils.debug.error("FSNameIdentifierMappingResponse: " + 135 "could not parse IssueInstant.", e); 136 throw new FSMsgException("wrongInput",null); 137 } 138 } 139 140 // get ResponseID 141 responseID = root.getAttribute(IFSConstants.RESPONSE_ID); 142 if ((responseID == null) || (responseID.length() < 1)) { 143 FSUtils.debug.error("FSNameIdentifierMappingResponse: " + 144 "response doesn't have ResponseID"); 145 String[] args = { IFSConstants.RESPONSE_ID }; 146 throw new FSMsgException("missingAttribute",args); 147 } 148 149 // get InResponseTo 150 inResponseTo = root.getAttribute(IFSConstants.IN_RESPONSE_TO); 151 if (inResponseTo == null) { 152 FSUtils.debug.error("FSNameIdentifierMappingResponse: " + 153 "response doesn't have InResponseTo"); 154 String[] args = { IFSConstants.IN_RESPONSE_TO }; 155 throw new FSMsgException("missingAttribute",args); 156 } 157 158 // get and check versions 159 parseMajorVersion(root.getAttribute(IFSConstants.MAJOR_VERSION)); 160 parseMinorVersion(root.getAttribute(IFSConstants.MINOR_VERSION)); 161 162 // get ProviderID, Status & NameIdentifier 163 NodeList nl = root.getChildNodes(); 164 Node child; 165 String childName; 166 int length = nl.getLength(); 167 for (int i = 0; i < length; i++) { 168 child = nl.item(i); 169 if ((childName = child.getLocalName()) != null) { 170 if (childName.equals(IFSConstants.STATUS)) { 171 try { 172 status = new Status((Element) child); 173 } catch (SAMLException se) { 174 FSUtils.debug.error("FSNameIdentifierMappingResponse:" + 175 " unable to initialize Status", se); 176 throw new FSMsgException("statusCreateError",null,se); 177 } 178 } else if (childName.equals(IFSConstants.PROVIDER_ID)) { 179 providerID = XMLUtils.getElementValue((Element) child); 180 } else if (childName.equals(IFSConstants.NAME_IDENTIFIER)) { 181 try { 182 nameIdentifier = 183 new NameIdentifier((Element) child); 184 } catch (SAMLException samle) { 185 FSUtils.debug.error("FSNameIdentifierMappingResponse:" + 186 " unable to initialize NameIdentifier", samle); 187 throw new FSMsgException("nameIdentifierCreateError", 188 null,samle); 189 } 190 } 191 } 192 } 193 194 // get signature 195 List signs = XMLUtils.getElementsByTagNameNS1( 196 root, 197 SAMLConstants.XMLSIG_NAMESPACE_URI, 198 SAMLConstants.XMLSIG_ELEMENT_NAME); 199 int signsSize = signs.size(); 200 if (signsSize == 1) { 201 Element elem = (Element)signs.get(0); 202 setSignature(elem); 203 signed = true; 204 } else if (signsSize != 0) { 205 FSUtils.debug.error("FSNameIdentifierMappingResponse: " + 206 "included more than one Signature element."); 207 throw new FSMsgException("moreElement",null); 208 } 209 } 210 211 /** 212 * Creates <code>FSNameIdentifierMappingResponse</code> object. 213 * This object is created by parsing the <code>XML</code> string. 214 * 215 * @param xml the <code>XML</code> string to be parse. 216 * @return the <code>FSNameIdentifierMappingResponse</code> object. 217 * @throws FSMsgException if there is an error in parsing the 218 * <code>XML</code> string. 219 */ 220 public static FSNameIdentifierMappingResponse parseXML(String xml) 221 throws FSMsgException { 222 Document doc = XMLUtils.toDOMDocument(xml, FSUtils.debug); 223 if (doc == null) { 224 FSUtils.debug.error("FSNameIdentifierMappingResponse.parseXML: " + 225 "error while parsing input xml string"); 226 throw new FSMsgException("parseError",null); 227 } 228 Element root = doc.getDocumentElement(); 229 return new FSNameIdentifierMappingResponse(root); 230 } 231 232 /** 233 * Returns the value of <code>ProviderID</code> attribute. 234 * 235 * @return the value of <code>ProviderID</code> attribute. 236 */ 237 public String getProviderID() { 238 return providerID; 239 } 240 241 /** 242 * Returns the <code>Status</code>. 243 * 244 * @return the <code>Status</code>. 245 */ 246 public Status getStatus() { 247 return status; 248 } 249 250 /** 251 * Returns the <code>NameIdentifier</code> object. This is the resulting 252 * mapped name identifier for the desired identity federation. 253 * 254 * @return <code>NameIdentifier</code> object, the resulting mapped name 255 * identifier for the desired identity federation 256 */ 257 public NameIdentifier getNameIdentifier() { 258 return nameIdentifier; 259 } 260 261 /** 262 * Sets the <code>MajorVersion</code> by parsing the version string. 263 * 264 * @param version a String representing the <code>MajorVersion</code> to 265 * be set. 266 * @throws FSMsgException on error. 267 */ 268 private void parseMajorVersion(String version) throws FSMsgException { 269 try { 270 majorVersion = Integer.parseInt(version); 271 } catch (NumberFormatException e) { 272 if (FSUtils.debug.messageEnabled()) { 273 FSUtils.debug.message("FSNameIdentifierMappingResponse." + 274 "parseMajorVersion:invalid MajorVersion:" + version, e); 275 } 276 throw new FSMsgException("wrongInput",null); 277 } 278 279 if (majorVersion != SAMLConstants.PROTOCOL_MAJOR_VERSION) { 280 if (majorVersion > SAMLConstants.PROTOCOL_MAJOR_VERSION) { 281 if (FSUtils.debug.messageEnabled()) { 282 FSUtils.debug.message("FSNameIdentifierMappingResponse." + 283 "parseMajorVersion: MajorVersion is too high"); 284 } 285 throw new FSMsgException("requestVersionTooHigh",null); 286 } else { 287 if (FSUtils.debug.messageEnabled()) { 288 FSUtils.debug.message("FSNameIdentifierMappingResponse." + 289 "parseMajorVersion: MajorVersion is too low"); 290 } 291 throw new FSMsgException("requestVersionTooLow",null); 292 } 293 } 294 } 295 296 /** 297 * Sets the <code>MinorVersion</code> by parsing the version string. 298 * 299 * @param minorVer a String representing the <code>MinorVersion</code> to 300 * be set. 301 * @throws FSMsgException when the version mismatchs. 302 */ 303 private void parseMinorVersion(String version) throws FSMsgException { 304 try { 305 minorVersion = Integer.parseInt(version); 306 } catch (NumberFormatException e) { 307 if (FSUtils.debug.messageEnabled()) { 308 FSUtils.debug.message("FSNameIdentifierMappingResponse." + 309 "parseMinorVersion:invalid MinorVersion:" + version, e); 310 } 311 throw new FSMsgException("wrongInput",null); 312 } 313 if (minorVersion > IFSConstants.FF_12_PROTOCOL_MINOR_VERSION) { 314 if (FSUtils.debug.messageEnabled()) { 315 FSUtils.debug.message("FSNameIdentifierMappingResponse." + 316 "parseMinorVersion: MinorVersion is too high"); 317 } 318 throw new FSMsgException("requestVersionTooHigh",null); 319 } else if (minorVersion < IFSConstants.FF_11_PROTOCOL_MINOR_VERSION) { 320 if (FSUtils.debug.messageEnabled()) { 321 FSUtils.debug.message("FSNameIdentifierMappingResponse." + 322 "parseMinorVersion: MinorVersion is too low"); 323 } 324 throw new FSMsgException("requestVersionTooLow",null); 325 } 326 } 327 328 /** 329 * Signs the XML document representing 330 * <code>NameIdentifierMappingResponse</code> using the certificate 331 * indicated by the property "com.sun.identity.saml.xmlsig.certalias" 332 * in AMConfig.properties file. 333 * 334 * @throws SAMLException if there is an error signing 335 * the <code>XML</code> string. 336 */ 337 public void signXML() throws SAMLException { 338 String certAlias = SystemConfigurationUtil.getProperty( 339 Constants.SAML_XMLSIG_CERT_ALIAS); 340 signXML(certAlias); 341 } 342 343 /** 344 * Signs the <code>XML</code> document representing 345 * <code>NameIdentifierMappingResponse</code> using the specified 346 * certificate. 347 * 348 * @param certAlias the alias/name of the certificate used for signing 349 * the XML document 350 * @throws SAMLException if there is an error signing 351 * the <code>XML</code> string or if the message is already 352 * signed. 353 */ 354 public void signXML(String certAlias) throws SAMLException { 355 FSUtils.debug.message("FSNameIdentifierMappingResponse.signXML"); 356 if (signed) { 357 if (FSUtils.debug.messageEnabled()) { 358 FSUtils.debug.message("FSNameIdentifierMappingResponse.signXML:" 359 + " the response is already signed."); 360 } 361 throw new SAMLResponderException(FSUtils.BUNDLE_NAME, 362 "alreadySigned",null); 363 } 364 if (certAlias == null || certAlias.length() < 1) { 365 if (FSUtils.debug.messageEnabled()) { 366 FSUtils.debug.message("FSNameIdentifierMappingResponse.signXML:" 367 + " null certAlias"); 368 } 369 throw new SAMLResponderException(FSUtils.BUNDLE_NAME, 370 "cannotFindCertAlias",null); 371 } 372 try { 373 XMLSignatureManager manager = XMLSignatureManager.getInstance(); 374 signatureString = manager.signXML(this.toXMLString(true, true), 375 certAlias, (String)null, 376 IFSConstants.RESPONSE_ID, 377 this.getResponseID(), false); 378 signature = XMLUtils.toDOMDocument(signatureString, FSUtils.debug) 379 .getDocumentElement(); 380 signed = true; 381 } catch (Exception e){ 382 FSUtils.debug.error("FSNameIdentifierMappingResponse.signXML: " + 383 "unable to sign", e); 384 throw new SAMLResponderException( 385 FSUtils.BUNDLE_NAME,"signFailed",null); 386 } 387 } 388 389 /** 390 * Returns the string representation of this object. 391 * 392 * @return An XML String representing the object. 393 * @throws FSMsgException on error. 394 */ 395 public String toXMLString() throws FSMsgException { 396 return toXMLString(true, true); 397 } 398 399 /** 400 * Returns a String representation of this object. 401 * 402 * @param includeNS : Determines whether or not the namespace qualifier 403 * is prepended to the Element when converted 404 * @param declareNS : Determines whether or not the namespace is declared 405 * within the Element. 406 * @return a string containing the valid XML for this element 407 * @throws FSMsgException if there is an error converting 408 * this object ot a string. 409 */ 410 411 public String toXMLString(boolean includeNS, boolean declareNS) 412 throws FSMsgException { 413 return toXMLString(includeNS, declareNS, false); 414 } 415 416 /** 417 * Returns a String representation of this object. 418 * 419 * @param includeNS Determines whether or not the namespace qualifier 420 * is prepended to the Element when converted 421 * @param declareNS Determines whether or not the namespace is declared 422 * within the Element. 423 * @param includeHeader Determines whether the output include the xml 424 * declaration header. 425 * @return a string containing the valid XML for this element 426 * @throws FSMsgException if there is an error converting 427 * this object ot a string. 428 */ 429 public String toXMLString(boolean includeNS, boolean declareNS, 430 boolean includeHeader) throws FSMsgException { 431 432 String prefixLIB = ""; 433 String uriLIB = ""; 434 String uriSAML = ""; 435 if (includeNS) { 436 prefixLIB = IFSConstants.LIB_PREFIX; 437 } 438 if (declareNS) { 439 uriLIB = IFSConstants.LIB_12_NAMESPACE_STRING; 440 uriSAML = IFSConstants.assertionDeclareStr; 441 } 442 String instantString = null; 443 try { 444 instantString = DateUtils.toUTCDateFormat(issueInstant); 445 } catch (Exception e) { 446 FSUtils.debug.error("FSNameIdentifierMappingResponse.toXMLString:" + 447 " could not convert issueInstant to String.", e); 448 } 449 // construct xml response 450 StringBuffer xml = new StringBuffer(1000); 451 if (includeHeader) { 452 xml.append(IFSConstants.XML_PREFIX) 453 .append(SAMLConstants.DEFAULT_ENCODING) 454 .append(IFSConstants.QUOTE) 455 .append(IFSConstants.SPACE) 456 .append(IFSConstants.QUESTION_MARK) 457 .append(IFSConstants.RIGHT_ANGLE) 458 .append(IFSConstants.NL); 459 } 460 xml.append(IFSConstants.LEFT_ANGLE) 461 .append(prefixLIB) 462 .append(IFSConstants.NAMEID_MAPPING_RESPONSE) 463 .append(uriLIB).append(uriSAML) 464 .append(IFSConstants.SPACE) 465 .append(IFSConstants.RESPONSE_ID) 466 .append(IFSConstants.EQUAL_TO) 467 .append(IFSConstants.QUOTE) 468 .append(responseID) 469 .append(IFSConstants.QUOTE) 470 .append(IFSConstants.SPACE) 471 .append(IFSConstants.IN_RESPONSE_TO) 472 .append(IFSConstants.EQUAL_TO) 473 .append(IFSConstants.QUOTE) 474 .append(inResponseTo) 475 .append(IFSConstants.QUOTE) 476 .append(IFSConstants.SPACE) 477 .append(IFSConstants.SPACE) 478 .append(IFSConstants.MAJOR_VERSION) 479 .append(IFSConstants.EQUAL_TO) 480 .append(IFSConstants.QUOTE) 481 .append(majorVersion) 482 .append(IFSConstants.EQUAL_TO) 483 .append(IFSConstants.QUOTE) 484 .append(IFSConstants.SPACE) 485 .append(IFSConstants.SPACE) 486 .append(IFSConstants.MINOR_VERSION) 487 .append(IFSConstants.EQUAL_TO) 488 .append(IFSConstants.QUOTE) 489 .append(minorVersion) 490 .append(IFSConstants.QUOTE) 491 .append(IFSConstants.SPACE) 492 .append(IFSConstants.SPACE) 493 .append(IFSConstants.ISSUE_INSTANT) 494 .append(IFSConstants.EQUAL_TO) 495 .append(instantString) 496 .append(IFSConstants.QUOTE) 497 .append(IFSConstants.SPACE) 498 .append(IFSConstants.RIGHT_ANGLE); 499 if (signed) { 500 if (signatureString != null) { 501 xml.append(signatureString); 502 } else if (signature != null) { 503 signatureString = XMLUtils.print(signature); 504 xml.append(signatureString); 505 } 506 } 507 xml.append(IFSConstants.LEFT_ANGLE) 508 .append(prefixLIB) 509 .append(IFSConstants.PROVIDER_ID) 510 .append(IFSConstants.RIGHT_ANGLE) 511 .append(providerID) 512 .append(IFSConstants.START_END_ELEMENT) 513 .append(prefixLIB) 514 .append(IFSConstants.PROVIDER_ID) 515 .append(IFSConstants.RIGHT_ANGLE) 516 .append(status.toString(includeNS, true)); 517 518 if (nameIdentifier != null) { 519 xml.append(nameIdentifier.toString()); 520 } 521 xml.append(IFSConstants.START_END_ELEMENT) 522 .append(prefixLIB) 523 .append(IFSConstants.NAMEID_MAPPING_RESPONSE) 524 .append(IFSConstants.RIGHT_ANGLE); 525 526 return xml.toString(); 527 } 528}