WsatTransactionFormatter.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. //------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //------------------------------------------------------------
  4. namespace System.ServiceModel.Transactions
  5. {
  6. using System;
  7. using System.Runtime;
  8. using System.Security.Permissions;
  9. using System.ServiceModel.Channels;
  10. using System.ServiceModel.Security;
  11. using System.Transactions;
  12. using System.Xml;
  13. using Microsoft.Transactions.Wsat.Messaging;
  14. using Microsoft.Transactions.Wsat.Protocol;
  15. using DiagnosticUtility = System.ServiceModel.DiagnosticUtility;
  16. abstract class WsatTransactionFormatter : TransactionFormatter
  17. {
  18. bool initialized;
  19. WsatConfiguration wsatConfig;
  20. WsatProxy wsatProxy;
  21. ProtocolVersion protocolVersion;
  22. protected WsatTransactionFormatter(ProtocolVersion protocolVersion)
  23. {
  24. this.protocolVersion = protocolVersion;
  25. }
  26. //=======================================================================================
  27. void EnsureInitialized()
  28. {
  29. if (!this.initialized)
  30. {
  31. lock (this)
  32. {
  33. if (!this.initialized)
  34. {
  35. this.wsatConfig = new WsatConfiguration();
  36. this.wsatProxy = new WsatProxy(this.wsatConfig, this.protocolVersion);
  37. this.initialized = true;
  38. }
  39. }
  40. }
  41. }
  42. //=======================================================================================
  43. // The demand is not added now (in 4.5), to avoid a breaking change. To be considered in the next version.
  44. /*
  45. [PermissionSet(SecurityAction.Demand, Unrestricted = true)] // because we call code from a non-APTCA assembly; WSATs are not supported in partial trust, so customers should not be broken by this demand
  46. */
  47. public override void WriteTransaction(Transaction transaction, Message message)
  48. {
  49. EnsureInitialized();
  50. ForcePromotion(transaction);
  51. // Make a context and add it to the message
  52. CoordinationContext context;
  53. RequestSecurityTokenResponse issuedToken;
  54. MarshalAsCoordinationContext(transaction, out context, out issuedToken);
  55. if (issuedToken != null)
  56. {
  57. CoordinationServiceSecurity.AddIssuedToken(message, issuedToken);
  58. }
  59. WsatTransactionHeader header = new WsatTransactionHeader(context, this.protocolVersion);
  60. message.Headers.Add(header);
  61. }
  62. //=======================================================================================
  63. void ForcePromotion(Transaction transaction)
  64. {
  65. // Force promotion. This may throw TransactionException.
  66. // We used to check the DistributedIdentifier property first, but VSWhidbey bug 547901
  67. // prevents us from doing so reliably in multi-threaded scenarios (there is a ----
  68. // in the System.Transactions code that can cause a NullReferenceException if we ask
  69. // for the identifier while the transaction is being promoted)
  70. TransactionInterop.GetTransmitterPropagationToken(transaction);
  71. }
  72. //=======================================================================================
  73. // The demand is not added now (in 4.5), to avoid a breaking change. To be considered in the next version.
  74. /*
  75. // We demand full trust because we use CoordinationServiceSecurity from a non-APTCA assembly and CoordinationServiceSecurity.GetIssuedToken(..) can call Environment.FailFast.
  76. // It's recommended to not let partially trusted callers to bring down the process.
  77. // WSATs are not supported in partial trust, so customers should not be broken by this demand.
  78. [PermissionSet(SecurityAction.Demand, Unrestricted = true)]
  79. */
  80. public override TransactionInfo ReadTransaction(Message message)
  81. {
  82. EnsureInitialized();
  83. CoordinationContext context = WsatTransactionHeader.GetCoordinationContext(message, this.protocolVersion);
  84. if (context == null)
  85. return null;
  86. // Incoming transaction tokens are optional
  87. RequestSecurityTokenResponse issuedToken;
  88. try
  89. {
  90. issuedToken = CoordinationServiceSecurity.GetIssuedToken(message, context.Identifier, this.protocolVersion);
  91. }
  92. catch (XmlException e)
  93. {
  94. throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
  95. new TransactionException(SR.FailedToDeserializeIssuedToken, e));
  96. }
  97. return new WsatTransactionInfo(this.wsatProxy, context, issuedToken);
  98. }
  99. //=======================================================================================
  100. public WsatTransactionInfo CreateTransactionInfo(CoordinationContext context,
  101. RequestSecurityTokenResponse issuedToken)
  102. {
  103. return new WsatTransactionInfo(this.wsatProxy, context, issuedToken);
  104. }
  105. //=======================================================================================
  106. // The demand is not added now (in 4.5), to avoid a breaking change. To be considered in the next version.
  107. /*
  108. // We demand full trust because we use CoordinationContext and CoordinationServiceSecurity from a non-APTCA assembly.
  109. // The CoordinationContext constructor can call Environment.FailFast and it's recommended to not let partially trusted callers to bring down the process.
  110. // WSATs are not supported in partial trust, so customers should not be broken by this demand.
  111. [PermissionSet(SecurityAction.Demand, Unrestricted = true)]
  112. */
  113. public void MarshalAsCoordinationContext(Transaction transaction,
  114. out CoordinationContext context,
  115. out RequestSecurityTokenResponse issuedToken)
  116. {
  117. Guid transactionId = transaction.TransactionInformation.DistributedIdentifier;
  118. string nonNativeContextId = null;
  119. context = new CoordinationContext(this.protocolVersion);
  120. // Get timeout, description and isolation flags
  121. uint timeout;
  122. IsolationFlags isoFlags;
  123. string description;
  124. OleTxTransactionFormatter.GetTransactionAttributes(transaction,
  125. out timeout,
  126. out isoFlags,
  127. out description);
  128. context.IsolationFlags = isoFlags;
  129. context.Description = description;
  130. // If we can, use cached extended information
  131. // Note - it may be worth using outgoing contexts more than once.
  132. // We'll let performance profiling decide that question
  133. WsatExtendedInformation info;
  134. if (WsatExtendedInformationCache.Find(transaction, out info))
  135. {
  136. context.Expires = info.Timeout;
  137. // The extended info cache only contains an identifier when it's non-native
  138. if (!string.IsNullOrEmpty(info.Identifier))
  139. {
  140. context.Identifier = info.Identifier;
  141. nonNativeContextId = info.Identifier;
  142. }
  143. }
  144. else
  145. {
  146. context.Expires = timeout;
  147. if (context.Expires == 0)
  148. {
  149. // If the timeout is zero, there are two possibilities:
  150. // 1) This is a root transaction with an infinite timeout.
  151. // 2) This is a subordinate transaction whose timeout was not flowed.
  152. // We have no mechanism for distinguishing between the two cases.
  153. //
  154. // We could always return zero here, instead of using the local max timeout.
  155. // The problem is that the 2004/08 WS-C spec does not specify the meaning
  156. // of a zero expires field. While we accept zero to mean "as large as possible"
  157. // it would be risky to expect others to do the same. So we only propagate
  158. // zero in the expires field if the local max timeout has been disabled.
  159. //
  160. // This is MB 34596: how can we flow the real timeout?
  161. context.Expires = (uint)TimeoutHelper.ToMilliseconds(this.wsatConfig.MaxTimeout);
  162. }
  163. }
  164. if (context.Identifier == null)
  165. {
  166. context.Identifier = CoordinationContext.CreateNativeIdentifier(transactionId);
  167. nonNativeContextId = null;
  168. }
  169. string tokenId;
  170. if (!this.wsatConfig.IssuedTokensEnabled)
  171. {
  172. tokenId = null;
  173. issuedToken = null;
  174. }
  175. else
  176. {
  177. CoordinationServiceSecurity.CreateIssuedToken(transactionId,
  178. context.Identifier,
  179. this.protocolVersion,
  180. out issuedToken,
  181. out tokenId);
  182. }
  183. AddressHeader refParam = new WsatRegistrationHeader(transactionId, nonNativeContextId, tokenId);
  184. context.RegistrationService = wsatConfig.CreateRegistrationService(refParam, this.protocolVersion);
  185. context.IsolationLevel = transaction.IsolationLevel;
  186. context.LocalTransactionId = transactionId;
  187. if (this.wsatConfig.OleTxUpgradeEnabled)
  188. {
  189. context.PropagationToken = TransactionInterop.GetTransmitterPropagationToken(transaction);
  190. }
  191. }
  192. }
  193. //------------------------------------------------------------------------------------------
  194. // Versioned Wsat transaction formatters
  195. //------------------------------------------------------------------------------------------
  196. class WsatTransactionFormatter10 : WsatTransactionFormatter
  197. {
  198. static WsatTransactionHeader emptyTransactionHeader = new WsatTransactionHeader(null, ProtocolVersion.Version10);
  199. public WsatTransactionFormatter10() : base(ProtocolVersion.Version10) { }
  200. //=======================================================================================
  201. public override MessageHeader EmptyTransactionHeader
  202. {
  203. get { return emptyTransactionHeader; }
  204. }
  205. }
  206. class WsatTransactionFormatter11 : WsatTransactionFormatter
  207. {
  208. static WsatTransactionHeader emptyTransactionHeader = new WsatTransactionHeader(null, ProtocolVersion.Version11);
  209. public WsatTransactionFormatter11() : base(ProtocolVersion.Version11) { }
  210. //=======================================================================================
  211. public override MessageHeader EmptyTransactionHeader
  212. {
  213. get { return emptyTransactionHeader; }
  214. }
  215. }
  216. }