Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

914 lines
26 KiB

  1. using System;
  2. using System.IO;
  3. using System.Web;
  4. using System.Data;
  5. using System.Text;
  6. using System.Xml;
  7. using System.Data.SqlClient;
  8. using System.Xml.Serialization;
  9. using System.Security.Cryptography.X509Certificates;
  10. using System.Security.Principal;
  11. using System.Web.Services.Protocols;
  12. using System.Web.Security;
  13. using UDDI;
  14. using UDDI.Diagnostics;
  15. using UDDI.API.Authentication;
  16. namespace UDDI.API
  17. {
  18. public class UDDIExtension : SoapExtension
  19. {
  20. Data data;
  21. DateTime begin;
  22. private class Data
  23. {
  24. public bool log = true;
  25. public bool https = false;
  26. public bool validate = true;
  27. public bool performance = true;
  28. public bool authenticate = false;
  29. public bool transaction = false;
  30. public bool certificate = false;
  31. public string messageType = "";
  32. public Data(
  33. bool log,
  34. bool validate,
  35. bool performance,
  36. bool authenticate,
  37. bool transaction,
  38. bool https,
  39. bool certificate,
  40. string messageType )
  41. {
  42. this.log = log;
  43. this.https = https;
  44. this.validate = validate;
  45. this.performance = performance;
  46. this.authenticate = authenticate;
  47. this.transaction = transaction;
  48. this.certificate = certificate;
  49. this.messageType = messageType;
  50. }
  51. }
  52. static UDDIExtension()
  53. {
  54. Context.ContextType = ContextType.SOAP;
  55. }
  56. private void CheckForHttps( SoapMessage message )
  57. {
  58. Debug.Enter();
  59. if( 1 == Config.GetInt( "Security.HTTPS", 1 ) )
  60. {
  61. Debug.Write( SeverityType.Info, CategoryType.Soap, "URL: " + message.Url );
  62. Debug.Verify( message.Url.ToLower().StartsWith( "https" ), "UDDI_ERROR_FATALERROR_HTTPSREQUIREDFORPUBLISH" );
  63. }
  64. else
  65. {
  66. Debug.Write( SeverityType.Warning, CategoryType.Soap, "HTTPS check is turned off. Content may be published without SSL. To turn this check on remove or modify the Security.HTTPS configuration setting" );
  67. }
  68. Debug.Leave();
  69. }
  70. private void CheckCertificate( SoapMessage message )
  71. {
  72. HttpClientCertificate httpCert = HttpContext.Current.Request.ClientCertificate;
  73. X509Certificate requestCert = new X509Certificate( httpCert.Certificate );
  74. Debug.Verify( !Utility.StringEmpty( httpCert.Issuer ), "UDDI_ERROR_FATALERROR_CLIENTCERTREQUIRED" );
  75. Debug.Verify( !Utility.StringEmpty( httpCert.Subject ), "UDDI_ERROR_FATALERROR_CLIENTCERTREQUIRED" );
  76. SqlStoredProcedureAccessor sp = new SqlStoredProcedureAccessor( "net_operatorCert_get" );
  77. sp.Parameters.Add( "@certSerialNo", SqlDbType.NVarChar, UDDI.Constants.Lengths.CertSerialNo );
  78. sp.Parameters.SetString( "@certSerialNo", requestCert.GetSerialNumberString() );
  79. SqlDataReaderAccessor reader = sp.ExecuteReader();
  80. try
  81. {
  82. if( reader.Read() )
  83. {
  84. Context.RemoteOperator = reader.GetGuidString( "operatorKey" );
  85. byte[] operatorCertRaw = reader.GetBinary( "certificate" );
  86. byte[] requestCertRaw = httpCert.Certificate;
  87. Debug.Verify(
  88. null != operatorCertRaw,
  89. "UDDI_ERROR_FATALERROR_CLIENTCERTNOTSTORED",
  90. ErrorType.E_fatalError,
  91. Context.RemoteOperator );
  92. if( operatorCertRaw.Length != requestCertRaw.Length )
  93. {
  94. throw new UDDIException(
  95. ErrorType.E_unknownUser,
  96. "UDDI_ERROR_UNKNOWNUSER_UNKOWNCERT" );
  97. }
  98. for( int i = 0; i < operatorCertRaw.Length; i ++ )
  99. {
  100. if( operatorCertRaw[ i ] != requestCertRaw[ i ] )
  101. {
  102. throw new UDDIException(
  103. ErrorType.E_unknownUser,
  104. "UDDI_ERROR_UNKNOWNUSER_UNKOWNCERT" );
  105. }
  106. }
  107. /*
  108. * TODO: Check to see if this works instead
  109. *
  110. X509Certificate operatorCert = new X509Certificate( operatorCertRaw );
  111. X509Certificate requestCert = new X509Certificate( requestCertRaw );
  112. if( !requestCert.Equals( operatorCert ) )
  113. {
  114. throw new UDDIException(
  115. ErrorType.E_unknownUser,
  116. "Unknown certificate" );
  117. }
  118. */
  119. }
  120. else
  121. {
  122. throw new UDDIException(
  123. ErrorType.E_unknownUser,
  124. "UDDI_ERROR_UNKNOWNUSER_UNKOWNCERT" );
  125. }
  126. }
  127. finally
  128. {
  129. reader.Close();
  130. }
  131. }
  132. private void Validate( SoapMessage message )
  133. {
  134. Debug.Enter();
  135. StreamReader srdr = new StreamReader( message.Stream, System.Text.Encoding.UTF8 );
  136. #if DEBUG
  137. Debug.Write( SeverityType.Verbose, CategoryType.None, srdr.ReadToEnd() );
  138. message.Stream.Seek( 0,System.IO.SeekOrigin.Begin );
  139. #endif
  140. //
  141. // Validate incoming XML, ValidateStream will rewind stream when finished
  142. // so I don't have to.
  143. //
  144. SchemaCollection.Validate( message.Stream );
  145. Debug.Leave();
  146. }
  147. private void PublishMethodBegin( SoapMessage message )
  148. {
  149. Debug.Enter();
  150. begin = DateTime.Now;
  151. Debug.Leave();
  152. }
  153. private void PublishMethodEnd( SoapMessage message )
  154. {
  155. Debug.Enter();
  156. TimeSpan duration = DateTime.Now - begin;
  157. Debug.Write( SeverityType.Info, CategoryType.Soap, "Message took " + duration.TotalMilliseconds.ToString() + " ms" );
  158. Performance.PublishMessageData( data.messageType, duration );
  159. Debug.Leave();
  160. }
  161. //
  162. // What follows is the logic for selection of the authentication algorithm
  163. // Enjoy boys and girls
  164. //
  165. // Bit 3 - Anonymous User
  166. // Bit 2 - UDDI Authentication Mode
  167. // Bit 1 - Windows Authentication Mode
  168. // Bit 0 - Ticket Present
  169. // |
  170. // | Authentication Module Used
  171. // 0000 X
  172. // 0001 X
  173. // 0010 Windows
  174. // 0011 Exception ( UDDI authentication turned off )
  175. // 0100 UDDI ( will fail authentication due to invalid credentials )
  176. // 0101 UDDI
  177. // 0110 Windows
  178. // 0111 UDDI
  179. // 1000 X
  180. // 1001 X
  181. // 1010 Exception UDDI authentication turned off
  182. // 1011 Exception ""
  183. // 1100 UDDI ( will fail authentication due to invalid credentials )
  184. // 1101 UDDI
  185. // 1110 UDDI ( will fail authentication due to invalid credentials )
  186. // 1111 UDDI
  187. //
  188. //
  189. // Reduction Work
  190. //
  191. // A - Anonymous User
  192. // B - UDDI Authentication Mode
  193. // C - Windows Authentication Mode
  194. // D - Ticket Present
  195. //
  196. // Key
  197. // e - throw exception invalid configuration
  198. // x - invalid state
  199. // w - windows authentication
  200. // u - uddi authentication
  201. //
  202. // CD
  203. // AB 00 01 11 10
  204. // 00 x x e w
  205. // 01 u u u w
  206. // 11 u u u u
  207. // 10 x x e e
  208. //
  209. // if( !A && C && !D )
  210. // w - windows authentication
  211. // else if( B )
  212. // u - uddi authentication
  213. // else
  214. // throw exception
  215. //
  216. private void Authenticate( SoapMessage message )
  217. {
  218. Debug.Enter();
  219. IAuthenticateable authenticate = (IAuthenticateable) message.GetInParameterValue(0);
  220. //WindowsIdentity identity = (WindowsIdentity)HttpContext.Current.User.Identity;
  221. IIdentity identity = HttpContext.Current.User.Identity;
  222. int mode = Config.GetInt( "Security.AuthenticationMode", (int) AuthenticationMode.Both );
  223. if( mode == (int) AuthenticationMode.Passport )
  224. {
  225. if( identity is PassportIdentity )
  226. {
  227. string ticket = authenticate.AuthInfo.Trim();
  228. //
  229. // Authentication the user using the attached passport ticket
  230. //
  231. PassportAuthenticator pa = new PassportAuthenticator();
  232. pa.Authenticate( ticket, Config.GetInt( "Security.TimeOut", 60 ) );
  233. }
  234. else
  235. {
  236. throw new UDDIException( ErrorType.E_fatalError,
  237. "UDDI_ERROR_FATALERROR_PASSPORTBADCONFIG" ) ;
  238. }
  239. Debug.Write( SeverityType.Info, CategoryType.Soap, "Authenticated user: using Passport based authentication Identity is " + identity.Name );
  240. }
  241. else if( !( (WindowsIdentity)identity ).IsAnonymous &&
  242. ( ( mode & (int) AuthenticationMode.Windows ) != 0 ) &&
  243. Utility.StringEmpty( authenticate.AuthInfo ) )
  244. {
  245. /* 0X10 Case */
  246. //
  247. // Authenticate the user using the currently impersonated credentials
  248. //
  249. WindowsAuthenticator wa = new WindowsAuthenticator();
  250. wa.Authenticate( authenticate.AuthInfo, Config.GetInt( "Security.TimeOut", 60 ) );
  251. Debug.Write( SeverityType.Info, CategoryType.Soap, "Authenticated user: using Windows based authentication Identity is " + identity.Name );
  252. }
  253. else if( ( mode & (int) AuthenticationMode.Uddi ) != 0 )
  254. {
  255. /* X1XX Case for leftovers */
  256. //
  257. // If windows authentication is turned off or the
  258. Debug.Write( SeverityType.Info, CategoryType.Soap, "Anonymous user: using UDDI authentication" );
  259. //
  260. // Authenticate the user using the authToken
  261. //
  262. UDDIAuthenticator ua = new UDDIAuthenticator();
  263. ua.Authenticate( authenticate.AuthInfo, Config.GetInt( "Security.TimeOut", 60 ) );
  264. }
  265. else
  266. {
  267. //
  268. // Throw exception for the rest
  269. //
  270. throw new UDDIException( UDDI.ErrorType.E_unsupported,
  271. "UDDI_ERROR_UNSUPPORTED_BADAUTHENTICATIONCONFIG" );
  272. }
  273. //
  274. // Check to make sure the authenticated user has publisher credentials
  275. //
  276. Debug.Verify( Context.User.IsPublisher,
  277. "UDDI_ERROR_FATALERROR_USERNOPUBLISHERCRED",
  278. UDDI.ErrorType.E_fatalError,
  279. Context.User.ID );
  280. //
  281. // The server can be configured for automatic registration of publishers with credentials
  282. //
  283. if( !Context.User.IsRegistered )
  284. {
  285. if( 1 == Config.GetInt( "Security.AutoRegister", 0 ) )
  286. {
  287. //
  288. // Mark the user as verified.
  289. //
  290. Context.User.TrackPassport = false;
  291. Context.User.Verified = true;
  292. Context.User.Register();
  293. }
  294. else
  295. {
  296. throw new UDDIException( UDDI.ErrorType.E_unknownUser,
  297. "UDDI_ERROR_UNKNOWNUSER_NOTREGISTERED" );
  298. }
  299. }
  300. Context.User.Login();
  301. #if DEBUG
  302. Debug.Write( SeverityType.Info, CategoryType.Soap, "Windows Identity is " + WindowsIdentity.GetCurrent().Name );
  303. Debug.Write( SeverityType.Info, CategoryType.Soap, "Thread Identity is " + System.Threading.Thread.CurrentPrincipal.Identity.Name );
  304. Debug.Write( SeverityType.Info, CategoryType.Soap, "HttpContext Identity is " + identity.Name );
  305. Debug.Write( SeverityType.Info, CategoryType.Soap, "IsAdministrator = " + Context.User.IsAdministrator );
  306. Debug.Write( SeverityType.Info, CategoryType.Soap, "IsCoordinator = " + Context.User.IsCoordinator );
  307. Debug.Write( SeverityType.Info, CategoryType.Soap, "IsPublisher = " + Context.User.IsPublisher );
  308. Debug.Write( SeverityType.Info, CategoryType.Soap, "IsUser = " + Context.User.IsUser );
  309. #endif
  310. Debug.Leave();
  311. }
  312. public override object GetInitializer( Type t )
  313. {
  314. return null;
  315. }
  316. public override object GetInitializer( LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute )
  317. {
  318. UDDIExtensionAttribute attr = (UDDIExtensionAttribute) attribute;
  319. return new Data( attr.log, attr.validate, attr.performance, attr.authenticate, attr.transaction, attr.https, attr.certificate, attr.messageType );
  320. }
  321. public override void Initialize( object initializer )
  322. {
  323. data = (UDDIExtension.Data) initializer;
  324. }
  325. public override void ProcessMessage(SoapMessage message)
  326. {
  327. Debug.Enter();
  328. #if DEBUG
  329. string info = "log: " + data.log.ToString() +
  330. "; https: " + data.https.ToString() +
  331. "; validate: " + data.validate.ToString() +
  332. "; performance: " + data.performance.ToString() +
  333. "; authenticate: " + data.authenticate.ToString() +
  334. "; transaction: " + data.transaction.ToString() +
  335. "; messageType: " + data.messageType;
  336. Debug.Write( SeverityType.Info, CategoryType.Soap, info );
  337. #endif
  338. try
  339. {
  340. switch( message.Stage )
  341. {
  342. //
  343. // First Event
  344. //
  345. case SoapMessageStage.BeforeDeserialize:
  346. //
  347. // Initialize our context.
  348. //
  349. Context.Current.Initialize();
  350. Config.CheckForUpdate();
  351. //
  352. // TODO: Since we are using DispositionReport.ThrowFinal() I don't think this is
  353. // needed anymore.
  354. //
  355. //
  356. // Check to make sure the authenticated user has user credentials
  357. //
  358. Debug.Verify( "1" != HttpContext.Current.Request.ServerVariables[ "Exception" ],
  359. "UDDI_ERROR_FATALERROR_VERSIONCHECKERROR",
  360. UDDI.ErrorType.E_fatalError );
  361. Debug.Write( SeverityType.Info, CategoryType.Soap, "URL: " + message.Url );
  362. Debug.Write( SeverityType.Info, CategoryType.Soap, "SOAPAction: " + HttpContext.Current.Request.Headers[ "SOAPAction" ] );
  363. string contentType = HttpContext.Current.Request.ContentType.ToLower();
  364. bool validEncoding = ( contentType.IndexOf( "charset=\"utf-8\"" ) >= 0 ) ||
  365. ( contentType.IndexOf( "charset=utf-8" ) >= 0 );
  366. Debug.Verify( validEncoding, "UDDI_ERROR_UNSUPPORTED_CONTENTTYPEHEADERMISSING", ErrorType.E_unsupported );
  367. if( data.performance )
  368. PublishMethodBegin( message );
  369. if( data.https )
  370. CheckForHttps( message );
  371. //
  372. // Validation has been moved into the other SOAP extension
  373. //
  374. // if( data.validate )
  375. // Validate( message );
  376. break;
  377. //
  378. // Second Event
  379. //
  380. case SoapMessageStage.AfterDeserialize:
  381. ConnectionManager.Open( data.transaction, data.transaction );
  382. if( data.certificate )
  383. CheckCertificate( message );
  384. if( data.authenticate )
  385. Authenticate( message );
  386. else if( 0 != ( Config.GetInt( "Security.AuthenticationMode", (int) AuthenticationMode.Both )
  387. & (int) AuthenticationMode.AuthenticatedRead ) )
  388. {
  389. //
  390. // Authenticated reads are turned on and this is a read request
  391. // Make sure the caller is authenticated using Windows and is at least a user
  392. //
  393. WindowsIdentity identity = (WindowsIdentity) HttpContext.Current.User.Identity;
  394. WindowsAuthenticator wa = new WindowsAuthenticator();
  395. wa.Authenticate( "", 0 /* not used */ );
  396. Debug.Write( SeverityType.Info, CategoryType.Soap, "Authenticated user: using Windows based authentication Identity is " + identity.Name );
  397. //
  398. // Check to make sure the authenticated user has user credentials
  399. //
  400. Debug.Verify( Context.User.IsUser,
  401. "UDDI_ERROR_FATALERROR_NOUSERCREDS",
  402. UDDI.ErrorType.E_fatalError,
  403. Context.User.ID );
  404. }
  405. break;
  406. //
  407. // Third Event
  408. //
  409. case SoapMessageStage.BeforeSerialize:
  410. break;
  411. //
  412. // Last Event
  413. //
  414. case SoapMessageStage.AfterSerialize:
  415. //
  416. // Cleanup the connection and commit the database activity
  417. //
  418. if( data.transaction &&
  419. ( null != (object) ConnectionManager.GetConnection() ) &&
  420. ( null != (object) ConnectionManager.GetTransaction() ) )
  421. {
  422. if( null == (object) message.Exception )
  423. {
  424. ConnectionManager.Commit();
  425. }
  426. else
  427. {
  428. ConnectionManager.Abort();
  429. }
  430. }
  431. ConnectionManager.Close();
  432. try
  433. {
  434. if( data.performance )
  435. PublishMethodEnd( message );
  436. }
  437. catch
  438. {
  439. Debug.OperatorMessage(
  440. SeverityType.Warning,
  441. CategoryType.None,
  442. OperatorMessageType.UnableToPublishCounter,
  443. "An error occurred while trying to publish a performance counter, the system will continue" );
  444. }
  445. break;
  446. default:
  447. throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_UNKNOWNEXTSTAGE" );
  448. }
  449. }
  450. catch( Exception e )
  451. {
  452. DispositionReport.Throw( e );
  453. }
  454. Debug.Leave();
  455. }
  456. public override Stream ChainStream( Stream stream )
  457. {
  458. return base.ChainStream( stream );
  459. }
  460. }
  461. [AttributeUsage(AttributeTargets.Method, AllowMultiple=false)]
  462. public class UDDIExtensionAttribute : SoapExtensionAttribute
  463. {
  464. private int priority;
  465. //
  466. // The default constructor should be configured for the inquire API set
  467. //
  468. public UDDIExtensionAttribute() : this( true, true, true, false, false, false, false, "" ){}
  469. public UDDIExtensionAttribute(
  470. bool log,
  471. bool validate,
  472. bool performance,
  473. bool authenticate,
  474. bool transaction,
  475. bool https,
  476. bool certificate,
  477. string messageType )
  478. {
  479. this.log = log;
  480. this.https = https;
  481. this.validate = validate;
  482. this.performance = performance;
  483. this.authenticate = authenticate;
  484. this.transaction = transaction;
  485. this.certificate = certificate;
  486. this.messageType = messageType;
  487. }
  488. public override Type ExtensionType
  489. {
  490. get { return typeof(UDDIExtension); }
  491. }
  492. public override int Priority
  493. {
  494. get { return priority; }
  495. set { priority = value; }
  496. }
  497. public bool log;
  498. public bool https;
  499. public bool validate;
  500. public bool performance;
  501. public bool authenticate;
  502. public bool transaction;
  503. public bool certificate;
  504. public string messageType;
  505. }
  506. /// ********************************************************************
  507. /// public class VersionSupportExtension
  508. /// --------------------------------------------------------------------
  509. /// <summary>
  510. /// </summary>
  511. /// ********************************************************************
  512. ///
  513. public class VersionSupportExtension : SoapExtension
  514. {
  515. Stream oldStream;
  516. Stream newStream;
  517. public override object GetInitializer( LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute )
  518. {
  519. return null;
  520. }
  521. public override object GetInitializer( Type type )
  522. {
  523. return null;
  524. }
  525. public override void Initialize( object initializer )
  526. {
  527. }
  528. public override void ProcessMessage(SoapMessage message)
  529. {
  530. try
  531. {
  532. switch( message.Stage )
  533. {
  534. case SoapMessageStage.BeforeDeserialize:
  535. //
  536. // Check to see if the server has been manually stopped.
  537. //
  538. if( 0 == Config.GetInt( "Run", 1 ) )
  539. {
  540. DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_busy, "UDDI_ERROR_BUSY_SERVICENOTAVAILABLE" ) );
  541. //
  542. // DispositionReport.ThrowFinal will close the HTTP stream so there is no point going on in this method
  543. //
  544. return;
  545. }
  546. try
  547. {
  548. //
  549. // Validate against the UDDI schemas
  550. //
  551. SchemaCollection.Validate( oldStream );
  552. }
  553. catch( Exception e )
  554. {
  555. DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SCHEMAVALIDATIONFAILED", e.Message ) );
  556. //
  557. // DispositionReport.ThrowFinal will close the HTTP stream so there is no point going on in this method
  558. //
  559. return;
  560. }
  561. //
  562. // Make sure we only have 1 UDDI request in the SOAP body. This method will also set the versionMajor
  563. // member.
  564. //
  565. CheckForSingleRequest( oldStream );
  566. //
  567. // If this is a v1 message, we'll first map it to the v2
  568. // namespace so that it can be processed by the new
  569. // library.
  570. //
  571. if( 1 == Context.ApiVersionMajor || 2 == Context.ApiVersionMajor)
  572. {
  573. TextReader reader = new StreamReader( oldStream );
  574. TextWriter writer = new StreamWriter( newStream, new System.Text.UTF8Encoding( false ) );
  575. string xml = reader.ReadToEnd();
  576. if( 1 == Context.ApiVersionMajor )
  577. {
  578. xml = xml.Replace( "=\"urn:uddi-org:api\"", "=\"urn:uddi-org:api_v2\"" );
  579. xml = xml.Replace( "='urn:uddi-org:api'", "=\"urn:uddi-org:api_v2\"" );
  580. }
  581. writer.Write( xml );
  582. writer.Flush();
  583. newStream.Position = 0;
  584. }
  585. break;
  586. case SoapMessageStage.AfterDeserialize:
  587. //
  588. // After the message is deserialized is the earliest place where we
  589. // have access to our SOAP headers.
  590. //
  591. CheckSOAPHeaders( message );
  592. //
  593. // Now that the message has been deserialized, make
  594. // sure that the generic and xmlns attributes agree.
  595. //
  596. IMessage obj = message.GetInParameterValue( 0 ) as IMessage;
  597. if( null != obj )
  598. {
  599. //
  600. // We only need to do this if the deserialized object supports IMessage
  601. //
  602. string expected = Context.ApiVersionMajor + ".0";
  603. string actual = obj.Generic.Trim();
  604. if( expected != actual )
  605. throw new UDDIException( ErrorType.E_unrecognizedVersion, "UDDI_ERROR_UNKNOWNVERSION_GENERICNAMESPACEMISMATCH" );
  606. }
  607. break;
  608. case SoapMessageStage.BeforeSerialize:
  609. break;
  610. case SoapMessageStage.AfterSerialize:
  611. //
  612. // There may have been exceptions thrown during serialization.
  613. //
  614. if( null != message.Exception &&
  615. ( null == message.Exception.Detail ||
  616. 0 == message.Exception.Detail.ChildNodes.Count ) )
  617. {
  618. DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_FAILEDDESERIALIZATION" ) );
  619. //
  620. // DispositionReport.ThrowFinal will close the HTTP stream so there is no point going on in this method
  621. //
  622. return;
  623. }
  624. //
  625. // If the original request was v1, then we'll need to
  626. // remap the output to use the v1 namespace.
  627. //
  628. if( 1 == Context.ApiVersionMajor || 2 == Context.ApiVersionMajor )
  629. {
  630. newStream.Position = 0;
  631. TextReader reader = new StreamReader( newStream );
  632. TextWriter writer = new StreamWriter( oldStream, new System.Text.UTF8Encoding( false ) );
  633. string xml = reader.ReadToEnd();
  634. //
  635. // We don't have to use the same 'loose' replacement as we did on the incoming request
  636. // because our response will be serialized such that the default namespace is our UDDI
  637. // namespace.
  638. //
  639. if( 1 == Context.ApiVersionMajor )
  640. {
  641. xml = xml.Replace( "xmlns=\"urn:uddi-org:api_v2\"", "xmlns=\"urn:uddi-org:api\"" );
  642. xml = xml.Replace( "generic=\"2.0\"", "generic=\"1.0\"" );
  643. }
  644. writer.Write( xml );
  645. writer.Flush();
  646. }
  647. break;
  648. default:
  649. throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_UNKNOWNEXTSTAGE" );
  650. }
  651. }
  652. catch( Exception e )
  653. {
  654. DispositionReport.Throw( e );
  655. }
  656. }
  657. public override Stream ChainStream( Stream stream )
  658. {
  659. oldStream = stream;
  660. newStream = new MemoryStream();
  661. return newStream;
  662. }
  663. private void CheckSOAPHeaders( SoapMessage message )
  664. {
  665. // We want to check the following:
  666. //
  667. // - no SOAP Actor attribute exists
  668. // - no SOAP headers can have a must_understand attribute set to true
  669. //
  670. // Go through each header in our message
  671. //
  672. foreach( SoapHeader header in message.Headers )
  673. {
  674. if( header.MustUnderstand )
  675. {
  676. //
  677. // No headers can have this attribute set.
  678. //
  679. DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_MUSTUNDERSTANDATT" ) );
  680. return;
  681. }
  682. if( header.Actor.Length > 0 )
  683. {
  684. //
  685. // Can't have a SOAP Actor attribute set, generate a SOAP fault with
  686. // no detail element and a 'Client' fault code
  687. //
  688. DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_ACTORATT" ) );
  689. return;
  690. }
  691. }
  692. }
  693. //
  694. // TODO: see if there is a way to better modularize this method and rename it.
  695. //
  696. private void CheckForSingleRequest( Stream stream )
  697. {
  698. try
  699. {
  700. //
  701. // Move to the start of our stream
  702. //
  703. stream.Position = 0;
  704. XmlTextReader requestReader = new XmlTextReader( oldStream );
  705. requestReader.MoveToContent();
  706. //
  707. // TODO: should not hard-code SOAP names and namespaces
  708. //
  709. //
  710. // Move to the beginning of the SOAP envelope
  711. //
  712. requestReader.ReadStartElement( "Envelope", "http://schemas.xmlsoap.org/soap/envelope/" );
  713. //
  714. // Move to the SOAP body
  715. //
  716. while( !requestReader.IsStartElement( "Body", "http://schemas.xmlsoap.org/soap/envelope/" ) && !requestReader.EOF )
  717. {
  718. requestReader.Skip();
  719. }
  720. //
  721. // Advance the current node to the first child of Body. This is presumably the UDDI message
  722. //
  723. requestReader.ReadStartElement( "Body", "http://schemas.xmlsoap.org/soap/envelope/" );
  724. requestReader.MoveToContent();
  725. //
  726. // This element MUST have a UDDI namespace
  727. //
  728. string uddiNamespace = requestReader.LookupNamespace( requestReader.Prefix );
  729. switch( uddiNamespace )
  730. {
  731. case "urn:uddi-org:api":
  732. {
  733. Context.ApiVersionMajor = 1;
  734. break;
  735. }
  736. case "urn:uddi-org:api_v2":
  737. {
  738. Context.ApiVersionMajor = 2;
  739. break;
  740. }
  741. case "urn:uddi-microsoft-com:api_v2_extensions":
  742. {
  743. Context.ApiVersionMajor = 2;
  744. break;
  745. }
  746. case "urn:uddi-org:repl":
  747. {
  748. Context.ApiVersionMajor = 2;
  749. break;
  750. }
  751. default:
  752. {
  753. //
  754. // This is a problem, we don't have a UDDI namespace. Throw an exception and get out of here. The
  755. // exception will be caught in our outer catch and sent to our client using DispositionReport.ThrowFinal.
  756. //
  757. throw new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_MISSINGUDDINS" );
  758. }
  759. }
  760. //
  761. // Skip the children of this node
  762. //
  763. requestReader.Skip();
  764. requestReader.MoveToContent();
  765. //
  766. // Reset our stream so someone else can use it.
  767. //
  768. stream.Position = 0;
  769. //
  770. // If we are not at the end of the Body tag, then we have multiple requests, we should reject the message.
  771. //
  772. if( false == requestReader.LocalName.Equals( "Body" ) )
  773. {
  774. DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_MULTIPLEREQUEST" ) );
  775. }
  776. }
  777. catch( UDDIException uddiException )
  778. {
  779. DispositionReport.ThrowFinal( uddiException );
  780. }
  781. catch
  782. {
  783. //
  784. // We'll get this exception if the message contains any invalid elements
  785. //
  786. DispositionReport.ThrowFinal( new UDDIException( ErrorType.E_fatalError, "UDDI_ERROR_FATALERROR_SOAP_INVALIDELEMENT" ) );
  787. }
  788. }
  789. }
  790. }