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