Pages

Monday, 23 April 2012

Custom WCF Authentication Using Message Contract

When you create a WCF Service or Web Service, you may want to implement subscription based access to this service. This allows you to have many customers consuming the service, with each customer having their own account and credentials to access.

When creating ASP.NET Web Services, implementing subscription based access can be done by using the SOAP header to add credentials particular to the customer consuming the web service. In a WCF service, this is done using the Message Contract.

For this post, I have created a test WCF service application which is used to return account data for a customer. The interface has one operations contract called GetAccountsData which returns a message contract called accounts info. As the accounts info message contract has sensitive information, I am also specifying the EncryptAndSign attribution on the properties on the message contract. 

The Interface:

[ServiceContract]
    public interface IAccountsService
    {
        [OperationContract]
        AccountsInfo GetAccountsData(CustomerCredential cCredential);
    }


The message contract:

    [MessageContract]
    public class AccountsInfo
    {
        [MessageBodyMember(Order = 1, ProtectionLevel = ProtectionLevel.EncryptAndSign)]
        public string CustomerID;

        [MessageBodyMember(Order = 2, ProtectionLevel = ProtectionLevel.EncryptAndSign)]
        public string AccountNumber;

        [MessageBodyMember(Order = 3, ProtectionLevel = ProtectionLevel.EncryptAndSign)]
        public double CurrentBalance;
    }

You'll notice the use of another message contract in the GetAccountsData method - CustomerCredential. This method contract is used to pass authentication information from the client when GetAccountsData is called.

    [MessageContract]
    public class CustomerCredential
    {
        [MessageHeader(ProtectionLevel = ProtectionLevel.EncryptAndSign)]
        public string SecurityToken;

        [MessageBodyMember(Order = 1, ProtectionLevel = ProtectionLevel.EncryptAndSign)]
        public string CustomerID;

    }

I also have a class that implements IAccountsService and contains GetAccountsData. For the purposes of this example, I have hard-coded an authentication token and have continued if the correct token is passed through, however this is where you would make a call to your data store to authenticate the token and validate the client accessing the service.

public AccountsInfo GetAccountsData(CustomerCredential credentials)
        {
            if (!string.IsNullOrEmpty(credentials.SecurityToken))
            {
                if (!credentials.SecurityToken.Equals("7DB8B334-8F1A-4EF0-8586-1FF7C279502F"))
                {
                    throw new FaultException("You are not authorised to access the method");
                }
                else
                {
                    // A database/data store call would be made here to get the data and return based on the customer ID.
                    // You could also do further security checks such as the customer requiring to enter a PIN
                    if (credentials.CustomerID.Equals("C123"))
                    {
                        return new AccountsInfo { CustomerID = "C123", AccountNumber = "987654321", CurrentBalance = 1000 };
                    }
                    else
                    {
                        return new AccountsInfo { CustomerID = "N/A", AccountNumber = "N/A", CurrentBalance = 0 };
                    }
                }
            }
            else
            {
                throw new FaultException("Security information not provided");
            }
        }


To consume this service, I have created an empty MVC3 Web Application which has just one controller returning an action result - I didn't want to overcomplicate for the example, but you should be able to see how this example can be extended and built upon.

The Index ActionResult of my HomeController has the following code:

CustomerAccount account = new CustomerAccount();
            try
            {
                ChannelFactory<IAccountsService> factory = new ChannelFactory<IAccountsService>("WSHttpBinding_IAccountsService", new EndpointAddress("http://localhost:8013/AccountsService.svc"));
                IAccountsService proxy = factory.CreateChannel();
                CustomerCredential credentials = new CustomerCredential();
                credentials.SecurityToken = "7DB8B334-8F1A-4EF0-8586-1FF7C279502F";
                credentials.CustomerID = "C123";
                AccountsInfo accountDetails = proxy.GetAccountsData(credentials);
                
                account.CustomerID = accountDetails.CustomerID;
                account.AccountNumber = accountDetails.AccountNumber;
                account.CurrentBalance = accountDetails.CurrentBalance;

            }
            catch (Exception ex)
            {
                throw ex;
            }

The CustomerAccount is my model, and I am calling my WCF service to return data in the model which is passed to the view.

You'll notice the endpoint of http://localhost:8013 - this is where I published my WCF service and the address I used to add the service reference to my MVC3 application. If you download the attached sample, be sure to either change references to http://localhost:8013 to the URL you published the WCF service to.

To pass in the credentials, I am creating a CustomerCredential object, which we defined in the service. Here I am passing in my hard coded token, however in reality you would issue a token to each client consuming your service for them to pass in at this stage. Your service would then validate the token used as described above.

The test WCF service and MVC3 Web Application used for this post are available from here - www.web-architecture.com/files/WCFSample.zip




Thursday, 19 April 2012

UAC Administrator Access on a Windows Server 2008 Development Server

Windows Server 2008 R2 has UAC build-in and turned on by default. With a server hosting your applications that is exposed to the outside world, you definitely want to keep this feature turned on!


On an internal development server however, you may wish to disable UAC for the administrator users group as with it turned on you will find barriers when testing that can slow things down - one example is changing a value in an exe.config file in the programs directory as you won't be able to save the config file back to the same location without running the editing program in Administrator mode.


To disable UAC for the administrators group, follow these simple steps:


1. On your server, go to "Administrative Tools" > "Local Security Policy"
2. Expand the "Local Policies" > "Security Options" node and find the "User Account Control: Run all administrators in Admin Approval Mode" policy in the main window.



3. Open the policy and select the "Disabled" option. Click OK and reboot your server.



Again, I would stress that you should never contemplate doing this on a production server, even to allow items to be dropped in the GAC. An installer should always be created for adding DLLs to the GAC on a production server in Windows 2008 R2.


Wednesday, 18 April 2012

SQL Server: Insert, Update and Delete Data in a Single Execution by Using MERGE


With the release of SQL Server 2008, insert, update, or delete operations can be performed in a single statement using MERGE. Prior to SQL Server 2008, each insert, update or delete had to be run separately. The MERGE statement allows you to join a data source with a target table or view, and then perform multiple actions against the target based on the results of that join. 


The following example is taken from TechNet, and the original example can be found at http://technet.microsoft.com/en-us/library/bb522522(v=sql.105).aspx This example uses MERGE to insert, update, or delete rows in a target table based on differences with the source data.


Example
==================


Consider a small company with five departments, each with a department manager. The company decides to re-organise its departments. To implement the re-organisation results in the target table dbo.Departments, the MERGE statement must implement the following changes:


  • Some existing departments will not change.
  • Some existing departments will have new managers.
  • Some departments are newly created.
  • Some departments will not exist after the reorganization.

The following code creates the target table dbo.Departments and populates it with managers.


Transact-SQL

USE AdventureWorks2008R2;
GO
IF OBJECT_ID (N'dbo.Departments', N'U') IS NOT NULL 
    DROP TABLE dbo.Departments;
GO
CREATE TABLE dbo.Departments (DeptID tinyint NOT NULL PRIMARY KEY, DeptName nvarchar(30), 
    Manager nvarchar(50));
GO
INSERT INTO dbo.Departments 
    VALUES (1, 'Human Resources', 'Margheim'),(2, 'Sales', 'Byham'), 
           (3, 'Finance', 'Gill'),(4, 'Purchasing', 'Barber'),
           (5, 'Manufacturing', 'Brewer');


The organizational changes to be made to the departments are stored in the source table dbo.Departments_delta. The following code creates and populates this table:


Transact-SQL

USE AdventureWorks2008R2;
GO
IF OBJECT_ID (N'dbo.Departments_delta', N'U') IS NOT NULL 
    DROP TABLE dbo.Departments_delta;
GO
CREATE TABLE dbo.Departments_delta (DeptID tinyint NOT NULL PRIMARY KEY, DeptName nvarchar(30), 
    Manager nvarchar(50));
GO
INSERT INTO dbo.Departments_delta VALUES 
    (1, 'Human Resources', 'Margheim'), (2, 'Sales', 'Erickson'),
    (3 , 'Accounting', 'Varkey'),(4, 'Purchasing', 'Barber'), 
    (6, 'Production', 'Jones'), (7, 'Customer Relations', 'Smith');
GO


Finally, to reflect the company reorganization in the target table, the following code uses the MERGE statement to compare the source table, dbo.Departments_delta, with the target table dbo.Departments. The search condition for this comparison is defined in the ON clause of the statement. Based on the results of the comparison, the following actions are taken.


Departments that exist in both tables are updated in the target table with new names, new managers, or both in table Departments. If there are no changes, nothing is updated. This is accomplished in the WHEN MATCHED THEN clause.


Any departments in Departments_delta that don't exist in Departments are inserted into Departments. This is accomplished in the WHEN NOT MATCHED THEN clause.


Any departments in Departments that do not exist in the source table Departments_delta are deleted from Departments. This is accomplished in the WHEN NOT MATCHED BY SOURCE THEN clause.


Transact-SQL

MERGE dbo.Departments AS d
USING dbo.Departments_delta AS dd
ON (d.DeptID = dd.DeptID)
WHEN MATCHED AND d.Manager <> dd.Manager OR d.DeptName <> dd.DeptName
    THEN UPDATE SET d.Manager = dd.Manager, d.DeptName = dd.DeptName
WHEN NOT MATCHED THEN
    INSERT (DeptID, DeptName, Manager)
        VALUES (dd.DeptID, dd.DeptName, dd.Manager)
WHEN NOT MATCHED BY SOURCE THEN
    DELETE
OUTPUT $action, 
       inserted.DeptID AS SourceDeptID, inserted.DeptName AS SourceDeptName, 
       inserted.Manager AS SourceManager, 
       deleted.DeptID AS TargetDeptID, deleted.DeptName AS TargetDeptName, 
       deleted.Manager AS TargetManager;    


Monday, 16 April 2012

MVC3 Custom 500 Error Handling Inside the MVC Pipeline

In any well designed application, 500 errors are handled. I have seen too many applications that simply throw an exception when an error occurs - at best this gives a bad user experience, but at worst it could expose security information a potential attacker could use to exploit a hole in your application and/or system.

In many applications, particularly enterprise applications, an end user experiencing an error needs to be coupled with a logging process or a notification to a support team depending on the severity of the error. In ASP.NET Web Forms, this can be achieved by using the Application_Error event in global.asax to send a notification or log an event. The user can then be redirected to an error page either by setting the appropriate  CustomErrors properties in web.config or using a redirect in the Application_Error method.

Web.Config Redirect:


    <customErrors mode="Off" defaultRedirect="~/errors/500.aspx">
      <error statusCode="500" redirect="~/errors/500.aspx"/>
    </customErrors>

Global.asax Redirect:


    void Application_Error(object sender, EventArgs e) 
    {
        
        // Code that runs when an unhandled error occurs
        Exception objErr = Server.GetLastError().GetBaseException();
        // Do logging or email notification

        Response.Redirect("/Errors/500.aspx");
    }

Custom Exception Handling


In both Web Forms and MVC3, custom exception handlers can be built which give you a good indication of exactly what a user was doing when an error occurs. A method can then throw a custom exception on error, with customised exception details then being logged or sent to the support team.

A big downside to this approach is the decoupling of the exception from details of what the user was doing EXACTLY when the error occurred. Whether create your own exception handlers or not, your 500 error page may have a form for a user to complete and send a notification to a support team. The decoupling of the application error message and further information from the user occurs due to the timings between the 500 error details and the user sending the support message. This can make troubleshooting and/or problem solving very difficult if you need to know exactly what the user was doing at the time the error occurred - especially important if an error is only thrown when a user enters certain information in a form (for example).

The MVC3 Approach


With MVC3, 500 error handling can be done completely within the MVC pipeline. To do this requires a few steps:


Global.asax


There is a new method in global.asax called RegisterGlobalFilters, and this adds new global filters to your application. Global filters are applied to all actions within your MVC3 application and for this post I am going to add a custom exception hander filter:

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute
            {
                ExceptionType = typeof(CustomError1),
                // CustomError1.cshtml is a view in the Shared folder.
                View = "CustomError1",
                Order = 1
            });
            filters.Add(new HandleErrorAttribute());
        }

The exception type is set to a custom exception handler I have created:

    [Serializable]
    public class CustomError1 : Exception
    {
        public CustomError1()
            : base("Custom error one has been thrown.") { }
    }

The view is a view in the Shared folder, called CustomError1. This view can only have one model, which is the System.Web.Mvc.HandleErrorInfo model:


CustomError1.cshtml


@model System.Web.Mvc.HandleErrorInfo

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <title>CustomError1</title>
</head>
<body>
    <div>
        @Model.Exception.Message
    </div>
</body>
</html>

You will also need to add the following to your web.config file:

<customErrors mode="On"></customErrors>

As you can see, details of the custom exception are available in the model for the CustomError1 view. In this view, I can then create a form which captures user input about the error, combines that with the exception from the model and send a notification/log an event that has all the information needed to troubleshoot in one place.

I hope this post is useful in detailing the power of MVC3 and exception handling, and how this can be used to couple together MVC3 exceptions with user information - your support teams will love you for this!