Tuesday, April 13, 2010

ASP.NET Forms Authentication Sharing for Silverlight/WCF applications

Over two years ago I’ve blogged on how to share the forms authentication between ASP.NET and ClickOnce applications. We use this technique often for it’s just convenient to have just one, centralized authentication mechanism (ASP.NET Membership Provider in this case) in large modular systems.

In just few words – that sharing is possible by passing a Forms authentication cookie to the ClickOnce application as an Uri parameter and append the cookie to the CookieContainer of a webservice proxy class. This way all requests from the ClickOnce application back to the server (which pass through the FormsAuthenticationModule) are correctly recognized as authenticated and in the same time an exception is thrown in case of a missing or invalid authentication cookie. On the server side – web service methods are guarded with the PrincipalPermission attribute. Please, refer to the article for more details.

As we finally add Silverlight to our daily toolbox, I strongly needed a corresponding mechanism for WCF Services and Silverlight proxies. I need to share the Forms authentication between my ASP.NET hosting application and the Silverlight application users run after they login to the ASP.NET application.

What seemed to be a piece of cake, has turned out to be tricky, mostly because of the way in which exceptions are passed between the server and the Silverlight at the client side.

Guarding WCF calls with PrincipalPermission – an easy step

The first step is easy. Since Silverlight is hosted in web browser, there’s no need to copy cookies as cookies are appended automatically to WCF service calls (assuming that both hosting application and hosted Silverlight come from the same domain but this is usually the case). Assuming then that users need to login first using Forms authentication and then they get access to the Silverlight application, whenever the Silverlight calls back the WCF on the server, Forms authentication cookies are there.

So I just happily put PrincipalPermission over my WCF methods but unfortunately no matter whether I logged first or not, I got an exception all the time. It seems that WCF needs an few additional spells to be casted for this to work (and these spells was not needed for ASP.NET WebServices):

  • an additional AspNetCompatibilityRequirements attribute over the WCF class
  • setting Thread.CurrentPrincipal in the WCF constructor
[ServiceContract( Namespace = "http://my.namespace.com/service1" )]
[AspNetCompatibilityRequirements( RequirementsMode = AspNetCompatibilityRequirementsMode.Required )]
public class Service1
{
public Service1()
{
/* this line is crucial for PrincipalPermission to work */
Thread.CurrentPrincipal = HttpContext.Current.User;
}
... WCF methods follow ...





  • forcing the runtime to use ASP.NET compatibility mode for WCF (web.config)




...
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
...
</system.serviceModel>





Having all these prepared, I can safely guard my WCF business methods with PrincipalPermission attribute and exceptions are thrown back to the client in case of unauthenticated users.




...
public class Service1
{
[OperationContract]
[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
public int DoWork( int i )
{
return i;
}


Detecting the Forms Cookie timeout – a slightly more difficult step





Unfortunately, I cannot stop at this point. This is because Forms Authentication cookies do timeout. For ASP.NET application this is not a problem – the FormsAuthenticationModule just redirects the call to the LoginPage so users just relogin. For ClickOnce/WebService – I can catch SOAPExceptions, take a look at the Message and check whether it says something about PrincipalPermission. If this is so, I can relogin my users with an additional unguarded Web Service method which just reappends valid Forms Authentication cookies to the proxy’s cookie container.



In my WCF/Silverlight scenario, the timeout of the Forms cookie is a disaster. All consecutive WCF requests still contain the Forms cookie but the cookie is not valid anymore and the PrincipalPermission attribute causes these calls to fail. An exception from the inside of server’s runtime environment is returned to the client as just “The remote server returned an error: Not found”. My chance to detects the actual cause of the exception is zero. “Not found” does not mean “Your cookie has just timed out!”.



What I need is to use two independent mechanisms:




  • I have to catch principal permission issues explicitely on the server so that …


  • … I can explicitely pass it to Silverlight and catch on the client side



Let’s start from the latter, since it’s more obvious – there’s an explicit way of passing exceptions from the server to Silverlight using FaultContract.



FaultContract is an attribute on my service class with tells WCF how to pass exceptions to the client.



The former can be achieved by using an imperative way of permission demanding – rather than using PrincipalPermissionAttribute, we will use the PrincipalPermission and call it’s Demand method so that we can catch the exception of the server side, wrap it into the FaultContract and send it to the client.



Let’s start from the FaultContract at the server side:




/* note that this does not inherit from the Exception class ... */
[DataContract]
public class MyException
{
[DataMember]
public int Code { get; set; }
[DataMember]
public string ActualException { get; set; }
}

[ServiceContract( Namespace = "" )]
[AspNetCompatibilityRequirements(
RequirementsMode = AspNetCompatibilityRequirementsMode.Required )]
public class Service1
{
public Service1()
{
Thread.CurrentPrincipal = HttpContext.Current.User;
}

[OperationContract]
/* ... because this is the FaultContract which inherits from Exception */
[FaultContract( typeof( MyException ) )]
public int DoWork( int i )
{





Inside our business method we have to check if the call is authenticated and if not – we wrap it in a FaultException instance.



There’s still one caveat however – MSDN claims that in Silverlight, FaultContracts have to be handled using a WCF Behavior Extension due to some limitations of the web browser’s stack. Fortunately, someone has pointed that much simpler way – I just need to cast a single-line spell inside the business method so that exceptions are passed with 200 OK instead of 500 Internal Server error and the FaultContract can be consumed at the Silverlight’s client side:




[OperationContract]
[FaultContract( typeof( MyException ) )]
public int DoWork( int i )
{
/* a powerfull spell so that I do not need a WCF
Behavior Extension as MSDN says
*/
System.ServiceModel.Web.WebOperationContext
.Current.OutgoingResponse.StatusCode =
System.Net.HttpStatusCode.OK;

/* Check for principal permissions explicitely rather than
using the attribute */

/* I do not need any special username/role but I need
calls to be authenticated */
PrincipalPermission p = new PrincipalPermission( null, null, true );
try
{
p.Demand();
}
catch ( Exception ex )
{
/* wrap the exception so that Silverlight can consume it */
MyException fault =
new MyException()
{
/* Code = 1 will mean "unauthenticated!" */
Code = 1, ActualException = ex.Message
};

throw new FaultException<MyException>( fault );
}

return i;
}







This is all at the server’s side, let’s go back to the Silverlight client and call the service:




private void Button1_Click( object sender, RoutedEventArgs e )
{
ServiceReference1.Service1Client c = new Service1Client();

c.DoWorkCompleted +=
new EventHandler<DoWorkCompletedEventArgs>( c_DoWorkCompleted );

/* cannot try-catch here as this is an asynchronous call */
c.DoWorkAsync( 5 );
}

void c_DoWorkCompleted( object sender, DoWorkCompletedEventArgs e )
{
/*
Note that e.Error is set whenever an exception is raised from somewhere.
However, this time instead of "Not found." we have the FaultContract passed
to the client.
*/

HandleFaultException( e.Error );

MessageBox.Show( e.Result.ToString() );
}

/* A generic method to handle WCF exceptions */
void HandleFaultException( Exception ex )
{
/* Is it my custom Fault sent to the Client? */
if ( ex is FaultException<MyException> )
{
FaultException<MyException> exception = (FaultException<MyException>)ex;

/* Is the Code == 1? Remember the convention - 1 means "unauthenticated" */
if ( exception.Detail.Code == 1 )
{
/* Do whatever you like but now you know
what's the reason of the exception */
/* You can for example redirect the Silverlight
application to ASP.NET Login page
so that the user relogins and will likely run
the Sliverlight application again */
HtmlPage.Window.Navigate( new Uri( "loginPage.aspx", UriKind.Relative ) );
}
}
}





Happy coding.

Friday, March 26, 2010

Container-Child Relation for Windows.Forms with no MdiContainer

In a Windows.Forms application, it’s fairly easy to make use of a MdiContainer in a Parent form and create new forms inside it’s container. However, the same result can be obtained with no MdiContainer at all.

You just create a new Form, set it’s TopLevel property to false and add it to the Controls collection of another Form.

ChildForm form = new ChildForm();
form.TopLevel = false;

ParentForm.Controls.Add( form );
form.Show();



As a result, you get a fully draggable child form which can also be minimized and maximized. The only difference between this and MdiContainer would be the lack of a scroller automatically added by the MdiContainer.






Wednesday, March 24, 2010

Individual DataTemplates for Programmatically Populated Silverlight TreeView

Silverlight TreeView can be populated both declaratively and programmatically. In complex scenarios, the latter is preferred. This simple tutorial demonstrates how individual DataTemplates can be selected for newly created items.

Let’s start with creating a business class we will use to create tree items:

public class Person
{
public string Name { get; set; }
public string Surname { get; set; }

public override string ToString()
{
return string.Format( "{0} {1}", Name, Surname );
}
}





Let’s also create the XAML containing a TreeView and two distinct DataTemplates for tree items. Note that both templates contain explicit bindings to model’s properties: first template will render a tree item as text while the second will render it as a button.



In the latter case we’d also like to be able to determine the business entity bound to the button the user clicks.




<UserControl 
xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
x:Class="SilverlightTutorial1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<DataTemplate x:Key="TreeViewItemTemplate1">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
<DataTemplate x:Key="TreeViewItemTemplate2">
<Button Content="{Binding}" Tag="{Binding}" Click="Button_Click" />
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<controls:TreeView x:Name="TheTree">

</controls:TreeView>
</Grid>
</UserControl>





Items will be created programmatically and the trick is to set an instance of the business entity as TreeViewItem’s Header and in the same time set TreeViewItem’s HeaderTemplate to point to a DataTemplate, picking one from resources.




public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();

TreeViewItem item = InitNodeFromTemplate1();
InitNodeFromTemplate2(item);
}

private TreeViewItem InitNodeFromTemplate1()
{
TreeViewItem item = new TreeViewItem();

item.Header = new Person() { Name = "John", Surname = "Kowalski" };
item.HeaderTemplate = (DataTemplate)this.Resources["TreeViewItemTemplate1"];

TheTree.Items.Add( item );

return item;
}

private void InitNodeFromTemplate2( TreeViewItem Parent )
{
TreeViewItem item = new TreeViewItem();

item.Header = new Person() { Name = "Tim", Surname = "Malinowski" };
item.HeaderTemplate = (DataTemplate)this.Resources["TreeViewItemTemplate2"];

Parent.Items.Add( item );
}

private void Button_Click( object sender, RoutedEventArgs e )
{
Button b = sender as Button;
Person p = b.Tag as Person;

MessageBox.Show( p.ToString() );
}





Note that the button’s click handler retrieves the business entity from the button’s Tag property and this is possible because of the binding inside the TreeViewItemTemplate2 DataTemplate.



The output of the above example looks like this:







Note that DataTemplates can be created programmatically. This is described in this blog note by Tamir Khason.

Friday, March 19, 2010

Silverlight Visual Tree Walker

Constrained by Silverlight’s FindName method and inspired by a blog note by Jim Baltzell-Gauthier and my own implementation of tree walker for ASP.NET, I came up with a corresponding code snippet for Silverlight.

What you get is two extension methods, Find to search for a single child component and FindAll to search for all children matching specified predicate.

Note that this way you have a complete freedom of what you would like to find. Controls of specific name, specific type, anything – is just a matter of correct predicate.

public static class VisualTreeWalker
{
public static FrameworkElement Find(
this DependencyObject Parent,
Predicate<FrameworkElement> Predicate )
{
if ( Parent is FrameworkElement )
if ( Predicate( (FrameworkElement)Parent ) )
return (FrameworkElement)Parent;

foreach ( DependencyObject child in GetChildren( Parent ) )
{
try
{
FrameworkElement childElement = child as FrameworkElement;
if ( childElement != null )
if ( Predicate( childElement ) )
return childElement;
}
catch { }
}
return null;
}

public static FrameworkElement[] FindAll(
this DependencyObject Parent,
Predicate<FrameworkElement> Predicate )
{
List<FrameworkElement> ret = new List<FrameworkElement>();

FindAllHelper( Parent, Predicate, ret );

return ret.ToArray();
}

private static void FindAllHelper(
DependencyObject Parent,
Predicate<FrameworkElement> Predicate,
List<FrameworkElement> results )
{
if ( Parent is FrameworkElement )
if ( Predicate( (FrameworkElement)Parent ) )
results.Add( (FrameworkElement)Parent );

// rekursja
foreach ( DependencyObject child in GetChildren( Parent ) )
{
FindAllHelper( child, Predicate, results );
}
}

private static IEnumerable<DependencyObject>
GetChildren( DependencyObject Parent )
{
int childCount = VisualTreeHelper.GetChildrenCount( Parent );

for ( int i = 0; i < childCount; i++ )
{
yield return VisualTreeHelper.GetChild( Parent, i );
}
}
}





As an example, suppose you have a complex Silverlight Grid, called TheGrid, and somewhere deeply inside it there are three ToggleButtons (that was an original case which motivated me to write the above snippet). Now I just iterate:




foreach ( ToggleButton toggleButton in
TheGrid /* parent control */
.FindAll( c => c is ToggleButton ) /* searching */
.Select( f => f as ToggleButton ) ) /* projection - casting */
{
/* now I got all my child controls I need */
}



Thursday, March 11, 2010

User Interface idea for Visual Studio 2012

Here’s a brilliant research idea for building GUI of integrated development environments:

http://www.cs.brown.edu/people/acb/codebubbles_site.htm

I hope to be able to work like this with Visual Studio 2012.

Friday, December 18, 2009

Rough implementation of Base64StreamReader and Base64StreamWriter

Decorator-like streams in .NET work beautifully - you can decorate any stream with any other stream so for example you can put compression and encryption over network stream seamlessly and this makes no difference for the client code as it always expects just a stream.

Few days ago I had to wrap a compressed stream so that the data which is actually passed to the client code is not an arbitrary stream of bytes but something that can be written in a text file. An obvious answer is Base64.

There's however one caveat - we do not have any implementation of base64 decorating streams in the base class library.

What I expected is something like:

using ( FileStream fs = new FileStream() )
using ( Base64StreamWriter b64 = new Base64StreamWriter( fs ) )
using ( StreamWriter sw = new StreamWriter( b64 ) )
    sw.Write( "some text" );

and corresponding:



using ( FileStream fs = new FileStream(...) )
using ( Base64StreamReader bw = new Base64StreamReader( fs ) )
using ( StreamReader sw = new StreamReader( bw ) )
    MessageBox.Show( sw.ReadToEnd() );

As I've not been able to find any useful implementation, I wrote some rough code which is not fully tested but seems to work correctly in few important scenarios. Please use and modify the code at your own risk.


Note also that you can alternatively switch between Base64 and BinHex encodings (BinHex uses only digits to encode data).



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
 
namespace Base64Streams
{
    public class Base64StreamReader : Stream
    {
        private static XmlReaderSettings InitXmlReaderSettings()
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ConformanceLevel = ConformanceLevel.Auto;
            settings.CloseInput = false;
            return settings;
        }
 
        private Stream TheStream;
        private XmlReader xw;
 
        public Base64StreamReader( Stream Stream )
        {
            this.TheStream = Stream;
        }
 
        public override bool CanRead
        {
            get { return TheStream.CanRead; }
        }
 
        public override bool CanSeek
        {
            get { return TheStream.CanSeek; }
        }
 
        public override bool CanWrite
        {
            get { return TheStream.CanWrite; }
        }
 
        public override void Flush()
        {
        }
 
        public override void Close()
        {
            xw.Close();
        }
 
        public override long Length
        {
            get
            {
                return TheStream.Length;
            }
        }
 
        public override long Position
        {
            get
            {
                return TheStream.Position;
            }
            set
            {
                throw new NotImplementedException();
            }
        }
 
        bool movedToContent = false;
        public override int Read( byte[] buffer, int offset, int count )
        {
            if ( !movedToContent )
            {
                xw = XmlReader.Create( TheStream, InitXmlReaderSettings() );
                xw.MoveToContent();
 
                movedToContent = true;
            }
 
            /* use 
             * int readed = xw.ReadElementContentAsBase64( buffer, offset, count );
             * for Base64 encoding
             */
            int readed = xw.ReadElementContentAsBinHex( buffer, offset, count );
 
            return readed;
        }
 
        public override long Seek( long offset, SeekOrigin origin )
        {
            throw new NotImplementedException();
        }
 
        public override void SetLength( long value )
        {
            throw new NotImplementedException();
        }
 
        public override void Write( byte[] buffer, int offset, int count )
        {
        }
    }
 
    public class Base64StreamWriter : Stream
    {
        private static XmlWriterSettings InitXmlWriterSettings()
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.ConformanceLevel = ConformanceLevel.Auto;
            settings.Encoding = Encoding.ASCII;
            settings.OmitXmlDeclaration = true;
            settings.CloseOutput = true;
            return settings;
        }
 
        private Stream TheStream;
        private XmlWriter xw;
 
        public Base64StreamWriter( Stream Stream )
        {
            this.TheStream = Stream;
 
            xw = XmlWriter.Create( Stream, InitXmlWriterSettings() );
            xw.WriteStartElement( "data" );
        }
 
        public override bool CanRead
        {
            get { return TheStream.CanRead; }
        }
 
        public override bool CanSeek
        {
            get { return TheStream.CanSeek; }
        }
 
        public override bool CanWrite
        {
            get { return TheStream.CanWrite; }
        }
 
        public override void Close()
        {
            if ( xw.WriteState != WriteState.Closed )
            {
                xw.WriteEndElement();
                xw.Close();
            }
            base.Close();
        }
 
        public override void Flush()
        {
            xw.Flush();
        }
 
        public override long Length
        {
            get
            {
                return TheStream.Length;
            }
        }
 
        public override long Position
        {
            get
            {
                return TheStream.Position;
            }
            set
            {
                throw new NotImplementedException();
            }
        }
 
        public override int Read( byte[] buffer, int offset, int count )
        {
            throw new NotImplementedException();
        }
 
        public override long Seek( long offset, SeekOrigin origin )
        {
            throw new NotImplementedException();
        }
 
        public override void SetLength( long value )
        {
            throw new NotImplementedException();
        }
 
        public override void Write( byte[] buffer, int offset, int count )
        {
            /* use 
             * xw.WriteBase64( buffer, offset, count ); 
             * for Base64 encoding
             */
            xw.WriteBinHex( buffer, offset, count );
        }
    }
}