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