Maarten Balliauw {blog}

Web development, NuGet, Microsoft Azure, PHP, ...

NAVIGATION - SEARCH

ASP.NET MVC framework - Security

Some posts ago, I started playing with the ASP.NET MVC framework. In an example I'm creating, I'm trying to add Forms-based security.

"Classic" ASP.NET offers a nice and easy way to set security on different pages in a web application, trough Web.config. In the example I'm building, I wanted to allow access to "/Entry/Delete/" only to users with the role "Administrator". So I gave the following snip a try:

[code:c#]

<location path="/Entry/Delete">
   <system.web>
     <authorization>
       <allow roles="Administrators"/>
       <deny users="*"/>
     </authorization>
   </system.web>
</location>

[/code]

This seems to work in some occasions, but not always. Second, I think it is very confusing to define security in a different place than my route table... Since the ASP.NET MVC framework is built around "dynamically" changing URL schemes, I'm not planning to maintain my Web.config security for each change...

In an ideal world, you would specify permissions for a route at the same location as you specify the route. Since the ASP.NET MVC framework is still an early CTP, perhaps this might be added in future versions. For now, the follwing strategies can be used.

Code Access Security

Luckily, the .NET framework offers a nice feature under the name "CAS" (Code Access Security). Sounds scary? Perhaps, but it's useful in the MVC security context!

The idea behind CAS is that you specify security requirements using attributes. For example, if authentication is required in my EntryController (serving /Entry/...), I could use the following code snippet:

[code:c#]

[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
public class EntryController : Controller {
    // ...
}

[/code]

Now let's try my example from the beginning of this post. The URL "/Entry/Delete" is routed to my EntryController's Delete method. So why not decorate that method with a CAS attribute?

[code:c#]

[ControllerAction]
[PrincipalPermission(SecurityAction.Demand, Role="Administrator"]
public void Delete(int? id) {
   ...
}

[/code]

This snippet makes sure the Delete method can only be called by users in the role "Administrator"

Exception handling

Problem using the CAS approach is that you are presented an ugly error when a security requirement is not met. There are two possible alternatives for catching these Exceptions.

Alternative 1: Using Global.asax

In Global.asax, you can specify an Application_Error event handler. Within this event handler, you can catch specific types of Exceptions and route them to the right error page. The following example redirects each SecurityException to the /Login, my LoginController:

[code:c#]

protected void Application_Error(object sender, EventArgs e) {
    Exception ex = Server.GetLastError().GetBaseException();
    if (ex is SecurityException) {
        Response.Redirect("/Login");
    }
}

[/code]

Alternative 2: Use more attributes!

Fredrik Normén has posted an ExceptionHandler attribute on his blog, which allows you to specify which type of Exception should be handled by which type of view. Hope this makes it into a future ASP.NET MVC framework version too!

Alternative 3: Use in-line CAS

Another option is to use in-line CAS. For example, you can do the folluwing in your ControllerAction:

[code:c#]

try {
    PrincipalPermission permission = new PrincipalPermission(User.Identity.Name, "Administrators", true);
    permission.Demand();
} catch (SecurityException secEx) {
    // Handle the Exception here...
    // Redirect to Login page, for example.
}

[/code]

ASP.NET DataPager not paging after first PostBack?

A few posts ago, I mentioned that I am currently giving a classroom training on ASP.NET. People attending are currently working on a project I gave them, and today one of them came up to me with a strange problem...

Here's the situation: in VS 2008, a web page was created containing 2 controls: a DataList and a DataPager. This DataPager serves as the paging control for the DataList. Databinding is done in the codebehind:

[code:c#]

protected void Page_Load(object sender, EventArgs e) {
    ListView1.DataSource = NorthwindDataSource;
    ListView1.DataBind();
}

[/code]

This works perfectly! When the page is rendered in a brwoser window, data is shown in the DataList control. Now, when testing the DataPager, something strange happens: when a page number is clicked, ASP.NET will process a PostBack, rendering... the same page as before! Clicking the DataPager again is the only way to really go to a different page in the result set.

Let's have a look at the ASP.NET page lifecycle... The page Load event is actually not the best place to call the DataBind() method. PreRender is a better place to call DataBind():

[code:c#]

protected void Page_Load(object sender, EventArgs e) {
    ListView1.DataSource = NorthwindDataSource;
}

protected void Page_Render(object sender, EventArgs e) {
    ListView1.DataBind();
}

[/code]

kick it on DotNetKicks.com

Thank you, ISP!

Living in Belgium sometimes feels like living in the desert. Over the past few years, my ISP has always offered the same: a cable modem subscription with 10Mbit downstream speed, and 256Kbit upstream speed. Great! Except for that data transfer limit of 12 GB per month and the ridiculous price of 42 EUR (that is 61 US$). And no, there are few better alternatives in this center of Europe...

Good news though! My ISP has been teasing its customers with a marketing campaign, focussing on great improvements in every Internet access subscription they offer. Rumours came along on different user forums: price drops, no more data transfer limits, ... Now here's the real improvement: everything stays the same, except you get a faster upload speed (512Kbit, yes!). Data transfer limit stays... (press release)

12 GB of data transfer is filled quite fast at 10Mbps... Here's what I think, and many others too...

 
(For the record: the information on this website represents my personal opinion. My opinion may differ with other people's opinion and my employer's opinion. This website is by no means related to other people nor my employer. )

ASP.NET MVC Framework - Basic sample application

ASP.NET MVC FrameworkYou might have noticed that I'm quite enhousiast about the new ASP.NET MVC framework.

What are you talking about?

Basically, this new ASP.NET MVC framework is an alternative to standard ASP.NET webforms, with some advantages:

  • No more postbacks or viewstate, no more page lifecycle trouble: all communication is done using a REST pattern
  • Separation of concerns: no more pages containing cluttered business logic inside view logic (MVC)
  • Testable model and controller: you can now create uinit tests which communicate with your model as if a user is browsing your website

Is there a tutorial available?

For more information and a step-by-step tutorial, check Scott Guthrie's blog:

My own sample project

For an article I'm working on, I am writing a sample application using this framework. This sample application is a very basic photo album website, listing some albums and photo's. Anyone who's interested in a sample MVC application (no data entry yet!) can download it.

Current shortcomings...

There are some shortcomings in the current CTP... Current databound controls can not be used easily. There are some ways around, but using a simple <% foreach ... %> is currently the easiest way to display data on your web page. Another way around is the MVCToolkit project, which adds support for some helper methods and classes.

ASP.NET 3.5 Extensions CTP preview released

Just over the weekend, Microsoft has released the ASP.NET 3.5 Extensions CTP. This download includes several additions to ASP.NET 3.5:

  • ASP.NET AJAX Improvements contains some new AJAX features like browser history support (back-forward), improvements to the JavaScript library, ...
  • The new ASP.NET MVC framework which I blogged about last week
  • ASP.NET Dynamic Data Support contains some new features for building a data-driven application much faster.
  • ASP.NET Silverlight support
  • ADO.NET data services, also known under the name "Astoria".

I'll be doing some testing during the week, and keep you all informed.

LINQ to filesystem

The past few hours, I've been experimenting with LINQ. As a sample application, I'm trying to create a small photo album website, which shows me all images in a specific folder on my webserver.

What does LINQ have to do with that? Everyone has used a loop over all files in a folder, and I decided to try LINQ for that matter. Here's how:

[code:c#]

var rootFolder = "C:\\";
var selectedImages = from file in Directory.GetFiles(rootFolder, "*.jpg")
                             select new { Path = file,
                                          Name = new FileInfo(file).Name,
                                          CreationDate = new FileInfo(file).CreationTime,
                                          DirectoryName = new FileInfo(file).DirectoryName
                                    };

[/code]

There you go! A collection named "selectedImages", filled with anonymous class instances containg a file Path, Name, CreationDate and DirectoryName. This collection can now be bound to, for example, a GridView:

[code:c#]

this.gridView1.DataSource = selectedImages;
this.gridView1.DataBind();

[/code]

EDIT: (mental note to myself: add LINQ keywords to syntax highlighter...) - done!

ASP.NET MVC framework preview to be released next week

Half the world has been focussing on the release of the new Visual Studio 2008 and .NET 3.5 last week. That is good, as .NET 3.5 offers lots of nice new features and improvements. In the blogosphere, I haven't read much about an extension I've been waiting for anxiously: the new ASP.NET MVC framework.

Luckily, Scott Guthrie posted some examples on it, and I can't wait for a preview to be released next week. I'll keep you informed!

ASP.NET load balancing and ASP.NET state server (aspnet_state)

At one of our clients, we used to have only one server for ASP.NET applications (including web services). Since this machine is actually business-critical and load is constantly growing, the need for a second machine is higher than ever.

This morning I was asked to set up a simple demo of a load-balanced ASP.NET environment. I already did this in PHP a couple of times, but in ASP.NET, this question was totally new to me. Things should not be very different, I thought. And this thought proved right!

A bit later, we had a load balancer in front of 2 web server machines. We got everything configured, fired up our webbrowser and saw a different page on each refresh (stating the server's hostname). Load balancing mission succeeded!

Next thing: session state. In our PHP environment, we chose to centralize all session data in a database. ASP.NET provides the same functionality, but we chose to use the ASP.NET state server for this demo. This proved to be a difficult yourney... But we managed to get things running! Here's how.

1. Set up the ASP.NET state service

Pick a server which will serve as the session state server. Fire up the services control panel (services.msc). Select the "ASP.NET State Service" item and make it start automatically. Great! Our state service is running.

Caveat 1: state server will not listen on any public IP address. So fire up your registry editor, change the following key and restart the ASP.NET state service:

HKLM\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\AllowRemoteConnections

Eventually change the port on which the state server will be listening:

HKLM\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters\Port (default: 42424)

Caveat 2: after changing the AllowRemoteConnections directive, make sure the server's port 42424 is NOT open for the Internet, just for your web servers!

2. Make both ASP.NET servers use the state server

Every Web.config file contains a nice configuration directive named "sessionState". So open up your Web.config, and make it look like this:

[code:xml]

<?xml version="1.0"?>
<configuration>
    <system.web>
        <!-- ... -->
        <sessionState
            mode="StateServer"
            stateConnectionString="tcpip=your_server_ip:42424"
            cookieless="false"
            timeout="20" />
        <!-- ... -->
    </system.web>
</configuration>

[/code]

3. So you think you are finished...

...but that's not the case! Our load balancer did a great job, but both servers where returning different session data. We decided to take a look at the session ID in our cookie: it was the same for both machines. Strange!

Some research proved that it was ASP.NET's <machineKey> configuration which was the issue. Both web servers should have the same <machineKey> configuration. Let's edit Web.config one more time:

[code:xml]

<?xml version="1.0"?>
<configuration>
    <system.web>
        <machineKey
          validationKey="1234567890123456789012345678901234567890AAAAAAAAAA"
          decryptionKey="123456789012345678901234567890123456789012345678"
          validation="SHA1"
          decryption="Auto"
        />
        <!-- ... -->
        <sessionState
            mode="StateServer"
            stateConnectionString="tcpip=your_server_ip:42424"
            cookieless="false"
            timeout="20" />
        <!-- ... -->
    </system.web>
</configuration>

[/code]

(more on the machineKey element on MSDN)

Also check MS KB 325056, this was an issue we did not meet, but it might save your day.

4. Great success!

Our solution now works! Only problem left is that we have a new single point of failure (SPOF): the ASP.NET state service. But we might just set up 2 of those and fail over both session service machines.

UPDATE 2008-01-23: Also check out my blog post on Session State Partitioning!

kick it on DotNetKicks.com

PHP and OpenXML - New API project

It's been a while since I posted something related to PHP and OpenXML, but I fixed that glitch in this blog post.

Browsing CodePlex, I found a new PHP OpenXML project named OpenXML API. From what I can see, this project is just in the "Hello world" stage of creating Word documents in PHP, but not so long ago my PHPExcel project was in that stage too...

While I am talking about PHPExcel... Since today, the Subversion repository I host myself is synchronized 2 times a week with CodePlex's Team Foundation Server. If you want the latest source snapshot, please check PHPExcel's source code tab on CodePlex.

Advanced ASP.NET caching events

Currently, I'm giving an ASP.NET classroom training at our company, which actually is quite good for me: I needed to refresh all ASP.NET concepts, as those all fade away slowly when you don't use them for a while... Now, one of those refreshed concepts is ASP.NET's caching.

ASP.NET offers a flexible built-in caching mechanism, providing you with a global "Cache" object in which you can get and put data which needs to be cached for a while. One of the cool things about ASP.NET caching is that it actually listens to what you want: if you need the cache to expire after 10 minutes, it does so. Also, when memory is needed for other actions on the webserver, ASP.NET will gently clean the cache depeding on cache item priorities.

As for many things in this world, every good thing also has a downside... And here's the downside for ASP.NET's caching: when an item is removed from cache, you'll have to know and react to that. No problem, you say, as you can simply use an if-statement to fix things up. Here's a DataDet which will be cached to infinity (or untill memory is needed):

[code:c#]

if (Cache.Get("myDataSet") == null) {
    // Re-fetch data
    // ... DataSet ds = ....

    Cache.Insert(
        "myDataSet", ds, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration
    );
}

[/code]

Great thing! But... What if I want to centralise cache creation? What if I want to log something everytime a cache item has been removed due to memory limits being reached? Luckily, ASP.NET provides an answer to that: the System.Web.Caching.CacheItemRemovedCallback delegate. This delegate can be used to ask ASP.NET to notigy you using a delegate of what is happening inside the cache when something is removed from it. Here's the delegate signature:

[code:c#]

void (string key, Object value, CacheItemRemovedReason reason);

[/code]

As you can see, you can get the key that's being removed, its current value, and the reason why the item is being deleted. These reasons can be: Expired, Removed, Underused, and DependencyChanged. I think these speak for themselves, no?

Now let's implement this: I'll create a CacheRetrievalManager which will update my cache whenever an item is removed from cache:

[code:c#]

using System;
using System.Web.Caching;

public class CacheRetrievalManager
{
    public void RemovedCacheItemHandler(string key, Object value, CacheItemRemovedReason reason)
    {
        switch (key)
        {
            case "myDataSet":
                // call method to re-fetch data and re-set cache
                // ...
                break;
        }
    }
}

[/code]

One thing left is to specify that this method should be called whenever a cache item is removed:

[code:c#]

// Insert in cache ONCE, recreation will be handled by CacheRetrievalManager
DataSet ds = ...;
Cache.Insert(
    "myDataSet", ds, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, CacheRetrievalManager.RemoveCacheItemHandler
);

[/code]

Now I know exactly why something is removed, and that I can even log when this happens. You can now further extend this into separate CacheRetrievalManagers for every object you which to cache, fetch data inside that manager, ...