David Sackstein's Blog

"The more that you learn, the more places you'll go.”, Dr. Seuss

June 2009 - Posts

Mapping MediaElement onto a Sphere with WPF

In my previous post I created a 3D cube, mapped a MediaElement onto each of its faces and animated the camera around the cube. In this post, I will replace the 3D cube with a sphere and wrap the video onto the surface of the sphere. I omit the camera animation for simplicity, but you can add it back, of course.

You can download the source code here.

In order to create the sphere mesh I made use of Charles Petzold’s elegant SphereMeshGenerator.

In Chapter 6 of his book (3D Programming for Windows: Three-Dimensional Graphics Programming for the Windows Presentation Foundation) Petzold explains the technique very clearly - a highly recommended read. In this post, I will just provide the highlights.

The SphereMeshGenerator class is a factory that generates the MeshGeometry3D in code. Here it is:

//-----------------------------------------------------

// SphereMeshGenerator.cs (c) 2007 by Charles Petzold

//-----------------------------------------------------

using System;

using System.Windows;

using System.Windows.Media;

using System.Windows.Media.Media3D;

namespace Petzold.SphereResourceDemo

{

    public class SphereMeshGenerator

    {

        // Four private initialized fields.

        int slices = 32;

        int stacks = 16;

        Point3D center = new Point3D();

        double radius = 1;

        // Four public properties allow access to private fields.

        public int Slices

        {

            set { slices = value; }

            get { return slices; }

        }

        public int Stacks

        {

            set { stacks = value; }

            get { return stacks; }

        }

        public Point3D Center

        {

            set { center = value; }

            get { return center; }

        }

        public double Radius

        {

            set { radius = value; }

            get { return radius; }

        }

        // Get-only property generates MeshGeometry3D.

        public MeshGeometry3D Geometry

        {

            get

            {

                // Create a MeshGeometry3D.

                MeshGeometry3D mesh = new MeshGeometry3D();

                // Fill the vertices, normals, and textures collections.

                for (int stack = 0; stack <= Stacks; stack++)

                {

                    double phi = Math.PI / 2 - stack * Math.PI / Stacks;

                    double y = Radius * Math.Sin(phi);

                    double scale = -Radius * Math.Cos(phi);

                    for (int slice = 0; slice <= Slices; slice++)

                    {

                        double theta = slice * 2 * Math.PI / Slices;

                        double x = scale * Math.Sin(theta);

                        double z = scale * Math.Cos(theta);

                        Vector3D normal = new Vector3D(x, y, z);

                        mesh.Normals.Add(normal);

                        mesh.Positions.Add(normal + Center);

                        mesh.TextureCoordinates.Add(

                                    new Point((double)slice / Slices,

                                              (double)stack / Stacks));

                    }

                }

                // Fill the indices collection.

                for (int stack = 0; stack < Stacks; stack++)

                {

                    int top = (stack + 0) * (Slices + 1);

                    int bot = (stack + 1) * (Slices + 1);

                    for (int slice = 0; slice < Slices; slice++)

                    {

                        if (stack != 0)

                        {

                            mesh.TriangleIndices.Add(top + slice);

                            mesh.TriangleIndices.Add(bot + slice);

                            mesh.TriangleIndices.Add(top + slice + 1);

                        }

                        if (stack != Stacks - 1)

                        {

                            mesh.TriangleIndices.Add(top + slice + 1);

                            mesh.TriangleIndices.Add(bot + slice);

                            mesh.TriangleIndices.Add(bot + slice + 1);

                        }

                    }

                }

                return mesh;

            }

        }

    }

}

A slice is the area of the sphere surface that lies between two lines of longitude. The lines of longitude are circles centered at the center of the sphere that pass through the North Pole and South Pole. A stack is the area of the sphere surface that lies between two lines of latitude. The lines of latitude are parallel to the plane of the equator; only the equator itself is centered about the center of the sphere.

Properties of the SphereMeshGenerator allow you to specify the center and radius of the sphere as well as the number of slices and stacks that will be used. The higher the number of slices and stacks the smoother the image and the greater the load on the CPU and GPU.

Using the SphereMeshGenerator is straightforward. Just create a static instance of the SphereMeshGenerator as a resource of the Window, and bind the Geometry property of the ModelVisual3D to the Geometry factory property of SphereMeshGenerator.

Here is the XAML I used.

<Window x:Class="MediaElementOnSphere.Window1"

       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       xmlns:local="clr-namespace:Petzold.SphereResourceDemo"

       Title="Window1"

       Width="500"

       Height="400"

       Loaded="Window_Loaded">

   <Window.Resources>

      <local:SphereMeshGenerator x:Key="sphereMeshGenerator"

        Center="0 0 0"

        Radius="2" />

   </Window.Resources>

   <Grid>

      <Viewport3D>

         <Viewport3D.Camera>

            <PerspectiveCamera x:Name="camera"

                              UpDirection="0,1,0"

                              LookDirection="-4,-4,-4"

                              Position="4,4,4" />

         </Viewport3D.Camera>

         <ModelVisual3D>

            <ModelVisual3D.Content>

               <AmbientLight Color="White"></AmbientLight >

            </ModelVisual3D.Content>

         </ModelVisual3D>

         <ModelVisual3D>

            <ModelVisual3D.Content>

               <GeometryModel3D Geometry=

                    "{Binding Source={StaticResource sphereMeshGenerator}, Path=Geometry}">

                  <GeometryModel3D.Material>

                     <DiffuseMaterial>

                        <DiffuseMaterial.Brush>

                           <VisualBrush>

                              <VisualBrush.Visual>

                                 <MediaElement>

                                    <MediaElement.Triggers>

                                       <EventTrigger RoutedEvent="MediaElement.Loaded">

                                          <EventTrigger.Actions>

                                             <BeginStoryboard>

                                                <Storyboard>

                                                   <MediaTimeline x:Name="mediaTimeline"

                                                                  RepeatBehavior="Forever" />

                                                </Storyboard>

                                             </BeginStoryboard>

                                          </EventTrigger.Actions>

                                       </EventTrigger>

                                    </MediaElement.Triggers>

                                 </MediaElement>

                              </VisualBrush.Visual>

                           </VisualBrush>

                        </DiffuseMaterial.Brush>

                     </DiffuseMaterial>

                  </GeometryModel3D.Material>

               </GeometryModel3D>

            </ModelVisual3D.Content>

         </ModelVisual3D>

      </Viewport3D>

   </Grid>

</Window>

Finally, here is a snapshot of my spherical cinema:

image

Summary:

In this post I demonstrated how you can map a MediaElement onto the surface of a 3D spherical mesh in WPF. I used Charles Petzold’s SphereMeshGenerator to generate the mesh and set the DiffuseMaterial.Brush property to a VisualBrush that references the MediaElement.

Animating the Position of a 3D Camera in WPF

In this post we will use Point3DAnimationUsingPath from the previous post to rotate the viewpoint around a 3D cube while playing a video on all six of its faces.

You can download the complete source, with the Point3DAnimationUsingPath class from here.

Point3DAnimationUsingPath

"Look at me!
Look at me now!" said the cat.
"With a cup and a cake
On the top of my hat!
I can hold up TWO books!
I can hold up the fish!
And a litte toy ship!
And some milk on a dish!
And look!
I can hop up and down on the ball!
But that is not all!
Oh, no.
That is not all... “,  Dr.Seuss

 

Motivation

It is fairly straightforward to rotate 3D objects in WPF using the various Transform3D derived classes. But sometimes what you need is to rotate the viewer around the scene and not to rotate individual objects. That is, you need to animate the Position property of the Camera object over a path in three dimensions.

The problem is that the type of the Position property is Point3D and WPF doesnt provide a Point3DAnimationUsingPath class. I partially filled that gap with a class of that name in this previous post.

In addition, as we modify the Position of the Camera, we need to change the LookDirection so that the Camera always points to the same point in space.

To demonstrate the solution, I will place a cube at the center of the 3D coordinate system and play video on each side so you can clearly see how we are rotating around the object.

“Look at me! Look at me now!”, say I, “With a cube in the center and a video on SIX faces, I can rotate around it and look back to the center! But that is not all! Oh no, that is not all…”

Solution

I will build this solution in 8 steps:

  1. Describe the cube.
  2. Create a MediaElement that plays video continuously.
  3. Map the MediaElement on to each of the six faces of the cube.
  4. Define the light source.
  5. Define the camera and bind the LookDirection to a function of its Position.
  6. Describe the path over which we will animate the Position.
  7. Apply the Point3DAnimationUsingPath to the Position property of the Camera
  8. Put it all together.

Describe the Cube

We describe the cube as a MeshGeometry3D and place it in a GeometryModel3D. This can then be placed inside a ModelVisual3D which will be added to the Viewport3D.

In the MeshGeometry3D we define the Positions and TriangleIndices as follows:

   <GeometryModel3D.Geometry>

      <MeshGeometry3D Positions=

               "-1,-1,-1    1,-1,-1   1, 1,-1   -1, 1,-1

                -1,-1,-1   -1, 1,-1  -1, 1, 1   -1,-1, 1  

                 1,-1,-1   -1,-1,-1  -1,-1, 1    1,-1, 1

                 1, 1,-1    1,-1,-1   1,-1, 1    1, 1, 1   

                 1,-1, 1   -1,-1, 1  -1, 1, 1    1, 1, 1

                -1, 1,-1    1, 1,-1   1, 1, 1   -1, 1, 1"

      TriangleIndices=

               "3,2,1       1,0,3

                7,6,5       5,4,7

                11,10,9     9,8,11

                15,14,13    13,12,15

                19,18,17    17,16,19

                23,22,21    21,20,23"

      </MeshGeometry3D>

   </GeometryModel3D.Geometry>

There are more than one ways to do this, but I chose the more verbose way in order to make it easier to define the mapping between the MediaElement and the faces of the cube.

In the Positions collection there are six lines, one for each face of the cube, four points for each face.

In the TriangleIndices there are six lines, one for each face of the cube, two triangles for each face. Note that the order of the points in the triangles is counterclockwise around the outfacing norm to the triangle’s plane.

Here is a diagram of the cube and its vertices:

3D Cube in XAML

Create the MediaElement

The MediaElement itself is not new, but this XAML snippet also shows how it is used as the Visual property of a VisualBrush that we will use to paint the Material of our GeometryModel3D.

   <GeometryModel3D.Material>

      <DiffuseMaterial>

         <DiffuseMaterial.Brush>

            <VisualBrush>

               <VisualBrush.Visual>

                  <MediaElement>

                     <MediaElement.Triggers>

                        <EventTrigger RoutedEvent="MediaElement.Loaded">

                           <EventTrigger.Actions>

                              <BeginStoryboard>

                                 <Storyboard>

                                    <MediaTimeline x:Name="mediaTimeline"

                                      RepeatBehavior="Forever" />

                                 </Storyboard>

                              </BeginStoryboard>

                           </EventTrigger.Actions>

                        </EventTrigger>

                     </MediaElement.Triggers>

                  </MediaElement>

               </VisualBrush.Visual>

            </VisualBrush>

         </DiffuseMaterial.Brush>

      </DiffuseMaterial>

   </GeometryModel3D.Material>

This MediaElement has a MediaTimeline that plays forever. There is something missing, of course - the Uri identifying the source of the media. But, no fear, we will set that in code. That’s why the MediaTimeline has been given a name.

Map the MediaElement on to the Cube

There are two parts to this mapping.

The first part, completed in the previous step, is to set the Visual property of the VisualBrush for the Material. The second part is to provide a relative coordinate in the 2D space of the VisualBrush for each 3D coordinate in the Positions collection. I defined the Positions collection in a consistent way for each face, so it turns out that the texture mapping is identical for each of the six faces.

We will add this XAML snippet to the properties of the MeshGeometry3D (after TriangleIndices, for example):

TextureCoordinates=

         "1,1     0,1     0,0     1,0

          1,1     0,1     0,0     1,0

          1,1     0,1     0,0     1,0

          1,1     0,1     0,0     1,0

          1,1     0,1     0,0     1,0    

          1,1     0,1     0,0     1,0">

Define the Light Source

This one is simple. We will use an ambient white light to make sure that there is no shading on the cube.

   <ModelVisual3D>

      <ModelVisual3D.Content>

         <AmbientLight Color="White"></AmbientLight >

      </ModelVisual3D.Content>

   </ModelVisual3D>

Define the Camera

We will use a PerspectiveCamera in order to achieve the illusion of depth.

   <Viewport3D.Camera>

      <PerspectiveCamera

         x:Name="camera"

         UpDirection="0,0,1"

         LookDirection="{

               Binding RelativeSource={RelativeSource Self},

                       Path=Position,

                       Converter={StaticResource lookBackConverter}}"

         Position="0,0,0" />

   </Viewport3D.Camera>

The value of the Position property is meaningless because we are going to control this property with an animation. But, the LookDirection is interesting. In order to move the Camera and still look at the cube, we need to modify the look direction as the Position changes. This is how:

Camera.LookDirection = CenterPointOfInterest – Camera.Position

We could choose to animate the LookDirection concurrently with the Position property, but I chose a simpler, and in my opinion, a more elegant way.

I defined a converter from Camera.Position to Camera.LookDirection and apply it to a binding of LookDirection to Position.

This is the code for the converter:

    class LookBackConverter : IValueConverter

    {

        #region IValueConverter Members

 

        public object Convert(

            object value, Type targetType,

            object parameter,

            System.Globalization.CultureInfo culture)

        {

            return new Point3D(0,0,0) - (Point3D)value;

        }

 

        public object ConvertBack(

            object value, Type targetType,

            object parameter,

            System.Globalization.CultureInfo culture)

        {

            return null;

        }

 

        #endregion

    }

We could expose the center of interest as a public property of the LookBackConverter and set it in XAML, but for this demo, I will just assume its the origin of the coordinate system.

Describe the Path over which we will Animate the Position

You will recall from the previous post that Point3DAnimationUsingPath is lacking in that it doesn’t accept a description of a path in 3D space, but rather a constant Z coordinate and a path in 2D space.

So, we need to define a circle around the Z axis, with a diameter larger than the side of the cube, but not too much larger. The length of each side of the cube is 2 units, so 4 will do.

Unfortunately there is no EllipseGeometry for 2D, so I created my own with 2 ArcSegments. Here is the XAML:

   <PathGeometry x:Key="circlePath">

      <PathGeometry.Figures>

         <PathFigure StartPoint="-4, -4" IsClosed="False">

            <ArcSegment Point="4,4" Size="4, 4" />

            <ArcSegment Point="-4,-4" Size="4,4" />

         </PathFigure>

      </PathGeometry.Figures>

   </PathGeometry>

Apply the Point3DAnimationUsingPath to the Camera Position

Armed with the Point3DAnimationUsingPath and the circlePath PathGeometry it is now easy to define the animation we need:

   <Viewport3D.Triggers>

      <EventTrigger RoutedEvent="FrameworkElement.Loaded">

         <BeginStoryboard>

            <Storyboard>

               <local:Point3DAnimationUsingPath

                   Storyboard.TargetName="camera"

                   Storyboard.TargetProperty="Position"

                   Duration="0:0:20"

                   Z="2"

                   PathGeometry="{StaticResource circlePath}"

                   RepeatBehavior="Forever">

               </local:Point3DAnimationUsingPath>

            </Storyboard>

         </BeginStoryboard>

      </EventTrigger>

   </Viewport3D.Triggers>

As you can see, the camera named “camera” is the animation target and Position is the name of the target property. We set the circlePath PathGeometry as the PathGeometry and Z=2 so we rotate around the cube, but above it.

Putting It All Together

Here is the entire XAML for the solution:

<Window x:Class="RotatingCamera.Window1"

       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

       xmlns:local="clr-namespace:RotatingCamera"

       Title="Window1"

       Width="500"

       Height="400"

       Loaded="Window_Loaded">

   <Grid>

      <Viewport3D>

         <Viewport3D.Resources>

            <local:LookBackConverter x:Key="lookBackConverter" />

            <PathGeometry x:Key="circlePath">

               <PathGeometry.Figures>

                  <PathFigure StartPoint="-4, -4" IsClosed="False">

                     <ArcSegment Point="4,4" Size="4, 4" />

                     <ArcSegment Point="-4,-4" Size="4,4" />

                  </PathFigure>

               </PathGeometry.Figures>

            </PathGeometry>

         </Viewport3D.Resources>

         <Viewport3D.Triggers>

            <EventTrigger RoutedEvent="FrameworkElement.Loaded">

               <BeginStoryboard>

                  <Storyboard>

                     <local:Point3DAnimationUsingPath

                         Storyboard.TargetName="camera"

                         Storyboard.TargetProperty="Position"

                         Duration="0:0:20"

                         Z="2"

                         PathGeometry="{StaticResource circlePath}"

                         RepeatBehavior="Forever">

                     </local:Point3DAnimationUsingPath>

                  </Storyboard>

               </BeginStoryboard>

            </EventTrigger>

         </Viewport3D.Triggers>

         <Viewport3D.Camera>

            <PerspectiveCamera

               x:Name="camera"

               UpDirection="0,0,1"

               LookDirection="{

                     Binding RelativeSource={RelativeSource Self},

                             Path=Position,

                             Converter={StaticResource lookBackConverter}}"

               Position="0,0,0" />

         </Viewport3D.Camera>

         <ModelVisual3D>

            <ModelVisual3D.Content>

               <AmbientLight Color="White"></AmbientLight >

            </ModelVisual3D.Content>

         </ModelVisual3D>

         <ModelVisual3D>

            <ModelVisual3D.Content>

               <GeometryModel3D>

                  <GeometryModel3D.Geometry>

                     <MeshGeometry3D Positions=

                             "-1,-1,-1    1,-1,-1   1, 1,-1   -1, 1,-1

                               -1,-1,-1   -1, 1,-1  -1, 1, 1   -1,-1, 1  

                                1,-1,-1   -1,-1,-1  -1,-1, 1    1,-1, 1

                                1, 1,-1   1,-1,-1    1,-1, 1    1, 1, 1   

                                1,-1, 1   -1,-1, 1  -1, 1, 1    1, 1, 1

                               -1, 1,-1   1, 1,-1    1, 1, 1   -1, 1, 1"

                     TriangleIndices=

                             "3,2,1       1,0,3

                               7,6,5       5,4,7

                               11,10,9     9,8,11

                               15,14,13    13,12,15

                               19,18,17    17,16,19

                               23,22,21    21,20,23"

                     TextureCoordinates=

                             "1,1     0,1     0,0     1,0

                               1,1     0,1     0,0     1,0

                               1,1     0,1     0,0     1,0

                               1,1     0,1     0,0     1,0

                               1,1     0,1     0,0     1,0    

                               1,1     0,1     0,0     1,0">

                     </MeshGeometry3D>

                  </GeometryModel3D.Geometry>

                  <GeometryModel3D.Material>

                     <DiffuseMaterial>

                        <DiffuseMaterial.Brush>

                           <VisualBrush>

                              <VisualBrush.Visual>

                                 <MediaElement>

                                    <MediaElement.Triggers>

                                       <EventTrigger RoutedEvent="MediaElement.Loaded">

                                          <EventTrigger.Actions>

                                             <BeginStoryboard>

                                                <Storyboard>

                                                   <MediaTimeline x:Name="mediaTimeline"

                                                     RepeatBehavior="Forever" />

                                                </Storyboard>

                                             </BeginStoryboard>

                                          </EventTrigger.Actions>

                                       </EventTrigger>

                                    </MediaElement.Triggers>

                                 </MediaElement>

                              </VisualBrush.Visual>

                           </VisualBrush>

                        </DiffuseMaterial.Brush>

                     </DiffuseMaterial>

                  </GeometryModel3D.Material>

               </GeometryModel3D>

            </ModelVisual3D.Content>

         </ModelVisual3D>

      </Viewport3D>

   </Grid>

</Window>

and this is what my cube looked like in one of my orbits around it.

Point3DAnimationUsingPath

Summary

In this post I demonstrated the use of my Point3DAnimationUsingPath to animate the Position of a Camera around a 3D object.

Its amazing how with WPF you can have so much going on in parallel, video, animations and databinding. Like the Cat in The Hat, really.

A Custom Animation Class - Point3DAnimationUsingPath

For 2D graphics WPF provides three classes for animating points: PointAnimation (for linear interpolation), PointAnimationUsingKeyFrames and PointAnimationUsingPath.

For 3D graphics WPF provides only two: Point3DAnimation (for linear interpolation) and Point3DAnimationUsingKeyFrames. There is no built in class for animating points over a path in three dimensions.

In this post I will show you a custom animation class that I wrote to partially fill that gap. In the next post I will put it to use in a 3D animation demo.

Requirement

Provide an animation class that enables the animation of a 3D point over a 3D path such that the path is defined by a 2D path and a constant value of Z.

You can probably extend this to include more exciting paths in 3D, but it does not directly support animation of a 3D point over any 3D path.

The Point3DAnimationUsingPath Class

public class Point3DAnimationUsingPath : AnimationTimeline

{

    #region AnimationTimeline abstract overrides

 

    public override Type TargetPropertyType

    {

        get { return typeof (Point3D); }

    }

 

    protected override Freezable CreateInstanceCore()

    {

        return new Point3DAnimationUsingPath { Z = this.Z };

    }

 

    #endregion

 

    #region AnimationTimeline virtual overrides

 

    public override object GetCurrentValue(

        object defaultOriginValue, object defaultDestinationValue,

        AnimationClock animationClock)

    {

        PathGeometry path = this.PathGeometry;

 

        Point point;

        Point tangent;

 

        path.GetPointAtFractionLength(

            animationClock.CurrentProgress.Value, out point, out tangent);

 

        return new Point3D(point.X, point.Y, Z);

    }

 

    #endregion

 

    public double Z { get; set; }

 

    public PathGeometry PathGeometry

    {

        get { return (PathGeometry)GetValue(PathGeometryProperty); }

        set { SetValue(PathGeometryProperty, value); }

    }

 

    public static readonly DependencyProperty PathGeometryProperty =

        DependencyProperty.Register(

            "PathGeometry",

            typeof(PathGeometry),

            typeof(Point3DAnimationUsingPath));

}

Point3DAnimationUsingPath has two public properties, a 2D PathGeometry and a double called Z. These two define a coplanar path in 3D space and need to be set on objects of this type before starting an animation.

As a custom animation class, Point3DAnimationUsingPath must also derive from AnimationTimeline and override the abstract CreateInstanceCore method and TargetPropertyType property.

As you can see, TargetPropertyType returns the type of Point3D indicating that the target of the animation created by this class will be a property of type Point3D. CreateInstanceCore simply returns a clone of this class.

The magic of this class happens in the GetCurrentValue method which is an override of a virtual method. It’s actually quite simple. I delegate the animation task to the 2D path, and return a 3D point with the Z value set to the Z property.

In the next post I will provide an application that uses Point3DAnimationUsingPath.

Instrumentation Tip for ASMX Web Services

This post describes a simple way to trace all method invocations on an ASMX Web Service. The source code for the demo can be downloaded here.

Problem

I am currently adding some features to a customer’s application that exposes its core functionality as an ASMX Web Service.

About every mistake in the book was made in the design of this product, so I was having a rough time understanding the API of the service, never mind the implementation. For a start, the methods are not strongly typed, mostly accepting strings as parameters, and there are over 20 of them! Oh, and some of the API functions have as many as 15 parameters!

In order to understand the API I decided to trace each of the method invocations by the client (which seems to know how to consume the service). For each call I will trace the name of the method and the values of each of its parameters.

As I couldnt stand the humiliation of writing a macro to create tens of lines of verbose tracing code in the service, I looked for a more elegant way of achieving this goal.

Solution 1: Migrate to WCF

The first solution that came to mind was to upgrade the ASMX service to WCF with a basicHttpBinding. Then, use the extensive tracing capabilities that come with WCF with just a few lines in the configuration file.

Upgrading to WCF is easy and I have every intention of doing this eventually, but if you are familiar with WCF tracing you might agree that this alone was not a compelling reason to do so now. From my experience, WCF tracing offers too much information for simple tasks and, what bothers me most is that in order to make constructive use of the information it generates you need to use a dedicated tool – SvcTraceViewer.

Solution 2: Soap Extensions

For ASMX Web Services, ASP.NET allows you to intercept SOAP messages on the server. You can catch incoming messages (operation invocations) just after they are deserialized and outgoing messages (operation return values) just before they are serialized.

This solution has two pieces:

  1. Define a class that derives from SoapExtension (named MethodTraceExtension). In the ProcessMessage method, handle incoming messages only. Extract the name and parameters of the method being invoked and trace this information.
  2. Configure the application (in web.config) to inject MethodTraceExtension on every web method.

In piece two, I will demonstrate three different ways to extract the information from the SoapMessage. You only need one way - the other two are for fun. For the tracing I will be using the tip from my last post.

Implementation

This is the MethodTraceExtension class. You can compile it and place it in a class library or you can place the source file in the App_Code folder:

The MethodTraceExtension class

namespace MethodTrace

{

    public class MethodTraceExtension : SoapExtension

    {

        #region Dont Care Overrides

 

        public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attr)

        {

            return attr;

        }

 

        public override object GetInitializer(Type obj)

        {

            return obj;

        }

 

        public override void Initialize(object initializer)

        {

        }

 

        #endregion

 

        public override void ProcessMessage(SoapMessage message)

        {

            // On the server side, this condition is true when processing an incoming message

 

            if (message.Stage == SoapMessageStage.AfterDeserialize)

            {

                Trace("Using Message Members", TraceFromMessageMembers(message));

 

                //Trace("Using Linq over SOAP", TraceFromSoapWithLinq(message));

                //Trace("Using DOM with XPath", TraceFromSoapWithXPath(message));

            }

        }

 

        private void Trace(string category, string message)

        {

            HttpContext.Current.Trace.Write (category, message);

        }

 

        private string TraceFromMessageMembers(SoapMessage message)

        {

            LogicalMethodInfo method = message.MethodInfo;

            StringBuilder sb = new StringBuilder();

 

            sb.AppendFormat("{0} (", method.Name);

 

            int paramCount = method.InParameters.Length;

 

            for (int i = 0; i < paramCount; i++)

            {

                sb.Append(method.InParameters[i].Name);

                sb.Append(" = ");

                sb.Append(message.GetInParameterValue(i).ToString());

 

                if (i != paramCount - 1)

                {

                    sb.Append(", ");

                }

            }

 

            sb.Append(")");

 

            return sb.ToString();

        }

    }

}

As you can see, its only the ProcessMessage function that requires a non-trivial implementation.

The TraceFromMessageMembers uses SoapMessage members to extract the name of the method and the values of its parameters and returns the information as a string. The Trace function uses HttpContext.Current.Trace.Write as described in the previous post.

Configuring the Soap Extension

If you want to apply the SoapExtension to methods selectively, you can use a SoapExtensionAttribute as follows:

Define an attribute that derives from SoapExtensionAttribute, and override its ExtensionType property to return typeof (MethodTraceExtension). Then apply this attribute to any method on which you would like to apply the soap extension.

But, in my case, I want to apply the soap extension to all methods – and, you will remember, I wanted to avoid writing repetetive code for no good reason, even it its just an attribute : )

So I applied the other option – configuration through web.config:

  <system.web>

    . . .

    <webServices>

      <soapExtensionTypes>

        <add type="MethodTrace.MethodTraceExtension, App_Code"/>

      </soapExtensionTypes>

    </webServices> 

    . . .

  </system.web>

This will apply the MethodTraceExtension to every Web Method in the service.

Dont forget to configure tracing as recommended in the previous post.

Thats it.

The client in the source code calls each method of the service. Every call is now traced to the Traces.txt file on the server.

In the rest of this post, I just want to show you two other methods you can use to extract the information we need from an incoming SoapMessage. They both rely on knowledge of the SOAP schema, which can change in subtle ways if a few attributes are changed on the service, so I would not recommend using them instead of the one above.

Still, I included them here for fun. One uses LINQ to XML, and the other use DOM and XPath to extract the information we need from the incoming SOAP message.

Other Methods to Analyze the Soap Message

This method extracts the information using LINQ to XML.

        private string TraceFromSoapWithLinq(SoapMessage message)

        {

            message.Stream.Position = 0;

 

            XmlReader reader = XmlReader.Create(message.Stream);

            XDocument doc = XDocument.Load(reader);

 

            var body =

                (from element in doc.Root.Elements()

                 where element.Name.LocalName == "Body"

                 select element).Single();

 

            StringBuilder sb = new StringBuilder();

 

            XElement methodElement = body.Elements().Single();

 

            sb.AppendFormat("{0} (", methodElement.Name.LocalName);

 

            var parameters =

                (from element in methodElement.Descendants()

                 select new {

                     Name = element.Name.LocalName,

                     Value = element.Value }).ToArray();

 

            foreach (var p in parameters)

            {

                sb.Append(p.Name);

                sb.Append(" = ");

                sb.Append(p.Value);

 

                if (p != parameters.Last())

                {

                    sb.Append(", ");

                }

            }

 

            sb.Append(")");

 

            return sb.ToString();

        }

and this method extracts the information using the DOM model and XPath.

        private string TraceFromSoapWithXPath(SoapMessage message)

        {

            message.Stream.Position = 0;

 

            XmlReader reader = XmlReader.Create(message.Stream);

            XmlDocument doc = new XmlDocument();

            doc.Load(reader);

 

            XmlNamespaceManager nsm = new XmlNamespaceManager(doc.NameTable);

            nsm.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");

 

            XmlNode methodNode = doc.SelectNodes("// soap:Body/*", nsm)[0];

 

            StringBuilder sb = new StringBuilder();

 

            sb.AppendFormat("{0} (", methodNode.Name);

 

            XmlNodeList nodes = methodNode.ChildNodes;

 

            int paramCount = nodes.Count;

 

            for (int i = 0; i < paramCount; i++)

            {

                XmlNode node = nodes[i];

 

                sb.Append(node.Name);

                sb.Append(" = ");

                sb.Append(node.InnerText);

 

                if (i != paramCount - 1)

                {

                    sb.Append(", ");

                }

            }

 

            sb.Append(")");

            return sb.ToString();

        }

    }

Summary

In this post I demonstrated how to use a SoapExtension to trace every invocation of an ASMX Web Service in a centralized way.

I think that with this tool I will now find it easier to fathom the API of the service. I hope you find it useful too.

Instrumentation Tip for ASP.NET

The System.Diagnostics namespace provides a simple and extensible model for tracing. This post is about leveraging that model in ASP.NET (Web Applications, ASMX Web Services, Http Modules and Http Handlers).

Problem

The System.Diagnostics classes, Trace and Debug, simplify instrumentation by placing it's configuration outside your code, in one centralized place. That place can be an initialization method or a configuration file. Use of a configuration file allows you to change instrumentation behavior after deployment.

Moreover, the TraceListener model implemented by these classes allows you to extend instrumentation behavior by injecting your own custom listeners into the instrumentation infrastructure. Though you do have to implement custom listeners in code, you still have the choice of injecting them in initialization code or using a configuration file.

ASP.NET has an additional model for instrumentation. The key to this model is the System.Web.TraceContext class, which is widely documented, in particularly on MSDN.

The TraceContext class traces ASP.NET activity in detail and is controlled by the trace element in <system.web> in your configuration file.

You can add your own traces to its output using its Warn and Write methods. But with this model, you have limited control over where traces go and at first glance it doesn’t allow you to extend the model with your own custom listeners.

But, actually, it does. In fact you have two options:

  1. Use System.Diagnostics.Trace as you would in a Windows client application.
  2. Use System.Web.TraceContext with System.Diagnostics.Trace

Solution 1: Use System.Diagnostics

Add the system.diagnostics element in the Web.config for your ASP.NET application. Here is what I use to send my traces to a file called Traces.txt. In this case, I am using a built-in listener class, but you can replace it with a custom listener in exactly the same way as you would in a Windows client application.

  <system.diagnostics>

    <trace autoflush="true">

      <listeners>

        <clear/>

        <add name="TraceToFile"

             type="System.Diagnostics.TextWriterTraceListener"

             initializeData="Traces.txt"/>

      </listeners>

    </trace>

  </system.diagnostics>

In your code, you can now make calls to:

System.Diagnostics.Trace.Write("");

and your traces will go to the Traces.txt file.

This works regardless of how you configured TraceContext through the <system.web> configuration element.

Oh, one other thing.

For this to work, just like in a Windows client application you have to enable the TRACE compiler option. In ASP.NET applications you do this in the configuration file through the compileOptions attribute of the compiler element:

Here is the relevant snippet from my web.config:

  <system.codedom>

    <compilers>

      <compiler language="c#;cs;csharp" extension=".cs" warningLevel="4"

                type="Microsoft.CSharp.CSharpCodeProvider,

                      System,

                      Version=2.0.0.0, Culture=neutral,

                      PublicKeyToken=b77a5c561934e089"

                compilerOptions="/d:TRACE">

        <providerOption name="CompilerVersion" value="v3.5"/>

        . . .

      </compiler>

    </compilers>

  </system.codedom>

Solution 2: Use System.Web.TraceContext with System.Diagnostics

The first solution allows you to control your own traces – but doesnt give you any of the trace information from ASP.NET. Tracing of that information is still controlled by the trace element in <system.web>, and, if, enabled will go wherever TraceContext information goes.

This solution allows you to direct ASP.NET traces from TraceContext to the System.Diagnostics.Trace infrastructure. This way, your configuration in the system.diagnostics will handle your traces and those from ASP.NET.

For this option, implement Option 1 and, in addition, add the following in <system.web>

  <system.web>

    . . .

    <trace enabled="true" writeToDiagnosticsTrace="true"/>

    . . .

  </system.web>

Now, in your code, don’t use:

System.Diagnostics.Trace.Write("");

Use one of these instead:

Context.Trace.Write("");                // for an asmx WebService

Page.Trace.Write("");                   // for an aspx Page
HttpContext.Current.Trace.Write("");    // for an IHttpHandler or IHttpModule

If you use the configuration file snippets from this post, you will get all instrumentation information from ASP.NET and your own instrumentation calls directed to a file called Traces.txt.

You are back in control again : )

WCF Transactions – Barebones Demo – Part 4

In this post we will run the demo developed in the previous posts in the series and analyze the results. The last sections contain conclusions and suggestions for further reading.

The other posts in this series are:

WCF Transactions – Barebones Demo – Overview

WCF Transactions – Barebones Demo – Part 1 (Tools)

WCF Transactions – Barebones Demo – Part 2 (Service Code)

WCF Transactions – Barebones Demo – Part 3 (Client Code)

You can download the source code for the series from here.

Run the Demo (transactionFlow = true)

You will recall that in the configuration files of our projects, we have configured the client and service bindings with the setting transactionFlow = true. This, on a transaction-aware binding such as wsHttpBinding will allow a client transaction to flow from the client to the service.

To see that this is the case, select the Server project as the StartUp Project and press CTL+F5 to Run. Now select the Client project as the StartUp Project and press F5.

Client Side

On the Client console you should see this:

image 

The first section of prints shows us the result of the Run with a local instance of the VolatileResourceManager.

The first Set operation was executed outside the scope of a transaction. (“Set without Transaction”). Then, the code enters the scope of TransactionScope. Both before and after calling Set a second time, the transaction is a local transaction as all resources participating in the transaction are local.

The deliberate exception causes the transaction to abort, a rollback to occur, and, as expected, Get returns the first value that was set and not the second.

The section of prints shows us the result of the Run that uses the WCF service.

We dont see the prints from the VolatileResourceManager because these occur on the service side. But we do see that the transaction initiated on the client begins as a local transaction and after the call to the service is promoted to be a distributed transaction. This is a good indication that the transaction has been propagated across the service boundary and therefore needed to be promoted.

Finally, the deliberate exception on the client side causes a rollback to occur in the service and here also, Get returns the first value that was set and not the second.

Service Side

Let’s take a look at the prints on the service console:

image

The hosting code in the main of the Service project first prints out the addresses of the endpoints which it is listening on.

Then a Local Transaction begins. This actually occurs during the first call to Set, which was not part of a client transaction. The reason that this occurs is because the Set method has the following decoration:

[OperationBehavior(TransactionScopeRequired = true)]

This setting tells WCF that the implementation of this method must run in the context of a transaction. If the client is transactional and all other settings allow that transaction to flow to the service for this method, then joining the client’s transaction will meet this requirement. But, if the client is not running in a transaction, or it is, but the transaction does not flow to the service for some reason, the service will create its own local transaction for the method. In this case, the client has no transaction, so the service has created its own local transaction.

The local transaction commits at the end of the first Set() because by default, OperationBehavior.TransactionAutoComplete is true.

The next print occurs when the second Set is called in the client code. In this case, the client is executing in the scope of a transaction and all the conditions have been setup to allow the transaction to flow to the service.

We therefore see a distributed transaction on the service side. Note also, that the identifier of this transaction is the same as the one we see on the client after the call returns. Well, that’s really just what we wanted. We wanted the service to join the client’s transaction.

Indeed, this is what happens, and when the client’s transaction aborts, we see a Rollback on the service.

Run the Demo (transactionFlow = false)

Now let’s make a small change.

In the app.config of the server project set transactionFlow to false in the binding settings.

Now run the server using CTRL + F5 and update the service reference on the client side. Just to make sure, verify that transactionFlow is set to false in the client’s app.config too.

Now run the client.

Client Side

This is what you should see on the client console:

image

As you can see, nothing has changed (except for the identifier values) in the results of the Run that uses the local resource manager.

However, when using the service, we can see that the client transaction was not propagated to the service. This can be seen in two ways:

  1. Even after the call to the service, the transaction remains local (with the same identifier that it started with). This shows that the call to the service did not cause a promotion of the transaction to a distributed transaction.
  2. The result stored in the service object is the second result. (“Set 2”). This shows that despite the abort of the transaction on the client, the service operation did not rollback.

Service Side

Now let’s take a look at the service side:

image

Here you see two local transactions that commit. The first should be no surprise and occurs for the same reason discussed above when transactionFlow was true on the bindings.

The second transaction however, is now also a local transaction. This is because no transaction flowed in from the client so the service created its own local transaction. This transaction auto completed when the method returned and the second setting was commited.

Conclusion

Here are some conclusions from this demo:

  1. TransactionScopeRequired (of the OperationalBehavior attribute)
    When this setting is true, the method must run in the context of a transaction on the server. If the client transaction was allowed to flow across the service boundary, WCF will join that transaction, if not, a local transaction will be created (and autocompleted when the method completes).
  2. TransactionFlow (of some of the Binding classes)
    This is a property of the TCP, IPC and WS bindings. When true (the default is false), these bindings will enable a client transaction, if one exists, to flow to the service.
  3. TransactionFlowOption (of the TransactionFlow attribute, set on an operation at the contract level)
    In this example, I set this property to the value of TransactionFlowOption.Allowed. I did this so I could experiment with transactionFlow both true and false. Were this value TransactionFlowOption.Mandatory, an exception would be thrown if transactionFlow were false and were the value TransactionFlowOption.NotAllowed, an exception would be thrown if transactionFlow were true.

Further Reading and Experimentation

The three properties that were discussed here (see Conclusion) can be configured in a number of different ways. Some have no meaning, but 4 particular combinations do.

See this link (section 7.6.2) for a description of these modes.

Now that you have the barebones demo up and running, you can experiment with each of these and verify they work as expected.

Enjoy!

WCF Transactions – Barebones Demo – Part 3

In this post we will develop the client side of a WCF service that is capable of participating in the client transaction.

Other posts in this series are:

WCF Transactions – Barebones Demo – Overview

WCF Transactions – Barebones Demo – Part 1 (Tools)

WCF Transactions – Barebones Demo – Part 2 (Service Code)

WCF Transactions – Barebones Demo – Part 4 (Analysis)

You can download the source code for the series from here.

In Parts 1 and 2 we implemented some tracing tools and the service code. Here we implement the client code and in the next and final post in the series we will run the demo and analyze the results.

The Client Code

Well, first I used Add Service Reference option to create a proxy for the service. (The service configuration file specifies that mex is supported). The proxy is automatically named TextContainerClient.

That done, lets think about what we want to accomplish.

I would like to the test affect of accessing a resource manager within a transaction that aborts. First, I would like to see how that works with a resource manager that is in process, then I would like to see what happens when the resource manager is accessed via a WCF service. Both, of course, are expected to rollback on any changes made within the client transaction.

In order to run both options without duplicating too much code, I used the Adapter Design Pattern defining the following Adapter interface in the Client project:

namespace TransactionalClient

{

    interface ITextContainer

    {

        string Get();

        void Set(string s);

    }

}

I implemented this code twice, once for the a local resource manager and a second time for the resource manager sitting behind the service.

Here is the local resource manager implementation:

namespace TransactionalClient

{

    class LocalTextContainer : ITextContainer

    {

        VolatileResourceManager<string> rm;

 

        public LocalTextContainer()

        {

            rm = VolatileResourceManager<string>.Instance;

        }

 

        #region ITextContainer Members

 

        public string Get()

        {

            return rm.Get() as string;

        }

 

        public void Set(string s)

        {

            rm.Set(s);

        }

 

        #endregion

    }

}

In this case the Adaptee is the VolatileResourceManager.

For the service implementation, the Adaptee is the TextContainerClient service proxy.

namespace TransactionalClient

{

    class ServiceTextContainer : ITextContainer

    {

        TextContainerClient tc;

 

        public ServiceTextContainer()

        {

            tc = new TextContainerClient();

        }

 

        #region ITextContainer Members

 

        public string Get()

        {

            return tc.Get() as string;

        }

 

        public void Set(string s)

        {

            tc.Set(s);

        }

 

        #endregion

    }

}

Now for the business logic that works with the ITextContainer interface:

namespace TransactionalClient

{

    class Program

    {

        void Run<T>() where T : ITextContainer, new()

        {

            T t = new T();

 

            t.Set("Set 1");

 

            try

            {

                using (TransactionScope ts = new TransactionScope())

                {

                    Console.WriteLine("Before call to service");

 

                    Transaction.Current.Log();

 

                    t.Set("Set 2");

 

                    Console.WriteLine("After call to service");

 

                    Transaction.Current.Log();

 

                    throw new Exception("Deliberate exception");

 

                    ts.Complete();

                }

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

            }

 

            string result = t.Get();

            Console.WriteLine("Result = {0}", result);

        }

    }

}

The generic Run method creates a new ITextContainer object and calls Set non-transactionally with the value “Set 1”. This call will take affect regardless of any unhandled exceptions thrown later on.

Then, within the scope of a transaction, Run attempts to call Set with “Set 2”. Immediately afterwards the client throws an exception which prevents the transaction from completing - instead it aborts.

Twice in the code we use the Log() extension method added to the Transaction class to display information about the current transaction.

The last line prints the value stored the ITextContainer object after leaving the transaction scope.

The expected behavior of this method is, of course, that the “Set 2” should be rolled back and “Set 1” should be printed on the screen.

Finally, to complete the picture, here is the Main:

namespace TransactionalClient

{

    class Program

    {

        . . .

 

        void Run()

        {

            Console.WriteLine("\n===================== Local =======================\n ");

            Run<LocalTextContainer>();

 

            Console.WriteLine("\n==================== Service ======================\n ");

            Run<ServiceTextContainer>();

        }

 

        static void Main(string[] args)

        {

            new Program().Run();

            Console.ReadLine();

        }

    }

The Main executes the transaction code in Run<> twice, once against a local resource manager and then against the service wrapper.

The Client Configuration File

The client side looks very much like the service side.

Note the use of the wsHttpBinding that supports transaction flow and the explicit setting of transactionFlow = true to allow transactions to flow through the binding to the service.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <client>

      <endpoint address="http://localhost/Services/TextContainer"

                binding="wsHttpBinding"

                bindingConfiguration="TransactionalBindingConfiguration"

                contract="ServiceReferences.ITextContainer" />

    </client>

    <bindings>

      <wsHttpBinding>

        <binding name="TransactionalBindingConfiguration" transactionFlow="true">

          <security>

            <transport realm="" />

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

  </system.serviceModel>

</configuration>

Ok. We are done writing code.

In the next post we will run the demo,  observe and analyze the results and consider some configuration changes and their meanings.

WCF Transactions – Barebones Demo – Part 2

In this post we will develop a WCF service that is capable of participating in a client transaction.

The other posts in this series are:

WCF Transactions – Barebones Demo – Overview

WCF Transactions – Barebones Demo – Part 1 (Tools)

WCF Transactions – Barebones Demo – Part 3 (Client Code)

WCF Transactions – Barebones Demo – Part 4 (Analysis)

You can download the source code for the series from here.

The Service Contract

The contract, implementation and hosting code for the service are defined in the Server project in the source code. This is the contract:

namespace TransactionalService

{

    [ServiceContract(SessionMode=SessionMode.NotAllowed)]

    public interface ITextContainer

    {

        [OperationContract]

        [TransactionFlow(TransactionFlowOption.Allowed)]

        void Set(string text);

 

        [OperationContract]

        string Get();

    }

}

The interface itself is self-explanatory, but the WCF attributes deserve some attention.

First, note that the ServiceContract attribute explicitly forbids sessions. Truth be told, this isnt absolutely necessary, because by default the service wont exhibit sessionful behavior when propagating transactions to WCF service methods. In fact, the reason I explicitly disallowed sessions in this contract is to remind us that sessions are not the recommended practice for transactional WCF services.

The reason for this is as follows. A service method that votes to complete a transaction is expected to leave the service instance in a consistent state and to support a rollback to the previous consistent state if the transaction aborts. For this to happen, any changes made in instance state during the call must be stored in a resource manager that enlists to the current transaction. You are not supposed to have session state stored in the service instance, because all such state should be stored in a resource manager and retrieved whenever needed.

TransactionAutoComplete = true is the default for the OperationalBehavior attribute, so unless an unhandled exception is thrown from a service method, a WCF method that joins a transaction votes to complete it when the method returns.

So, by default, WCF will release the service instance upon such a method. This will happen even if you explicitly demand session mode by setting SessionMode = SessionMode.Required on the ServiceContract attribute on the contract and InstanceContextMode to PerSession in the ServiceBehavior attribute on the service implementation. In fact, even if you set InstanceContextMode to Single in the ServiceBehavior attribute for singleton behavior, the single instance will also be released at the end of an auto completing transactional method of the Service. In such case the only difference between the single and per call instance mode is that in single mode there can only be one instance of the service object at any time.

You can change this behavior by setting the ReleaseServiceInstanceOnTransactionComplete property to true on the ServiceBehavior attribute. You might want to do this, if, apart from the transactional resources that your service accesses you are also doing non-transactional work (for instance logging) that you do not want to roll back even if a transaction aborts.

The second attribute to note is TransactionFlowAttribute on the method Set(). The setting TransactionFlow.Allowed indicates that if the client is calling this method within a transaction, that transaction should be propagated to the Service (but if not, the method may be called without a client transaction).

The Service Implementation

This is the implementation of the service:

namespace TransactionalService

{

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]

    public class TextContainer : ITextContainer

    {

        VolatileResourceManager<string> rm;

 

        public TextContainer()

        {

            rm = VolatileResourceManager<string>.Instance;

        }

 

        #region ITextContainer Members

 

        [OperationBehavior(TransactionScopeRequired = true)]

        public void Set(string text)

        {

            rm.Set(text);

        }

 

        public string Get()

        {

            return rm.Get() as string;

        }

 

        #endregion

    }

}

Equipped with the VolatileResourceManager class, the implementation is again self-explanatory. Here also, attention is drawn to the WCF attributes.

First, the InstanceContextMode is set to PerCall, surrendering to the WCF philosophy described above that transaction methods should not be sessionful. For this reason, I implemented VolatileResourceManager as a singleton, so that the Set and Get methods would still access the same resource.

If you do need sessionful behavior such that each client holds its own transactional text entry, you have two options. One is to add a session id to the contract and have the client store the id and pass it to every method on the service. The resource manager might then provide a dictionary-like interface allowing access to a particular transactional object stored for that session id.

Another option is to store the id in the service instance and set ReleaseServiceInstanceOnTransactionComplete to true as described above. This prevents the instance from being released after a call to Set and the id will be persisted in the service instance for the next time on of its methods are called.

The second attribute is OperationalBehavior - with the TransactionScopeRequired property set to true.
This setting simply means that the Set method must execute in the scope of a transaction. If transaction propagation is enabled and a client is calling the service in the context of a client-side transaction, the method will join the client transaction (promoting the transaction from local to distributed if it was a local transaction to begin with). If there is no transaction to join, the service will create its own local transaction to run in, and this will terminate no later than when the method completes.

The Service Configuration File

The only points to note in the configuration file are that:

  1. In order to propagate transactions from the client to the service you need to choose a binding that supports transaction flow (only TCP, IPC and WS bindings are so “transaction aware”).
  2. You need to set the transactionFlow property of the binding to true.

Here is the complete configuration file:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service behaviorConfiguration="TransactionalServiceBehavior"

        name="TransactionalService.TextContainer">

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost/Services" />

          </baseAddresses>

        </host>

        <endpoint address="TextContainer"

                  binding="wsHttpBinding"

                  bindingConfiguration="TransactionalBindingConfiguration"

                  contract="TransactionalService.ITextContainer" />

        <endpoint address="mex"

                  binding="mexHttpBinding"

                  contract="IMetadataExchange" />

      </service>

    </services>

    <bindings>

      <wsHttpBinding>

        <binding name="TransactionalBindingConfiguration"

                 transactionFlow="true">

        </binding>

      </wsHttpBinding>

    </bindings>

    <behaviors>

      <serviceBehaviors>

        <behavior name="TransactionalServiceBehavior">

          <serviceMetadata/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

In the next post we will take a look at the client code.

WCF Transactions – Barebones Demo - Part 1

In this post we will prepare two tools that will help us trace WCF’s transaction support in our demo. Both tools can be found in the Common class library in the source code.

The other posts in this series are:

WCF Transactions – Barebones Demo – Overview (Tools)

WCF Transactions – Barebones Demo – Part 2 (Service Code)

WCF Transactions – Barebones Demo – Part 3 (Client Code)

WCF Transactions – Barebones Demo – Part 4 (Analysis)

You can download the source code for the series from here.

The first tool is an implementation IEnlistmentNotification - our own Resource Manager. As mentioned in the overview this class is far from complete. For a start, it doesnt support isolation between transactions. But in this demo we will assume that there is at most only one transaction trying to access the resource.

    public class VolatileResourceManager<T> : IEnlistmentNotification

    {

        #region Private

 

        private T oldValue = default(T);

        private T newValue = default(T);

        private T currValue = default(T);

 

        #endregion

 

        #region Singleton

 

        private static VolatileResourceManager<T> instance =

            new VolatileResourceManager<T>();

 

        private VolatileResourceManager() {}

 

        public static VolatileResourceManager<T> Instance

        {

            get { return instance; }

        }

 

        #endregion

 

        #region Public

 

        public T Get()

        {

            return this.currValue;

        }

 

        public void Set(T value)

        {

            Transaction transaction = Transaction.Current;

            if (transaction == null)

            {

                Console.Write("Set without transaction");

                this.currValue = value;

            }

            else

            {

                transaction.Log();

 

                Console.Write("Set with Transaction with EnlistVolatile");

 

                transaction.EnlistVolatile(this, EnlistmentOptions.None);

               

                oldValue = this.currValue;

                newValue = value;

            }

        }

 

        #endregion

 

        #region IEnlistmentNotification Members

 

        public void Commit(Enlistment enlistment)

        {

            Console.Write("Commit");

            oldValue = default(T);

        }

 

        public void InDoubt(Enlistment enlistment)

        {

            Console.Write("InDoubt");

        }

 

        public void Prepare(PreparingEnlistment preparingEnlistment)

        {

            Console.Write("Prepare");

            this.currValue = newValue;

            preparingEnlistment.Prepared();

            // or

            //preparingEnlistment.ForceRollback();

        }

 

        public void Rollback(Enlistment enlistment)

        {

            Console.Write("Rollback");

 

            this.currValue = oldValue;

            oldValue = default (T);

        }

 

        #endregion

    }

The implementation of IEnlistmentNotification is fairly simple, using the two private variables oldValue and newValue to manage state until either Rollback or Commit determine the final value for the transaction.

The Set method is the interesting one.

Here, we check whether there is an ambient transaction in progress. If not, we just set the currValue and return. If there is, we update newValue and remember oldValue in preparation for the two phase commit protocol.

The call to transaction.Log() will print on the console whether the transaction is a local or distributed transaction and also print its identifier. This will be useful when we want to verify that a transaction initiated by a client has been propagated over a service boundary.

The Log() method is the second tool in the Common class library. It is an extension method for the Transaction class defined as follows:

    public static class TransactionExtensions

    {

        public static void Log(this Transaction transaction)

        {

            TransactionInformation info = transaction.TransactionInformation;

 

            if (info.DistributedIdentifier == Guid.Empty)

            {

                Console.WriteLine ("{0} :", Process.GetCurrentProcess().ProcessName);

                Console.WriteLine("   Local Transaction " + info.LocalIdentifier);

            }

            else

            {

                Console.WriteLine("Distributed Transaction " + info.DistributedIdentifier);

            }

        }

    }

You may have noticed that I defined the VolatileResourceManager as a singleton. The reason for that is related to the way WCF handles service instances that take part in transactions.

More on that in Part 2.

WCF Transactions – Barebones Demo – Overview

In the next four posts, we will be building a barebones project to demonstrate WCF’s support for Transactions.

The other posts in this series are:

WCF Transactions – Barebones Demo – Part 1 (Tools)

WCF Transactions – Barebones Demo – Part 2 (Service Code)

WCF Transactions – Barebones Demo – Part 3 (Client Code)

WCF Transactions – Barebones Demo – Part 4 (Analysis)

You can download the source code for the series from here.

The demo focuses on the usage of these properties:

  1. TransactionScopeRequired (of the OperationalBehavior attribute)
  2. TransactionFlow (of some of the Binding classes)
  3. TransactionFlowOption (of the TransactionFlow attribute)

Hopefully, this demo will get you up and running and you can then easily use it to test other properties and behaviors that are not shown.

To operate the demo, you will not be needing an SQL server or any other resource manager, because we will build one ourselves. (A resource manager is the name given to objects that can enlist in transactions to commit and rollback changes made to a resource).

For a more in depth discussion of Volatile Resource Managers and a guide to implementing transactional types I recommend reading Volatile Resource Managers in .NET Bring Transactions to the Common Type by Juval Lowy.

In the next post I will introduce the VolatileResourceManager which, is what’s its name suggests. It implements the IEnlistmentNotification interface. It is simpler and has less functionality than Transaction<T> described in Juval’s article. However, it is sufficient for our purposes. I said barebones, right ? : )

As we have the source code for the VolatileResourceManager, we can easily see the commits and rollbacks (or lack thereof) that are generated by transactional (or non-transactional) code that uses it.

We will also want to see whether a transaction in which the VolatileResourceManager participates is a local or distributed transaction, and whether the transaction flowed from the client or was created locally within the service. To assist with this, I also implement an extension function to the Transaction class that displays this information about a transaction.

So far, no WCF.

In the third and fourth posts in this series, I will implement the Service and Client code respectively. Also in the fourth post, we will write code to use the VolatileResourceManager in a transactional operation that rolls back. The code will do so once against a local implementation of the class and a second time across a WCF service boundary.

This is where we will be able to observe how transactions flow (or not) from a WCF client to a WCF service.

The fifth post runs the demo and discusses the results.

WCF Security Scenarios – Barebones – Part 4

This post presents a barebones yet complete demonstration of security policies suitable for the Internet scenario.

Other posts in this series:

WCF Security Scenarios – Barebones (Overview)

WCF Security Scenarios – Barebones - Part 1 (Intranet)

WCF Security Scenarios – Barebones - Part 2 (Anonymous)

WCF Security Scenarios – Barebones - Part 3 (Business-to-Business)

Source code for all scenarios can be downloaded here.

Scenario: Internet

This scenario occurs when you want to provide a service to authenticated users over the internet.

The key characteristics of this scenario are:

  1. Interoperability: Required, client or server may not be running Windows or WCF.
  2. Firewall: Communication is over the internet, crossing a firewall.
  3. Point-to-Point: Messages may be redirected over channels which are not securable.
  4. Client Identity: The service and client need to authenticate each other.
    The client is not a business so it does not have a certificate.

Security Policy

  1. We must use an http binding to allow traffic to cross a firewall.
  2. The security mode needs to be Message as oppose to Transport.
    Message protection requires more processing than Transport but is required because there is no guarantee that messages will not travel over insecure channels on their way between the client and server.
  3. The client will authenticate the server using the server’s X.509 certificate.
  4. The server will authenticate the client using a username and password which will be stored and managed in a dedicated database.

Setting up the SqlMembershipProvider and SqlRoleProvider

In this scenario client authentication will be implemented with a username and password.

It is not practical to give all users from the Internet a local account on our Windows machine (or on the domain). Instead we need to use another store implementing username/passwords and roles.

To assist us with this task, .Net provides the Membership and Roles APIs (in the System.Web.Security namespace), implements a provider pattern, allowing you to implement your own implementation of these APIs and provides a default implementation that uses a database in SQL Server. (Though the API and the elements in the configuration files for this model all use the System.Web namespace you can use them freely in any .Net application. You do have to reference, however the System.Web assembly).

In order to use the SQL providers you must first set them up using the aspnet_regsql.exe wizard. This setups a database (which is by default called aspnetdb) to host your application specific users and roles.

This demo assumes there two users in the system, one called Bill, the other Fred and that there is one Role in the system called Admin.  Only Bill is a member of Admin.

To set this up I have wrote a small project called SetupMembership (provided with the source code) that makes the following calls to the Membership/Role API:

namespace SetupMembership

{

    class Program

    {

        static void Main(string[] args)

        {

            try

            {

                string[] userNames = new string[] { "Bill", "Fred" };

                string roleAdmin = "Admin";

                Roles.DeleteRole("Admin", false);

                foreach (string user in userNames)

                {

                    Membership.DeleteUser(user, true);

                }

                Membership.CreateUser("Bill", "billbill$");

                Membership.CreateUser("Fred", "fredfred$");

                Roles.CreateRole(roleAdmin);

                Roles.AddUserToRole("Bill", roleAdmin);

                Console.WriteLine("Under application {0}", Membership.ApplicationName);

                Console.WriteLine("you now have two users, Bill and Fred");

                Console.WriteLine("and one role, Admin.");

                Console.WriteLine("Bill is a member of Admin");

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

            }

            Console.ReadLine();

        }

    }

}

This code is backed up in the SetupMembership app.config file with the following settings:

  <connectionStrings>

    <add name="AspNetDb"

         connectionString =

          "Data Source=localhost;

            Initial Catalog=aspnetdb;

            Integrated Security=true"/>

  </connectionStrings>

  <system.web>

    <membership defaultProvider="MySqlMembershipProvider">

      <providers>

        <add name="MySqlMembershipProvider"

          type="System.Web.Security.SqlMembershipProvider"

          connectionStringName="AspNetDb"

          requiresQuestionAndAnswer="false"

          requiresUniqueEmail="false"

          applicationName="WcfSecurityScenarios"/>

      </providers>

    </membership>

    <roleManager enabled="true"

                 defaultProvider="MySqlRoleManager">

      <providers>

        <add name="MySqlRoleManager"

           type="System.Web.Security.SqlRoleProvider"

           connectionStringName="AspNetDb"

          applicationName="WcfSecurityScenarios"

         />

      </providers>

    </roleManager>

  </system.web>

This is the explanation of the file from top to bottom.

The default provider to be used for the MemberShip API is one called MySqlMembershipProvider which is of type System.Web.Security.SqlMembershipProvider. Such an object should be instantiated and scoped to an the application named WcfSecurityScenarios (other applications won’t see the users of this application). It should use the AspNetDb connection string to connect to the data store.

The default provider to be used for the Roles API is one called MySqlRoleManager which is of type System.Web.Security.SqlRoleProvider. Such an object should be instantiated and scoped to an the application named WcfSecurityScenarios (other applications won’t see the users of this application). It should use the AspNetDb connection string to connect to the data store.

Now let’s look at how we will use the Membership and Roles.

Implementing Role-based Security

On the Server

On the server we bar access to non-Admin members like so:

namespace CalculatorService

{

    [ServiceBehavior]

    public class Calculator : ICalculator

    {

        #region ICalculator Members

        public double Add(double a, double b)

        {

            if (! Roles.IsUserInRole("Admin"))

            {

                throw new Exception(

                    "You are not authorized to perform this operation");

            }

            return a + b;

        }

        #endregion

    }

}

On the Client

On the client we present the username and password like so:

namespace Client

{

    class Program

    {

        void Run()

        {

            try

            {

                RunAs ("Bill", "billbill$");

                RunAs ("Fred", "fredfred$");

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

            }

        }

        private static void RunAs(string userName, string password)

        {

            Console.WriteLine("Try as " + userName);

            CalculatorClient calc = new CalculatorClient();

            calc.ClientCredentials.UserName.UserName = userName;

            calc.ClientCredentials.UserName.Password = password;

            try

            {

                double result = calc.Add(5, 6);

                Console.WriteLine("Result = {0}", result);

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

            }

        }

        static void Main(string[] args)

        {

            new Program().Run();

            Console.ReadLine();

        }

    }

}

Service Configuration File

Now lets look at the configuration file at the service side:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service name="CalculatorService.Calculator"

               behaviorConfiguration="InternetServiceBehavior">

        <endpoint address="Calculator"

                  binding="wsHttpBinding"

                  bindingConfiguration="InternetBindingConfiguration"

                  contract="CalculatorService.ICalculator" />

        <endpoint address="mex"

                  binding="mexHttpBinding"

                  contract="IMetadataExchange" />

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost/Services" />

          </baseAddresses>

        </host>

      </service>

    </services>

    <bindings>

      <wsHttpBinding>

        <binding name="InternetBindingConfiguration">

          <security mode="Message">

            <message clientCredentialType="UserName"/>

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

    <behaviors>

      <serviceBehaviors>

        <behavior name="InternetServiceBehavior">

          <serviceCredentials>

            <!--makecert -n CN=TestServiceCert -sr LocalMachine -ss My -sky exchange -pe-->

            <serviceCertificate

              findValue="TestServiceCert"

              storeLocation="LocalMachine"

              x509FindType="FindBySubjectName"

              storeName="My"/>

            <userNameAuthentication

              userNamePasswordValidationMode="MembershipProvider"/>

          </serviceCredentials>

          <serviceAuthorization

              principalPermissionMode="UseAspNetRoles"/>

          <serviceMetadata/>

          <serviceDebug includeExceptionDetailInFaults="true"/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

  <connectionStrings>

    <add name="AspNetDb"

         connectionString =

          "Data Source=localhost;

            Initial Catalog=aspnetdb;

            Integrated Security=true"/>

  </connectionStrings>

  <system.web>

    <membership defaultProvider="MySqlMembershipProvider">

      <providers>

        <add name="MySqlMembershipProvider"

          type="System.Web.Security.SqlMembershipProvider"

          connectionStringName="AspNetDb"

          requiresQuestionAndAnswer="false"

          requiresUniqueEmail="false"

          applicationName="WcfSecurityScenarios"/>

      </providers>

    </membership>

    <roleManager enabled="true"

                 defaultProvider="MySqlRoleManager">

      <providers>

        <add name="MySqlRoleManager"

           type="System.Web.Security.SqlRoleProvider"

           connectionStringName="AspNetDb"

          applicationName="WcfSecurityScenarios"

         />

      </providers>

    </roleManager>

  </system.web>

</configuration>

This is a long file, but we have seen most of the components already.

Reading from the top:

This application hosts a service called Calculator.Service at an end point whose address is http://localhost/Services/Calculator, uses the wsHttpBinding and exposes the CalculatorService.ICalculator contract.

The application also provides metadata on a mex endpoint (configured as an endpoint, and a serviceMetadata entry in the B2BServiceBehavior at the bottom of the file).

The configuration for the wsHttpBinding, called InternetBindingConfiguration will be implementing Message security. Clients will be authenticated using a username and password.

The service implements a behavior called “InternetServiceBehavior” which declares the service certificate we created earlier for the service as the “service credential”.

In addition, under serviceCredentials we indicate that the client username and password that will be presented for authentication will be validated against the Membership API  and that Roles will be validated against the Roles API.

Outside and following <serviceModel> you can see the configuration elements that setup the providers for Membership and Roles. These are identical to those described in the SetupMembership project above.

Client Configuration File

Here is the App.config for the client:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <client>

      <endpoint address="http://localhost/Services/Calculator"

                binding="wsHttpBinding"

                bindingConfiguration="InternetBindingConfiguration"

                behaviorConfiguration="InternetEndpointBehavior"

                contract="ServiceReferences.ICalculator">

        <identity>

          <dns value="TestServiceCert"/>

        </identity>

      </endpoint>

    </client>

    <bindings>

      <wsHttpBinding>

        <binding name="InternetBindingConfiguration">

          <security mode="Message">

            <message clientCredentialType="UserName"/>

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

    <behaviors>

      <endpointBehaviors>

        <behavior name="InternetEndpointBehavior">

          <clientCredentials>

            <serviceCertificate>

              <authentication certificateValidationMode="PeerTrust"/>

            </serviceCertificate>

          </clientCredentials>

        </behavior>

      </endpointBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

This application will instantiate proxies for the calculator service that will communicate across the endpoint whose address is http://localhost/Services/Calculator, that uses the wsHttpBinding and exposes the CalculatorService.ICalculator contract.

Note that the Endpoint has an identity element that indicates that the expected identity of the service is not ‘localhost’ (the default name is the url of the machine) but the name of the certificate.

The configuration for the wsHttpBinding, called InternetBindingConfiguration will be implementing Message security to protect messages that crosses the channel. A username and password will be presented as client credentials to the service.

The endpoint also has a behavior called InternetEndpointBehavior in which it specifies clientCredentials which specify how the client authenticates the server. It does so using a serviceCertificate which will be trusted if the certificate can be found in the Trusted People store of the Current User.

Summary

In this series of five posts we have reviewed recommended security policies for four scenarios of security: Intranet, Anonymous, Business-to-Business, Internet that are identified in Juval Lowy’s book Programming WCF Services.

Foreach I have shown a demo that is implemented almost entirely through two configuration files, one for the client and one for the service.

The source code for the demos can be found here.

WCF Security Scenarios – Barebones – Part 3

This post presents a barebones yet complete demonstration of security policies suitable for the Business-to-Business scenario.

Other posts in this series:

WCF Security Scenarios – Barebones (Overview)

WCF Security Scenarios – Barebones - Part 1 (Intranet)

WCF Security Scenarios – Barebones - Part 2 (Anonymous)

WCF Security Scenarios – Barebones - Part 4 (Internet)

Source code for all scenarios can be downloaded here.

Scenario: Business-to-Business

This scenario occurs when you want to setup communication between two companies over the internet.

The key characteristics of this scenario are:

  1. Interoperability: Required, client or server may not be running Windows or WCF.
  2. Firewall: Communication is over the internet, crossing a firewall.
  3. Point-to-Point: Messages may be redirected over channels which are not securable.
  4. Client Identity: The service and client need to authenticate each other. Business entities usually have certificates.

Security Policy

  1. We must use an http binding to allow traffic to cross a firewall.
  2. The security mode needs to be Message as oppose to Transport.
    Message protection requires more processing than Transport but is required because there is no guarantee that messages will not travel over insecure channels on their way between the client and server.
  3. The service and client will authenticate each other using X.509 certificates.

Preparing a Certificate For the Client

In the previous scenario we created a certificate for the server. For this scenario we need to do the same for the client using makecert on the client machine as follows:

makecert -ss My -sr CurrentUser -n CN=TestClientCert -sky exchange -pe

This means: Create a new certificate for a subject called “TestClientCert” and install it in the Personal (My) store of the LocalMachine. The new certificate will have a private key and can be used to encrypt symmetrical keys that will be used to encrypt messages.

Then copy the new certificate to the server machine (can be the same machine) and import into the Trusted People store of the Local Machine.

We now have a X.509 certificate representing the client that the client can retrieve from the Personal Store of the Current User and the server will find in the Trusted People store of the Local Machine.

Service Configuration File

Here is the App.config for the server.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service name="CalculatorService.Calculator"

               behaviorConfiguration="B2BServiceBehavior">

        <endpoint address="Calculator"

                  binding="wsHttpBinding"

                  bindingConfiguration="B2BBindingConfiguration"

                  contract="CalculatorService.ICalculator" />

        <endpoint address="mex"

                  binding="mexHttpBinding"

                  contract="IMetadataExchange" />

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost/Services" />

          </baseAddresses>

        </host>

      </service>

    </services>

    <bindings>

      <wsHttpBinding>

        <binding name="B2BBindingConfiguration">

          <security mode="Message">

            <message clientCredentialType="Certificate"/>

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

    <behaviors>

      <serviceBehaviors>

        <behavior name="B2BServiceBehavior">

          <serviceCredentials>

            <!--makecert -n CN=TestServiceCert -sr LocalMachine -ss My -sky exchange -pe-->

            <serviceCertificate

              findValue="TestServiceCert"

              storeLocation="LocalMachine"

              x509FindType="FindBySubjectName"

              storeName="My"/>

            <clientCertificate>

              <authentication certificateValidationMode="PeerTrust"/>

            </clientCertificate>

          </serviceCredentials>

          <serviceMetadata/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Reading from the top:

This application hosts a service called Calculator.Service at an end point whose address is http://localhost/Services/Calculator, uses the wsHttpBinding and exposes the CalculatorService.ICalculator contract.

The application also provides metadata on a mex endpoint (configured as an endpoint, and a serviceMetadata entry in the B2BServiceBehavior at the bottom of the file).

The configuration for the wsHttpBinding, called B2BBindingConfiguration will be implementing Message security. Clients will be authenticated using a certificate.

The service implements a behavior called “B2BServiceBehavior” which declares the service certificate we created earlier for the service as the “service credential”.

In addition, under serviceCredentials we indicate that a client certificate that will be presented for authentication will be validated using PeerTrust which meand that the certificate can be found in the Trusted People store of the Local Machine.

Client Configuration File

Here is the App.config for the client:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <client>

      <endpoint address="http://localhost/Services/Calculator"

                binding="wsHttpBinding"

                bindingConfiguration="B2BBindingConfiguration"

                behaviorConfiguration="B2BEndpointBehavior"

                contract="ServiceReferences.ICalculator">

        <identity>

          <dns value="TestServiceCert"/>

        </identity>

      </endpoint>

    </client>

    <bindings>

      <wsHttpBinding>

        <binding name="B2BBindingConfiguration">

          <security mode="Message">

            <message clientCredentialType="Certificate"/>

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

    <behaviors>

      <!--makecert -ss My -sr CurrentUser -n CN=TestClientCert -sky exchange -pe-->

      <endpointBehaviors>

        <behavior name="B2BEndpointBehavior">

          <clientCredentials>

            <serviceCertificate>

              <authentication certificateValidationMode="PeerTrust"/>

            </serviceCertificate>

            <clientCertificate

              findValue="TestClientCert"

              storeName="My"

              storeLocation="CurrentUser"

              x509FindType="FindBySubjectName"/>

          </clientCredentials>

        </behavior>

      </endpointBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

This application will instantiate proxies for the calculator service that will communicate across the endpoint whose address is http://localhost/Services/Calculator, that uses the wsHttpBinding and exposes the CalculatorService.ICalculator contract.

Note that the Endpoint has an identity element that indicates that the expected identity of the service is not ‘localhost’ (the default name is the url of the machine) but the name of the certificate.

The configuration for the wsHttpBinding, called B2BBindingConfiguration will be implementing Message security to protect all data that crosses the channel. A certificate will be presented as client credentials to the service.

The endpoint also has a behavior called B2BEndpointBehavior in which it specifies clientCredentials which specify how the client authenticates the server. It does so using a serviceCertificate which will be trusted if the certificate can be found in the Trusted People store of the Current User. It also specifies the clientCertificate that we created above in order to authenticate the client toward the service.

In the next post we will take a look at the Internet scenario. In that scenario, we will replace client authentication by certificate with authentication by a username password. We will also demonstrate role based authorization.

WCF Security Scenarios – Barebones - Part 2

This post presents a barebones yet complete demonstration of security policies suitable for the Anonymous scenario.

Other posts in this series:

WCF Security Scenarios – Barebones (Overview)

WCF Security Scenarios – Barebones - Part 1 (Intranet)

WCF Security Scenarios – Barebones - Part 3 (Business-to-Business)

WCF Security Scenarios – Barebones - Part 4 (Internet)

Source code for all scenarios can be downloaded here.

Scenario: Anonymous

This scenario occurs when you provide an internet-facing service but you don’t care who the consumer is. However, you do want clients to be able authenticate the service and to ensure integrity and confidentiality of the communication.

The key characteristics of this scenario are:

  1. Interoperability: Required, client may not be running Windows or WCF.
  2. Firewall: Communication is over the internet, crossing a firewall.
  3. Point-to-Point: Messages may be redirected over channels which are not securable.
  4. Client Identity: The service can ignore the client’s identity.
  5. Service Identity: Clients must be able to authenticate the server.

Security Policy

  1. We must use an http binding to allow traffic to cross a firewall.
  2. The security mode needs to be Message as oppose to Transport.
    Message protection requires more processing than Transport but is required because there is no guarantee that messages will not travel over insecure channels on their way between the client and server.
  3. The service will enable clients to authenticate it using a X.509 certificate.
  4. Clients will not provide credentials to the service.

Preparing a Certificate For the Service

A real certificate costs money. Developers can use the makecert utility to test with.

In the intial negotiation between client and server the certificate will be presented to the client by the service as proof of trustworthiness. There are two methods that the client can use to assert that the certificate represents a trustworthy entity.

One method is to use to check whether a copy of the certificate is already installed in the “Trusted People” store for the current user on the client machine. This is called “Peer Trust”. This is not always not a practical approach because it would mean that a client would need to somehow retrieve and store copies of the certificates of all the services it needs to consume.

The other method is to check whether the certificate is signed by a trusted authority attesting to it’s trustworthiness. Trusted authorities are stored in the Trusted Root Certification Authorities store for the current user on the client machine. This way the client doesnt need to store every certificate for every service. Instead it only needs to store the certificates of certificate authorities that it trusts. If the certificate of the service is signed with a certificate that is not in the Trusted Root Certification Authorities folder the client can call out for the certificate of the signer of that certificate. If the signer’s certificate is in the Trusted Root Certification Authorities folder, then it can be trusted, and then therefore, so can the service. This method is called “Chain Trust” for obvious reasons.

Chain Trust is the default way of validating a certificate, but it requires a few more calls to makecert. For this reason, I chose to use a self-signed certificate and to apply Peer Trust by installing it in the Trusted People store for the Current User. Later in this post we will see how to change the default in the configuration file.

In order to test the Anonymous scenario in the source code, you need to invoke makecert as follows (on the service machine) :

makecert -n CN=TestServiceCert -sr LocalMachine -ss My -sky exchange –pe

Read out loud: Create a new certificate for a subject called “TestServiceCert” and install it in the Personal (My) store of the LocalMachine. The new certificate will have a private key and can be used to encrypt symmetrical keys that will be used to encrypt data (over our channel).

Then copy the new certificate to the client machine (can be the same machine) and import into the Trusted People store of the Current User.

OK, so now we have a X.509 representing the server that the server can retrieve from the Personal Store of the Local Machine and the client will find the certicate in the Trusted People store of the Current User.

Service Configuration File

Here is the App.config for the server.

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service name="CalculatorService.Calculator"

               behaviorConfiguration="AnonymousServiceBehavior">

        <endpoint address="Calculator"

                  binding="wsHttpBinding"

                  bindingConfiguration="AnonymousBindingConfiguration"

                  contract="CalculatorService.ICalculator" />

        <endpoint address="mex"

                  binding="mexHttpBinding"

                  contract="IMetadataExchange" />

        <host>

          <baseAddresses>

            <add baseAddress="http://localhost/Services" />

          </baseAddresses>

        </host>

      </service>

    </services>

    <bindings>

      <wsHttpBinding>

        <binding name="AnonymousBindingConfiguration">

          <security mode="Message">

            <message clientCredentialType="None"/>

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

    <behaviors>

      <serviceBehaviors>

        <behavior name="AnonymousServiceBehavior">

          <serviceAuthorization

            principalPermissionMode="None"/>

          <serviceCredentials>

            <!--makecert -n CN=TestServiceCert -sr LocalMachine -ss My -sky exchange -pe-->

            <serviceCertificate

              findValue="TestServiceCert"

              storeLocation="LocalMachine"

              x509FindType="FindBySubjectName"

              storeName="My"/>

          </serviceCredentials>

          <serviceMetadata/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Reading from the top:

This application hosts a service called Calculator.Service at an end point whose address is http://localhost/Services/Calculator, uses the wsHttpBinding and exposes the CalculatorService.ICalculator contract.

The application also provides metadata on a mex endpoint (configured as an endpoint, and a serviceMetadata entry in the AnonymousServiceBehavior at the bottom of the file).

The configuration for the wsHttpBinding, called AnonymousBindingConfiguration (are you following?) will be implementing Message security. Clients will not be authenticated.

The service implements a behavior called “AnonymousServiceBehavior” which declares the service certificate we just created as the “service credential”. Under serviceAuthorization the service indicates that no permission system will be implemented (principlePermissionMode).

Client Configuration File

Here is the App.config:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <client>

      <endpoint

        address="http://localhost/Services/Calculator"

        binding="wsHttpBinding"

        bindingConfiguration="AnonymousBindingConfiguration"

        behaviorConfiguration="AnonymousEndpointBehavior"

        contract="ServiceReferences.ICalculator">

        <identity>

          <dns value="TestServiceCert"/>

        </identity>

      </endpoint>

    </client>

    <bindings>

      <wsHttpBinding>

        <binding name="AnonymousBindingConfiguration">

          <security mode="Message">

            <message clientCredentialType="None"/>

          </security>

        </binding>

      </wsHttpBinding>

    </bindings>

    <behaviors>

      <endpointBehaviors>

        <behavior name="AnonymousEndpointBehavior">

          <clientCredentials>

            <serviceCertificate>

              <authentication

                certificateValidationMode="PeerTrust"/>

            </serviceCertificate>

          </clientCredentials>

          </behavior>

      </endpointBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Reading again from the top.

This application will instantiate proxies for the calculator service that will communicate across the endpoint whose address is http://localhost/Services/Calculator, that uses the wsHttpBinding and exposes the CalculatorService.ICalculator contract.

Note that the Endpoint has an identity element that indicates that the expected identity of the service is not ‘localhost’ (the default name is the url of the machine) but the name of the certificate.

The configuration for the wsHttpBinding, called AnonymousBindingConfiguration will be implementing Message security to encrypt and sign all data that crosses the channel. No client credentials will be presented to the service as this is an Anonymous scenario.

The endpoint also has a behavior called AnonymousEndpointBehavior in which it specifies clientCredentials which specify how the client authenticates the server. It does so using a serviceCertificate which will be trusted if the certificate can be found in the Trusted People store of the Current User.

In the next post we will take a look at the Business to Business scenario. In that scenario, we will see that the client implements similar policies as the service does in this Anonymous scenario.

WCF Security Scenarios – Barebones - Part 1

This post presents a barebones yet complete demonstration of security policies suitable for the Intranet scenario.

Other posts in this series:

WCF Security Scenarios – Barebones (Overview)

WCF Security Scenarios – Barebones Part 2 (Anonymous)

WCF Security Scenarios – Barebones Part 3 (Business-to-Business)

WCF Security Scenarios – Barebones Part 4 (Internet)

Source code for all scenarios can be downloaded here.

Scenario: Intranet

They key characteristics of this scenario are:

  1. Interoperability: Not needed, client and server run Windows and WCF.
  2. Firewall: There is no need to cross a firewall.
  3. Point-to-Point: In an intranet the client and server communicate directly, there is no intermediary.
  4. Client Identity: The client has a Windows Identity which can be used for authentication and authorization.

Security Policy

  1. We can use the net tcp binding for efficient communication between WCF parties that does not cross a firewall.
  2. The security mode should be Transport as oppose to Message.
    Message protection requires more processing and is not needed when there is no intermediary which might forward the message to its final destination over an insecure channel.
  3. Windows identities will be used for mutual authentication (and authorization – though this is not demonstrated).

Service Configuration File

Here is the App.config:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <services>

      <service name="CalculatorService.Calculator"

               behaviorConfiguration="IntranetServiceBehavior">

        <endpoint address="Calculator"

                  binding="netTcpBinding"

                  bindingConfiguration="IntranetBindingConfiguration"

                  contract="CalculatorService.ICalculator" />

        <endpoint address="mex"

                  binding="mexTcpBinding"

                  contract="IMetadataExchange" />

        <host>

          <baseAddresses>

            <add baseAddress="net.tcp://localhost/Services" />

          </baseAddresses>

        </host>

      </service>

    </services>

    <bindings>

      <netTcpBinding>

        <binding name="IntranetBindingConfiguration">

          <security mode="Transport">

            <transport

              clientCredentialType="Windows"

              protectionLevel="EncryptAndSign"/>

          </security>

        </binding>

      </netTcpBinding>

    </bindings>

    <behaviors>

      <serviceBehaviors>

        <behavior name="IntranetServiceBehavior">

          <serviceMetadata/>

        </behavior>

      </serviceBehaviors>

    </behaviors>

  </system.serviceModel>

</configuration>

Reading from the top:

This application hosts a service called Calculator.Service at an end point whose address is net.tcp://localhost/Services/Calculator, uses the netTcpBinding and exposes the CalculatorService.ICalculator contract.

The application also provides metadata on a mex endpoint (configured as an endpoint, and a serviceMetadata entry in the IntranetServiceBehavior at the bottom of the file).

The configuration for the netTcpBinding, called IntranetBindingConfiguration (are you following?) will be implementing Transport security to encrypt and sign all data that crosses the channel. Client credentials are derived from the client’s Windows identity.

Client Configuration File

Here is the App.config:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.serviceModel>

    <client>

      <endpoint address="net.tcp://localhost/Services/Calculator"

                binding="netTcpBinding"

                bindingConfiguration="IntranetBindingConfiguration"

                contract="ServiceReferences.ICalculator">

      </endpoint>

    </client>

    <bindings>

      <netTcpBinding>

        <binding name="IntranetBindingConfiguration">

          <security mode="Transport">

            <transport clientCredentialType="Windows"

                       protectionLevel="EncryptAndSign"/>

          </security>

        </binding>

      </netTcpBinding>

    </bindings>

  </system.serviceModel>

</configuration>

Reading again from the top.

This application will instantiate proxies for the calculator service that will communicate across the endpoint whose address is net.tcp://localhost/Services/Calculator, that uses the netTcpBinding and exposes the CalculatorService.ICalculator contract.

The configuration for the netTcpBinding, called IntranetBindingConfiguration will be implementing Transport security to encrypt and sign all data that crosses the channel. Client credentials are derived from the client’s Windows identity.

That was fairly straightforward ...

Next is the Anonymous scenario which requires some certificate handling. Do I hear you groaning?

I promise to keep it simple . . .

WCF Security Scenarios - Barebones

You can download the source code for these posts here.

In his book, “Programming WCF Services”, Juval Lowy does a superb job of explaining the principles of WCF Security and simplifies the subject by discussing 4 typical security scenarios.

I implemented those scenarios as demos for my latest class in WCF and I would like share them with you in this and the next few posts.

Why should you read on?

These demos are concise yet complete. My approach has been to use configuration files only - no code. For each scenario I present two complete configuration files, one for the client and one for the server. I have removed every character that is not absolutely necessary for the demonstration. I have also aligned the configuration files so you can easily compare them line by line to locate the differences.

Hopefully you should be able to get started with one of these demos very quickly.

Overview

There are four typical scenarios: Intranet, Anonymous, Business-to-Business and Internet. I have dedicated a post to each one.

Each has a demo in the source code. The four demos are identical except for their configuration files (and the Internet demo which differs slightly in code too).

Each demo consists of a self-hosted console application which also contains the service implementation and a console client application that consumes it. The service consists of a calculator contract with one method.

This is the contract:

namespace CalculatorService

{

    [ServiceContract]

    public interface ICalculator

    {

        [OperationContract]

        double Add(double a, double b);

    }

}

This is the implementation

namespace CalculatorService

{

    [ServiceBehavior]

    public class Calculator : ICalculator

    {

        #region ICalculator Members

 

        public double Add(double a, double b)

        {

            return a + b;

        }

 

        #endregion

    }

}

And this is the client code

namespace Client

{

    class Program

    {

        void Run()

        {

            try

            {

                CalculatorClient calc = new CalculatorClient();

                double result = calc.Add(5, 6);

 

                Console.WriteLine("Result = {0}", result);

            }

            catch (Exception ex)

            {

                Console.WriteLine(ex.Message);

            }

        }

 

        static void Main(string[] args)

        {

            new Program().Run();

            Console.ReadLine();

        }

    }

}

The only exception to this is in the Internet scenario where I demonstrate role-based security. I will show you the small differences in code for that scenario in the last post of this series.

Apart from that, we are done with code, let’s go and read those app.configs. . .

More Posts Next page »