//------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ namespace System.ServiceModel.Security { using System; using System.Collections.Generic; using System.Diagnostics; using System.IdentityModel.Protocols.WSTrust; using System.IdentityModel.Selectors; using System.IdentityModel.Tokens; using System.Runtime; using System.Runtime.InteropServices; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using IM = System.IdentityModel; /// /// A that produces objects used to /// communicate to a WS-Trust endpoint. /// [ComVisible(false)] public class WSTrustChannelFactory : ChannelFactory { // // NOTE: The properties on this class are designed to facilitate ease of use of the component and // to reduce the complexity of the constructors. The base class already gifts us with 8 constructor // overloads. // // Therefore, it is advisable that the fields *not* be used unless absolutely required. // /// /// These fields represent the property values that are "locked down" once the first channel is created. /// class WSTrustChannelLockedProperties { public TrustVersion TrustVersion; public WSTrustSerializationContext Context; public WSTrustRequestSerializer RequestSerializer; public WSTrustResponseSerializer ResponseSerializer; } // // Once we create a channel, our properties can be locked down. // object _factoryLock = new object(); bool _locked = false; WSTrustChannelLockedProperties _lockedProperties; // // The TrustVersion property can be set to an instance of TrustVersion.WSTrust13 or TrustVersion.WSTrustFeb2005 // to generate the built-in serializers for these trust namespaces. // TrustVersion _trustVersion; // // These fields contain the values used to construct the WSTrustSerializationContext used by the channels // we generate. // // _securityTokenResolver and _useKeyTokenResolver imply special behavior if they are null; however, // _securityTokenHandlerCollectionManager is not permitted to be null. // SecurityTokenResolver _securityTokenResolver; SecurityTokenResolver _useKeyTokenResolver; SecurityTokenHandlerCollectionManager _securityTokenHandlerCollectionManager = SecurityTokenHandlerCollectionManager.CreateDefaultSecurityTokenHandlerCollectionManager(); // // These serializers determine how the channels serialize RST and RSTR messages. // WSTrustRequestSerializer _wsTrustRequestSerializer; WSTrustResponseSerializer _wsTrustResponseSerializer; /// /// Initializes a new instance of the class. /// public WSTrustChannelFactory() : base() { } /// /// Initializes a new instance of the class with a specified endpoint /// configuration name. /// /// The configuration name used for the endpoint. public WSTrustChannelFactory(string endpointConfigurationName) : base(endpointConfigurationName) { } /// /// Initializes a new instance of the class. /// /// The specified for the channels produced by the factory public WSTrustChannelFactory(Binding binding) : base(binding) { } /// /// Initializes a new instance of the class with a specified endpoint. /// /// The for the channels produced by the factory. public WSTrustChannelFactory(ServiceEndpoint endpoint) : base(endpoint) { } /// /// Initializes a new instance of the class associated with a specified /// name for the endpoint configuration and remote address. /// /// The configuration name used for the endpoint. /// The that provides the location of the service. public WSTrustChannelFactory(string endpointConfigurationName, EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } /// /// Initializes a new instance of the class with a specified binding /// and endpoint address. /// /// The specified for the channels produced by the factory /// The that provides the location of the service. public WSTrustChannelFactory(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) { } /// /// Initializes a new instance of the class with a specified binding /// and remote address. /// /// The specified for the channels produced by the factory /// The that provides the location of the service. public WSTrustChannelFactory(Binding binding, string remoteAddress) : base(binding, remoteAddress) { } /// /// Gets or sets the version of WS-Trust the created channels will use for serializing messages. /// /// /// If this property is not set, created channels will use the set on any /// found on the channel factory's Endpoint object if one exists. /// /// This class will not support changing the value of this property after a channel is created. /// public TrustVersion TrustVersion { get { return _trustVersion; } set { lock (_factoryLock) { if (_locked) { throw IM.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID3287)); } _trustVersion = value; } } } /// /// Gets or sets the containing the set of /// objects used by created channels for serializing and validating /// tokens found in WS-Trust messages. /// /// /// This class will not support changing the value of this property after a channel is created. /// public SecurityTokenHandlerCollectionManager SecurityTokenHandlerCollectionManager { get { return _securityTokenHandlerCollectionManager; } set { if (value == null) { throw IM.DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); } lock (_factoryLock) { if (_locked) { throw IM.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID3287)); } _securityTokenHandlerCollectionManager = value; } } } /// /// Gets or sets the used to resolve security token references found in most /// elements of WS-Trust messages. /// /// /// /// If this property is not set created channels will use the ClientCertificate set on the factory's /// Endpoint's ClientCredentials behavior to create a resolver. If no such certificate is found, an empty /// resolver is used. /// /// /// This class will not support changing the value of this property after a channel is created. /// /// public SecurityTokenResolver SecurityTokenResolver { get { return _securityTokenResolver; } set { lock (_factoryLock) { if (_locked) { throw IM.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID3287)); } _securityTokenResolver = value; } } } /// /// The used to resolve security token references found in the /// UseKey element of RST messages as well as the RenewTarget element found in RST messages. /// /// /// /// If this property is not set an empty resolver is used. /// /// /// This class will not support changing the value of this property after a channel is created. /// /// public SecurityTokenResolver UseKeyTokenResolver { get { return _useKeyTokenResolver; } set { lock (_factoryLock) { if (_locked) { throw IM.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID3287)); } _useKeyTokenResolver = value; } } } /// /// Gets or sets the WSTrustRequestSerializer to use for serializing RequestSecurityTokens messages. /// /// /// /// If this property is not set, either or /// will be used. The serializer will correspond to the /// version of WS-Trust indicated by the property. /// /// /// This class will not support changing the value of this property after a channel is created. /// /// public WSTrustRequestSerializer WSTrustRequestSerializer { get { return _wsTrustRequestSerializer; } set { lock (_factoryLock) { if (_locked) { throw IM.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID3287)); } _wsTrustRequestSerializer = value; } } } /// /// Gets or sets the WSTrustResponseSerializer to use for serializing RequestSecurityTokensResponse messages. /// /// /// /// If this property is not set, either or /// will be used. The serializer will correspond to the /// version of WS-Trust indicated by the property. /// /// /// This class will not support changing the value of this property after a channel is created. /// /// public WSTrustResponseSerializer WSTrustResponseSerializer { get { return _wsTrustResponseSerializer; } set { lock (_factoryLock) { if (_locked) { throw IM.DiagnosticUtility.ThrowHelperInvalidOperation(SR.GetString(SR.ID3287)); } _wsTrustResponseSerializer = value; } } } /// /// Creates a that is used to send messages to a service at a specific /// endpoint address through a specified transport address. /// /// The that provides the location of the service. /// The that contains the transport address to which the channel sends messages. /// public override IWSTrustChannelContract CreateChannel(EndpointAddress address, Uri via) { IWSTrustChannelContract innerChannel = base.CreateChannel(address, via); WSTrustChannelLockedProperties lockedProperties = GetLockedProperties(); return CreateTrustChannel(innerChannel, lockedProperties.TrustVersion, lockedProperties.Context, lockedProperties.RequestSerializer, lockedProperties.ResponseSerializer); } /// /// Creates a using parameters that reflect the configuration of /// this factory. /// /// The channel created by the base class capable of sending and /// receiving messages. /// The version of WS-Trust that should be used. /// /// The that should be used to serialize WS-Trust messages. /// /// /// The that should be used to serialize WS-Trust request messages. /// /// /// The that should be used to serialize WS-Trust response messages. /// /// protected virtual WSTrustChannel CreateTrustChannel(IWSTrustChannelContract innerChannel, TrustVersion trustVersion, WSTrustSerializationContext context, WSTrustRequestSerializer requestSerializer, WSTrustResponseSerializer responseSerializer) { return new WSTrustChannel(this, innerChannel, trustVersion, context, requestSerializer, responseSerializer); } private WSTrustChannelLockedProperties GetLockedProperties() { lock (_factoryLock) { if (_lockedProperties == null) { WSTrustChannelLockedProperties tmpLockedProperties = new WSTrustChannelLockedProperties(); tmpLockedProperties.TrustVersion = GetTrustVersion(); tmpLockedProperties.Context = CreateSerializationContext(); tmpLockedProperties.RequestSerializer = GetRequestSerializer(tmpLockedProperties.TrustVersion); tmpLockedProperties.ResponseSerializer = GetResponseSerializer(tmpLockedProperties.TrustVersion); _lockedProperties = tmpLockedProperties; _locked = true; } return _lockedProperties; } } private WSTrustRequestSerializer GetRequestSerializer(TrustVersion trustVersion) { Fx.Assert(trustVersion != null, "trustVersion != null"); if (_wsTrustRequestSerializer != null) { return _wsTrustRequestSerializer; } if (trustVersion == TrustVersion.WSTrust13) { return new WSTrust13RequestSerializer(); } else if (trustVersion == TrustVersion.WSTrustFeb2005) { return new WSTrustFeb2005RequestSerializer(); } else { throw IM.DiagnosticUtility.ExceptionUtility.ThrowHelperError( new NotSupportedException(SR.GetString(SR.ID3137, trustVersion.ToString()))); } } private WSTrustResponseSerializer GetResponseSerializer(TrustVersion trustVersion) { Fx.Assert(trustVersion != null, "trustVersion != null"); if (_wsTrustResponseSerializer != null) { return _wsTrustResponseSerializer; } if (trustVersion == TrustVersion.WSTrust13) { return new WSTrust13ResponseSerializer(); } else if (trustVersion == TrustVersion.WSTrustFeb2005) { return new WSTrustFeb2005ResponseSerializer(); } else { throw IM.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ID3137, trustVersion.ToString()))); } } private TrustVersion GetTrustVersion() { TrustVersion trustVersion = _trustVersion; if (trustVersion == null) { BindingElementCollection elements = Endpoint.Binding.CreateBindingElements(); SecurityBindingElement sbe = elements.Find(); if (null == sbe) { throw IM.DiagnosticUtility.ExceptionUtility.ThrowHelperError( new InvalidOperationException( SR.GetString(SR.ID3269))); } trustVersion = sbe.MessageSecurityVersion.TrustVersion; } return trustVersion; } /// /// Creates a used by objects created /// by this factory. /// /// /// /// If is set to null, the /// ClientCertificate set on the factory's Endpoint's ClientCredentials behavior will be used to /// create a resolver. If no such certificate is found, an empty resolver is used. /// /// /// If is set to null, an empty resolver /// will be used. /// /// /// A WSTrustSerializationContext initialized with the trust client's properties. protected virtual WSTrustSerializationContext CreateSerializationContext() { // // Create a resolver with the ClientCredential's ClientCertificate if a resolver is not set. // SecurityTokenResolver resolver = _securityTokenResolver; if (resolver == null) { ClientCredentials factoryCredentials = Credentials; if (null != factoryCredentials.ClientCertificate && null != factoryCredentials.ClientCertificate.Certificate) { List clientCredentialTokens = new List(); clientCredentialTokens.Add(new X509SecurityToken(factoryCredentials.ClientCertificate.Certificate)); resolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(clientCredentialTokens.AsReadOnly(), false); } } // // If it is _still_ null, then make it empty. // if (resolver == null) { resolver = EmptySecurityTokenResolver.Instance; } // // UseKeyTokenResolver is empty if null. // SecurityTokenResolver useKeyResolver = _useKeyTokenResolver ?? EmptySecurityTokenResolver.Instance; return new WSTrustSerializationContext(_securityTokenHandlerCollectionManager, resolver, useKeyResolver); } } }