Section 10.13. Declarative Security Framework


10.13. Declarative Security Framework

WCF security is truly a vast topic. There is a daunting number of details to master, and intricate relationships exist between the various parts. The programming model is very complex, and at first there is an inescapable feeling of navigating a maze. To make things even worse, there are severe implications both at the application and at the business level for getting it wrong. The solution I came up with is a declarative security framework for WCF. For the service, I provided a security attribute (as well as matching support for the host); and for the client, I provided a few helper classes and secure proxy classes. My declarative framework grossly simplifies applying WCF security, and makes security configuration on par with the other aspects of WCF configuration, such as transactions or synchronization. I wanted a declarative model that would be simple to use and would minimize the need to understand the many details of security. As a developer, all you need to do is select the correct scenario (out of the five common scenarios discussed in this chapter) and my framework will automate the configuration. Not only that, my framework mandates the correct options and enforces my recommendations. At the same time, I wanted a model that maintains granularity and control of the underlying configuration if the need for that ever arises.

10.13.1. The SecurityBehaviorAttribute

Example 10-19 lists the definition of the SecurityBehaviorAttribute and the ServiceSecurity enum. ServiceSecurity defines the five scenarios supported by my framework.

Example 10-19. The SecurityBehaviorAttribute

 public enum ServiceSecurity {    None,    Anonymous,    BusinessToBusiness,    Internet,    Intranet } [AttributeUsage(AttributeTargets.Class)] public class SecurityBehaviorAttribute : Attribute,IServiceBehavior {    public SecurityBehaviorAttribute(ServiceSecurity mode);    public SecurityBehaviorAttribute(ServiceSecurity mode,                                     string serviceCertificateName);    public SecurityBehaviorAttribute(ServiceSecurity mode,                                     StoreLocation storeLocation,                                     StoreName storeName,                                     X509FindType findType,                                     string serviceCertificateName);    public bool ImpersonateAll    {get;set;}    public string ApplicationName    {get;set;}    public bool UseAspNetProviders    {get;set;} } 

When applying the SecurityBehavior attribute you need to provide it with the target scenario in the form of a ServiceSecurity value. You can use just the constructors of the SecurityBehavior attribute, or you can set the properties. Unset, the properties all default to reasonable values in the context of the target scenario. When selecting a scenario, the configured behavior follows to the letter my previous description of the individual scenarios. The SecurityBehavior attribute yields a composable security model, allowing quite a few permutations and subscenarios. When using the attribute, you can even have a security-free host config file, or you can combine settings from the config file with values driven by the attribute. Much the same way, your hosting code can be free of security or you can combine programmatic host security with the attribute.

10.13.1.1. Configuring intranet service

To configure a service for the intranet security scenario, apply SecurityBehavior with ServiceSecurity.Intranet:

 [ServiceContract] interface IMyContract {    [OperationContract]    void MyMethod( ); } [SecurityBehavior(ServiceSecurity.Intranet)] class MyService : IMyContract {    public void MyMethod( )    {...} } 

Even though the service contract used may not constrain the protection level, the attribute programmatically adds that demand to enforce message protection. You can use Windows NT groups for role-based security:

 [SecurityBehavior(ServiceSecurity.Intranet)] class MyService : IMyContract {    [PrincipalPermission(SecurityAction.Demand,Role = @"<Domain>\Customer")]    public void MyMethod( )    {...} } 

The service can programmatically impersonate the callers, or use the operation behavior attribute for individual methods impersonation. You can also configure the service to automatically impersonate all callers in all methods via the ImpersonateAll property. ImpersonateAll defaults to false, but when set to true the attribute will impersonate all callers in all operations without the need to apply any operation behavior attributes or host configuration:

 [SecurityBehavior(ServiceSecurity.Intranet,ImpersonateAll = true)] class MyService : IMyContract {...} 

10.13.1.2. Configuring Internet service

With the Internet scenario, you need to both configure for the Internet scenario and to select the service certificate to use. Note in Example 10-19 that the ServiceBehavior attribute constructor may take the service certificate name. Unspecified, the service certificate is loaded from the host config file as with Example 10-8:

 [SecurityBehavior(ServiceSecurity.Internet)] class MyService : IMyContract {...} 

You can also specify the service certificate name, in which case the specified certificate is loaded from the LocalMachine store from the My folder by name:

 [SecurityBehavior(ServiceSecurity.Internet,"MyServiceCert")] class MyService : IMyContract {...} 

If the certificate name is set to an empty string, then the attribute will infer the certificate name by using the hosting machine name (or domain) for the certificate name and load such a certificate from the LocalMachine store from the My folder by name:

 [SecurityBehavior(ServiceSecurity.Internet,"")] class MyService : IMyContract {...} 

Finally, the attribute lets you explicitly specify the store location, the store name, and the lookup method:

 [SecurityBehavior(ServiceSecurity.Internet,                   StoreLocation.LocalMachine,StoreName.My,                   X509FindType.FindBySubjectName,"MyServiceCert")] class MyService : IMyContract {...} 

Note that you can combine explicit location with an inferred certificate name:

 [SecurityBehavior(ServiceSecurity.Internet,                   StoreLocation.LocalMachine,StoreName.My,                   X509FindType.FindBySubjectName,"")] class MyService : IMyContract {...} 

Which credentials store to authenticate the client against is indicated by the UseAspNetProviders property. UseAspNetProviders defaults to false, meaning the default will be to authenticate the client's username and password as Windows credentials, as with Example 10-11. Because of that, when UseAspNetProviders is false you can by default use Windows NT groups for authorization:

 [SecurityBehavior(ServiceSecurity.Internet,"MyServiceCert")] class MyService : IMyContract {    [PrincipalPermission(SecurityAction.Demand,Role = @"<Domain>\Customer")]    public void MyMethod( )    {...} } 

and even impersonate all callers:

 [SecurityBehavior(ServiceSecurity.Internet,"MyServiceCert",ImpersonateAll = true)] class MyService : IMyContract {...} 

Instead of Windows credentials, if UseAspNetProviders is set to true, the attribute will use the ASP.NET membership and role providers as prescribed for the Internet scenario:

 [SecurityBehavior(ServiceSecurity.Internet,"MyServiceCert",                   UseAspNetProviders = true)] class MyService : IMyContract {    [PrincipalPermission(SecurityAction.Demand,Role = "Manager")]    public void MyMethod( )    {...} } 

The attribute will programmatically enable the role manager section in the config file.


The attribute allows the use of the NetTcpBinding with ServiceSecurity.Internet along with ASP.NET providers to allow intranet applications to avoid using Windows accounts and groups, as explained previously.

Next is the issue of supplying the application name for the ASP.NET providers. That is governed by the ApplicationName property. Unassigned, the attribute will look up the application name from the config file as in Examples 10-13 and 10-14. If no value is found in the host config file, the attribute will not default to using the meaningless / from machine.config. Instead, it will default to the host assembly name for the application name. If the ApplicationName property is assigned a value, it will override whatever application name is present in the host config file:

 [SecurityBehavior(ServiceSecurity.Internet,"MyServiceCert",                   UseAspNetProviders = true,ApplicationName = "MyApplication")] class MyService : IMyContract {...} 

10.13.1.3. Configuring business-to-business service

Configuring for the business-to-business scenario requires setting ServiceSecurity to ServiceSecurity.BusinessToBusiness. The attribute will use peer trust for validating the client's certificate. Configuring the service certificate is done just as with the ServiceSecurity.Internet; for example:

 [SecurityBehavior(ServiceSecurity.BusinessToBusiness)] class MyService : IMyContract {...} [SecurityBehavior(ServiceSecurity.BusinessToBusiness,"")] class MyService : IMyContract {...} [SecurityBehavior(ServiceSecurity.BusinessToBusiness,"MyServiceCert")] class MyService : IMyContract {...} 

By default, with ServiceSecurity.BusinessToBusiness, the attribute will set the PrincipalPermissionMode property of the host to PrincipalPermissionMode.None, and the service will not be able to authorize its callers. However, setting the UseAspNetProviders property to true will enable using the ASP.NET role providers, as in Example 10-18:

 [SecurityBehavior(ServiceSecurity.BusinessToBusiness,UseAspNetProviders = true)] class MyService : IMyContract {...} 

When using the ASP.NET role providers, the application name is looked up and decided upon just as with ServiceSecurity.Internet:

 [SecurityBehavior(ServiceSecurity.BusinessToBusiness,"MyServiceCert",                   UseAspNetProviders = true,ApplicationName = "MyApplication")] class MyService : IMyContract {...} 

10.13.1.4. Configuring Anonymous service

To allow callers according to the anonymous scenario, you need to configure the attribute with ServiceSecurity.Anonymous. Configuring the service certificate is done just as with the ServiceSecurity.Internet; for example:

 [SecurityBehavior(ServiceSecurity.Anonymous)] class MyService : IMyContract {...} [SecurityBehavior(ServiceSecurity.Anonymous,"")] class MyService : IMyContract {...} [SecurityBehavior(ServiceSecurity.Anonymous,"MyServiceCert")] class MyService : IMyContract {...} 

10.13.1.5. Configuring No-Security service

To turn off security completely, provide the attribute with ServiceSecurity.None:

 [SecurityBehavior(ServiceSecurity.None)] class MyService : IMyContract {...} 

10.13.1.6. Implementing SecurityBehaviorAttribute

Example 10-20 is a partial listing of the implementation of SecurityBehaviorAttribute.

Example 10-20. Implementing SecurityBehaviorAttribute

 [AttributeUsage(AttributeTargets.Class)] class SecurityBehaviorAttribute : Attribute,IServiceBehavior {    SecurityBehavior m_SecurityBehavior;    string m_ApplicationName = String.Empty;    bool m_UseAspNetProviders;    bool m_ImpersonateAll;    public SecurityBehaviorAttribute(ServiceSecurity mode)    {       m_SecurityBehavior = new SecurityBehavior(mode);    }    public SecurityBehaviorAttribute(ServiceSecurity mode,                                     string serviceCertificateName)    {       m_SecurityBehavior = new SecurityBehavior(mode,serviceCertificateName);    }    public bool ImpersonateAll     //Accesses m_ImpersonateAll    {get;set;}    public string ApplicationName  //Accesses m_ApplicationName    {get;set;}    public bool UseAspNetProviders //Accesses m_UseAspNetProviders    {get;set;}    void IServiceBehavior.ApplyDispatchBehavior(...)    {}    void IServiceBehavior.AddBindingParameters(ServiceDescription description,                                               ServiceHostBase serviceHostBase,                                              Collection<ServiceEndpoint> endpoints,                                              BindingParameterCollection parameters)    {      m_SecurityBehavior.AddBindingParameters(description,serviceHostBase,                                                              endpoints,parameters);    }    void IServiceBehavior.Validate(ServiceDescription description,                                   ServiceHostBase serviceHostBase)    {       m_SecurityBehavior.UseAspNetProviders = UseAspNetProviders;       m_SecurityBehavior.ApplicationName = ApplicationName;       m_SecurityBehavior.ImpersonateAll = ImpersonateAll;       m_SecurityBehavior.Validate(description,serviceHostBase);    }    //Rest of the implementation } 

The three public properties of the attribute correspond to three private members where the attribute saves the configured values.

SecurityBehaviorAttribute is a service behavior attribute, so you can apply it directly on the service class. When the AddBindingParameters( ) method of IServiceBehavior is called, SecurityBehaviorAttribute enforces the binding configuration that matches the requested scenario. The Validate( ) method of IServiceBehavior is where SecurityBehaviorAttribute configures the host. Other than that, the attribute does little or no work besides sequencing the overall order of configuration. The actual configuration is accomplished using a helper class called SecurityBehavior. The attribute constructs an instance of SecurityBehavior, providing it with the scenario (the mode parameter) as well as the certificate name in the matching constructor. SecurityBehavior provides systematic meticulous setting of all security scenarios using programmatic calls. SecurityBehavior encapsulates all the explicit steps described previously per scenario. SecurityBehavior is a service behavior in its own right, and it is designed to even be used standalone, independent of the attribute. Example 10-21 contains a partial listing of SecurityBehavior, demonstrating how it operates.

Example 10-21. Implementing SecurityBehavior (partial)

 class SecurityBehavior : IServiceBehavior {    ServiceSecurity m_Mode;    StoreLocation m_StoreLocation;    StoreName m_StoreName;    X509FindType m_FindType;    string m_SubjectName;    bool m_UseAspNetProviders;    string m_ApplicationName = String.Empty;    bool m_ImpersonateAll;    public SecurityBehavior(ServiceSecurity mode) :                                 this(mode,StoreLocation.LocalMachine,StoreName.My,                                                X509FindType.FindBySubjectName,null)    {}    public SecurityBehavior(ServiceSecurity mode,StoreLocation storeLocation,                       StoreName storeName,X509FindType findType,string subjectName)    {...} //Sets the corresponding members   public bool ImpersonateAll     //Accesses m_ImpersonateAll    {get;set;}    public bool UseAspNetProviders //Accesses UseAspNetProviders    {get;set;}    public string ApplicationName  //Accesses m_ApplicationName    {get;set;}    public void Validate(ServiceDescription description,                         ServiceHostBase serviceHostBase)    {       if(m_SubjectName != null)       {          switch(m_Mode)          {             case ServiceSecurity.Anonymous:             case ServiceSecurity.BusinessToBusiness:             case ServiceSecurity.Internet:             {                string subjectName;                if(m_SubjectName != String.Empty)                {                   subjectName = m_SubjectName;                }                else                {                   subjectName = description.Endpoints[0].Address.Uri.Host;                }                serviceHostBase.Credentials.ServiceCertificate.                 SetCertificate(m_StoreLocation,m_StoreName,m_FindType,subjectName);                break;             }          }       }       .       .       .    }    public void AddBindingParameters(ServiceDescription description,                                     ServiceHostBase serviceHostBase,                                     Collection<ServiceEndpoint> endpoints,                                     BindingParameterCollection parameters)    {       .       .       .       switch(m_Mode)       {          case ServiceSecurity.Intranet:          {             ConfigureIntranet(endpoints);             break;          }          case ServiceSecurity.Internet:          {             ConfigureInternet(endpoints,UseAspNetProviders);             break;          }          .          .          .       }    }    internal static void ConfigureInternet(Collection<ServiceEndpoint> endpoints)    {       foreach(ServiceEndpoint endpoint in endpoints)       {          Binding binding = endpoint.Binding;          if(binding is WSHttpBinding)          {             WSHttpBinding wsBinding = (WSHttpBinding)binding;             wsBinding.Security.Mode = SecurityMode.Message;             wsBinding.Security.Message.ClientCredentialType =                                            MessageCredentialType.UserName;             continue;          }          .          .          .          throw new InvalidOperationException(binding.GetType( ) +                                     "is unsupported with ServiceSecurity.Internet");       }    }    //Rest of the implementation } 

The constructors of SecurityBehavior store in member variables the construction parameters, such as the security mode and the details of the certificate. The Validate( ) method is a decision tree that configures the host according to the scenario and the provided information, supporting the behavior of SecurityBehaviorAttribute described earlier. AddBindingParameters( ) calls a dedicated helper method for each scenario to configure the collection of endpoints the host exposes. Each helper method (such as ConfigureInternet( )) iterates over the collection of endpoints of the service. For each endpoint, it verifies whether the binding used matches the scenario, and then configures the binding according to the scenario.

10.13.2. Host and Declarative Security

While declarative security via the SecurityBehavior attribute is easy and handy, often it is up to the host to configure security, and the service just focuses on the business logic. In addition, you may be required to host services you do not develop, and those services do not happen to use my declarative security. The natural next step is to add declarative security to ServiceHost<T> with a set of SetSecurityBehavior( ) methods:

 public class ServiceHost<T> : ServiceHost {    public void SetSecurityBehavior(ServiceSecurity mode,                                    bool useAspNetProviders,                                    string applicationName,                                    bool impersonateAll);    public void SetSecurityBehavior(ServiceSecurity mode,                                    string serviceCertificateName,                                    bool useAspNetProviders,                                    string applicationName,                                    bool impersonateAll);    //More members } 

Using the declarative security via the host follows the same consistent guidelines as with the SecurityBehavior attribute. For example, here is how to configure the host (and the service) for Internet security with ASP.NET providers:

 ServiceHost<MyService> host = new ServiceHost<MyService>( ); host.SetSecurityBehavior(ServiceSecurity.Internet,                          "MyServiceCert",true,"MyApplication",false); host.Open( ); 

Example 10-22 shows a partial listing of the declarative security support in ServiceHost<T>.

Example 10-22. Adding declarative security for ServiceHost<T>

 public class ServiceHost<T> : ServiceHost {    public void SetSecurityBehavior(ServiceSecurity mode,                                    string serviceCertificateName,                                    bool useAspNetProviders,                                    string applicationName,                                    bool impersonateAll)    {       if(State == CommunicationState.Opened)       {          throw new InvalidOperationException("Host is already opened");       }       SecurityBehavior securityBehavior = new                                      SecurityBehavior(mode,serviceCertificateName);       securityBehavior.UseAspNetProviders = useAspNetProviders;       securityBehavior.ApplicationName = applicationName;       securityBehavior.ImpersonateAll = impersonateAll;      Description.Behaviors.Add(securityBehavior);    }    //More members } 

The implementation of SetSecurityBehavior( ) relies on the fact that the SecurityBehavior class supports IServiceBehavior. SetSecurityBehavior( ) initializes an instance of SecurityBehavior with the supplied parameters and then adds it to the collection of behaviors in the service description, as if the service were decorated with SecurityBehaviorAttribute.

10.13.3. Client-Side Declarative Security

WCF does not allow applying an attribute on the proxy class, and while a contract-level attribute is possible, the client may need to provide its credentials and other settings at runtime. The first step in supporting declarative security on the client side is my SecurityHelper static helper class, defined in Example 10-23.

Example 10-23. The SecurityHelper helper class

 public static class SecurityHelper {    public static void UnsecuredProxy<T>(ClientBase<T> proxy)  where T : class;    public static void AnonymousProxy<T>(ClientBase<T> proxy)  where T : class;    public static void SecureProxy<T>(ClientBase<T> proxy,                                   string userName,string password) where T : class;    public static void SecureProxy<T>(ClientBase<T> proxy,                     string domain,string userName,string password) where T : class;    public static void SecureProxy<T>(ClientBase<T> proxy,string domain,         string userName,string password,TokenImpersonationLevel impersonationLevel)                                                                    where T : class;    public static void SecureProxy<T>(ClientBase<T> proxy,                                      string clientCertificateName) where T : class;    public static void SecureProxy<T>(ClientBase<T> proxy,                                    StoreLocation storeLocation,StoreName storeName,                X509FindType findType,string clientCertificateName) where T : class;    //More members } 

You use SecurityHelper to configure a plain proxy according to the desired security scenario and behavior, using the dedicated static methods SecurityHelper offers. You can only configure the proxy before opening the proxy. There is no need for any security settings in the client's config file or elsewhere in the client's code.

SecurityHelper is smart, and it will select the correct security behavior based on provided parameters and the method invoked. There is no need to explicitly use the ServiceSecurity enum.

For example, here is how to secure a proxy for the intranet scenario and provide it with the client's Windows credentials:

 MyContractClient proxy = new MyContractClient( ); SecurityHelper.SecureProxy(proxy,"MyDomain","MyUsername","MyPassword"); proxy.MyMethod( ); proxy.Close( ); 

For the Internet scenario, the client only needs to provide the username and the password (remember that the decision of whether those are Windows or ASP.NET provider credentials is a service-side decision):

 MyContractClient proxy = new MyContractClient( ); SecurityHelper.SecureProxy(proxy,"MyUsername","MyPassword"); proxy.MyMethod( ); proxy.Close( ); 

For the business-to-business scenario, the client can specify a null or an empty string for the client certificate name if it wants to use the certificate in its config file, or it can list the certificate name explicitly:

 MyContractClient proxy = new MyContractClient( ); SecurityHelper.SecureProxy(proxy,"MyClientCert"); proxy.MyMethod( ); proxy.Close( ); 

SecurityHelper will load the certificate from the client's LocalMachine store from the My folder by name. The client can also specify all the information required to find and load the certificate. For simplicity's sake in designing SecurityHelper, when using the BasicHttpBinding in the business-to-business scenario, the client must explicitly specify the service certificate location, either in the config file or programmatically.

For an anonymous client, use the AnonymousProxy( ) method:

 MyContractClient proxy = new MyContractClient( ); SecurityHelper.AnonymousProxy(proxy); proxy.MyMethod( ); proxy.Close( ); 

and for no security at all, use the UnsecuredProxy( ) method:

 MyContractClient proxy = new MyContractClient( ); SecurityHelper.UnsecuredProxy(proxy); proxy.MyMethod( ); proxy.Close( ); 

10.13.3.1. Implementing SecurityHelper

Internally, SecurityHelper uses SecurityBehavior to configure the proxy's endpoint as well as set the credentials, as shown in Example 10-24.

Example 10-24. Implementing SecurityHelper (partial)

 public static class SecurityHelper {    public static void SecureProxy<T>(ClientBase<T> proxy,                                    string userName,string password) where T : class    {       if(proxy.State == CommunicationState.Opened)       {          throw new InvalidOperationException("Proxy channel is already opened");       }       Collection<ServiceEndpoint> endpoints = new Collection<ServiceEndpoint>( );       endpoints.Add(proxy.Endpoint);       SecurityBehavior.ConfigureInternet(endpoints);       proxy.ClientCredentials.UserName.UserName = userName;       proxy.ClientCredentials.UserName.Password = password;       proxy.ClientCredentials.ServiceCertificate.Authentication.                CertificateValidationMode = X509CertificateValidationMode.PeerTrust;    }    //Rest of the implementation } 

10.13.3.2. The SecureClientBase<T> class

The advantage of using SecurityHelper is that it can operate on any proxy; even a proxy the client developer is not responsible for creating. The disadvantage is that it is a step the client has to take. If you are responsible for generating the proxy, you can take advantage of my SecureClientBase<T> class, defined in Example 10-25.

Example 10-25. The SecureClientBase<T> class

 public abstract class SecureClientBase<T> : ClientBase<T> where T : class {    //These constructors target the default endpoint    protected SecureClientBase( );    protected SecureClientBase(ServiceSecurity mode);    protected SecureClientBase(string userName,string password);    protected SecureClientBase(string domain,string userName,string password,                               TokenImpersonationLevel impersonationLevel);    protected SecureClientBase(string domain,string userName,string password);    protected SecureClientBase(string clientCertificateName);    protected SecureClientBase(StoreLocation storeLocation,                               StoreName storeName,X509FindType findType,                               string clientCertificateName);    //More constructors for other types of endpoints } 

SecureClientBase<T> derives from the conventional ClientBase<T> and adds the declarative security support. You need to derive your proxy from SecureClientBase<T> instead of ClientBase<T>, provide constructors that match your security scenario, and call the base constructors of SecureClientBase<T> with the supplies credentials and endpoint information:

 class MyContractClient : SecureClientBase<IMyContract>,IMyContract {    public MyContractClient(ServiceSecurity mode) : base(mode)    {}    public MyContractClient(string userName,string password) :                                                            base(userName,password)    {}    /* More constructors */    public void MyMethod( )    {       Channel.MyMethod( );    } } 

Using the derived proxy is straightforward. For example, for the Internet scenario:

 MyContractClient proxy = new MyContractClient("MyUsername","MyPassword"); proxy.MyMethod( ); proxy.Close( ); 

or for the Anonymous scenario:

 MyContractClient proxy = new MyContractClient(ServiceSecurity.Anonymous); proxy.MyMethod( ); proxy.Close( ); 

The implementation of SecureClientBase<T> simply uses SecurityHelper (as shown in Example 10-26) so SecureClientBase<T> follows the same behaviors, such as regarding the client certificate.

Example 10-26. Implementing SecureClientBase<T> (partial)

 public class SecureClientBase<T> : ClientBase<T> where T : class {    protected SecureClientBase(ServiceSecurity mode)    {       switch(mode)       {          case ServiceSecurity.None:          {             SecurityHelper.UnsecuredProxy(this);             break;          }          case ServiceSecurity.Anonymous:          {             SecurityHelper.AnonymousProxy(this);             break;          }          ...       }    }    protected SecureClientBase(string userName,string password)    {       SecurityHelper.SecureProxy(this,userName,password);    }    //More constructors } 

10.13.3.3. Secure channel factory

If you are not using a proxy at all, then both SecurityHelper and SecureClientBase<T> are of little use to you. For that case, I wrote the SecureChannelFactory<T> class, defined in Example 10-27.

Example 10-27. The SecureChannelFactory<T>

 public class SecureChannelFactory<T> : ChannelFactory<T> {    public SecureChannelFactory( )    {}    public SecureChannelFactory(string endpointName) : base(endpointName)    {}    //More constructors    public void SetSecurityMode(ServiceSecurity mode);    public void SetCredentials(string userName,string password);    public void SetCredentials(string domain,string userName,string password,                               TokenImpersonationLevel impersonationLevel);    public void SetCredentials(string domain,string userName,string password);    public void SetCredentials(string clientCertificateName);    public void SetCredentials(StoreLocation storeLocation,StoreName storeName,                               X509FindType findType,string clientCertificateName); } 

You use SecureChannelFactory<T> just like the WCF-provided channel factory, except you can utilize declarative security. You need to call the SetSecurityMode( ) method or one of the SetCredentials( ) methods that fits your target scenario before opening the channel. For example, with a proxy to an Internet security-based service:

 SecureChannelFactory<IMyContract> factory =                                                new SecureChannelFactory< IMyContract>(""); factory.SetCredentials("MyUsername","MyPassword"); IMyContract proxy = factory.CreateChannel( ); using(proxy as IDisposable) {    proxy.MyMethod( ); } 

Implementing SecureChannelFactory<T> was very similar to implementing SecurityHelper and so I have omitted detailing that code.

10.13.3.4. Duplex client and declarative security

I also provided the SecureDuplexClientBase<T,C> class (similar to SecureClientBase<T>), defined in Example 10-28.

Example 10-28. The SecureDuplexClientBase<T,C> class

 public abstract class SecureDuplexClientBase<T,C> : DuplexClientBase<T,C>                                                     where T : class {    protected SecureDuplexClientBase(C callback);    protected SecureDuplexClientBase(ServiceSecurity mode,C callback);    protected SecureDuplexClientBase(string userName,string password,C callback);    protected SecureDuplexClientBase(string domain,string userName,string password,                            TokenImpersonationLevel impersonationLevel,C callback);    protected SecureDuplexClientBase(string domain,string userName,string password,                                                                       C callback);    protected SecureDuplexClientBase(string clientCertificateName,C callback);    protected SecureDuplexClientBase(StoreLocation storeLocation,                                     StoreName storeName,X509FindType findType,                                     string clientCertificateName,C callback);   /* More constructors with InstanceContext<C> and constructors that       target the configured endpoint and a programatic endpoint */ } 

SecureDuplexClientBase<T,C> derives from my type-safe DuplexClientBase<T,C> class presented in Chapter 5, and it adds the declarative scenario-based security support. As with the DuplexClientBase<T,C> class, you need to derive your proxy class from it, and take advantage of either the callback parameter or the type-safe context InstanceContext<C>. For example, given this service contract and callback contract definition:

 [ServiceContract(CallbackContract = typeof(IMyContractCallback))] interface IMyContract {    [OperationContract]    void MyMethod( ); } interface IMyContractCallback {    [OperationContract]    void OnCallback( ); } 

your derived proxy class will look like this:

 class MyContractClient :                 SecureDuplexClientBase<IMyContract,IMyContractCallback>,IMyContract {    public MyContractClient(IMyContractCallback callback) : base(callback)    {}    public MyContractClient(ServiceSecurity mode,IMyContractCallback callback)                                                               : base(mode,callback)    {}    /* More constructors */    public void MyMethod( )    {       Channel.MyMethod( );    } } 

When using it, provide the security scenario or credentials, the callback object, and the endpoint information. For example, when targeting the Anonymous scenario:

 class MyClient : IMyContractCallback {...} IMyContractCallback callback = new MyClient( ); MyContractClient proxy = new MyContractClient(ServiceSecurity.Anonymous,callback); proxy.MyMethod( ); proxy.Close( ); 

Implementing SecureDuplexClientBase<T,C> was almost identical to SecureClientBase<T>, with the main difference being a different base class.

10.13.3.5. The SecureDuplexChannelFactory<T,C> class

When you're not using a SecureDuplexClientBase<T,C>-derived proxy to set up the bidirectional communication, you can use my SecureDuplexChannelFactory<T,C> channel factory, defined in Example 10-29.

Example 10-29. The SecureDuplexChannelFactory<T,C>

 public class SecureDuplexChannelFactory<T,C> : DuplexChannelFactory<T,C>                                                                     where T : class {    public SecureDuplexChannelFactory(C callback)    public SecureDuplexChannelFactory(InstanceContext<C> context,Binding binding) :                                                               base(context,binding)    //More constructors    public void SetSecurityMode(ServiceSecurity mode);    public void SetCredentials(string userName,string password);    public void SetCredentials(string domain,string userName,string password,                               TokenImpersonationLevel impersonationLevel);    public void SetCredentials(string domain,string userName,string password);    public void SetCredentials(string clientCertificateName);    public void SetCredentials(StoreLocation storeLocation,StoreName storeName,                               X509FindType findType,string clientCertificateName); } 

SecureDuplexChannelFactory<T,C> derives from the type-safe DuplexChannelFactory<T,C> defined in Chapter 5. You construct SecureDuplexChannelFactory<T,C> with an instance of the generic type parameter for the callback (or an instance of the type-safe InstanceContext<C>). You need to call the SetSecurityMode( ) method or one of the SetCredentials( ) methods that fits your target scenario before opening the channel. For example, when targeting the Internet scenario:

 class MyClient : IMyContractCallback {...} IMyContractCallback callback = new MyClient( ); SecureDuplexChannelFactory<IMyContract,IMyContractCallback> factory =       new SecureDuplexChannelFactory<IMyContract,IMyContractCallback>(callback,""); factory.SetCredentials("MyUsername","MyPassword"); IMyContract proxy = factory.CreateChannel( ); using(proxy as IDisposable) {    proxy.MyMethod( ); } 

Implementing SecureDuplexChannelFactory<T,C> is very similar to implementing SecureChannelFactory<T> with the main difference being the base factory.




Programming WCF Services
Programming WCF Services
ISBN: 0596526997
EAN: 2147483647
Year: 2004
Pages: 148
Authors: Juval Lowy

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net