Thursday, June 27, 2013

How to enforce secure HTTP access to an ASP.NET MVC Application.

To ensure that your actions are called through only Https, you can decorate your action methods with RequireHttps attribute. But this will not work in your development server as usually the development box is not configured with HTTPS. Basically you need to to apply this attribute conditionally based on a configurable item. This can be achieved by creating a custom class that is derived from RequireHttpsAttribute and by overridding its OnAuthorization().


using System;
using System.Configuration;
using System.Web;
using System.Web.Mvc;

public class RequireSslAttribute : RequireHttpsAttribute
    {
        private const string EnableSslKey = "EnableSSLForMySite";

        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentException("filterContext");
            }

            if (!IsHttpContextNull(filterContext) && !IsEnableSsl())
            {
                return;
            }

            BaseOnAuthorization(filterContext);
        }

        protected virtual void BaseOnAuthorization(AuthorizationContext filterContext)
        {
            base.OnAuthorization(filterContext);
        }

        protected virtual bool IsHttpContextNull(AuthorizationContext filterContext)
        {
            return filterContext.HttpContext == null;
        }

        protected virtual bool IsEnableSsl()
        {
            var enableSslForMySite = HttpRuntime.Cache.Get(EnableSslKey);
            bool isEnableSsl = false;

            if (enableSslForMySite == null)
            {
                string configEnableSsl = ConfigurationManager.AppSettings[EnableSslKey] ?? string.Empty;

                isEnableSsl = (configEnableSsl.ToLower() == "true");
                HttpRuntime.Cache.Insert(EnableSslKey, isEnableSsl);
            }
            else
            {
                bool.TryParse(enableSsl.ToString(), out isEnableSsl);
            }

            return isEnableSsl;
        }
    }
BaseController
    [RequireSsl]
    public abstract class BaseController : Controller
    {
    }

Tuesday, June 25, 2013

How to track Browser's Back button to prevent user to visit an already deleted entity.


Solutions:

There are 2 ways by which the user can come back to a deleted entity. 1. By typing the Url in the browser address bar. 2. By clicking on the browser back button just after deleting the entity. While option-1 is already taken care of by redirecting the user to an Add New screen but the second option certainly allows the user to update an entity that is already deleted. So the solutions described here are applicable for option-2 scenario.

Solution – 1: By tracking a deleted flag using a Hidden Field.

The value of a hidden field is retained when you use the browser back/Forward button. So the value of a hidden field can be used to check if the user has clicked the browser back button to reach the current page.

The solution can be implemented by following three simple steps.

  1. A hidden field with default value “0” is added to all UI which provides the provision to delete an entity. This doesn’t work if we add it programmatically. It has to be added in the .cshtml file.
  2. Inside the success callback of the delete method, set the value of the hidden field to “1”.
  3. On document load, check the value of the hidden field. If it is “1” then the current entity is a deleted entity.

Solution – 2: Using History.js

The idea here to use HTML5 History/State API (pushState, getState etc.) to modify the history entries by replacing the current state with a delete state after a delete operation is over. pustState() & replaceState(0 are the two methods used to add/modify the history entries. This changes the referrer that gets used in the HTTP Header for XMLHttpRequest object. Then use the HTML5 onhaschange event to check if the current state of the entity matches with any of the previous deleted state in the History.

During this research it is established that Internet Explorer version 9 or less doesn’t supports HTML5 History/State API. Further research on this lead us to History.js plug-in which gracefully supports HTML5 History/State API in all browsers including the browsers that don’t support HTML5. So the POC is done based on the same idea using the History.js.

Here are the steps to implement this solution.

  1. Add code to modify the history entry of the current state with a deleted state inside the success callback of the delete Ajax call.
  2. Bind a custom function to the statechange event to check if the current state matches with any previous deleted state in history. If a match is found then take necessary action to alert the user about it.

Conclusions:

Based on the ease of development, it seems that Hidden Field approach is the best option available currently with only additional burden of adding a hidden field on all UI that provides the provision to delete an entity except search screens.

Resources:

http://msdn.microsoft.com/en-us/magazine/ff690558.aspx http://benalman.com/code/projects/jquery-hashchange/examples/hashchange/ https://github.com/browserstate/history.js


Altering a SQL Table on Production


Altering a SQL Table to add a column on Production can be risky affair as it contains vital production data in it. The sql script given below gives an idea how to go for it. In the example below, the table is getting altered to add the new column [Column6].

USE <>
GO

BEGIN TRANSACTION
SET QUOTED_IDENTIFIER ON
SET ARITHABORT ON
SET NUMERIC_ROUNDABORT OFF
SET CONCAT_NULL_YIELDS_NULL ON
SET ANSI_NULLS ON
SET ANSI_PADDING ON
SET ANSI_WARNINGS ON
COMMIT

BEGIN TRANSACTION
GO
CREATE TABLE dbo.Tmp_
	(
	[Key] [int] IDENTITY(1,1) NOT NULL,
	[Column1] [int] NOT NULL,
	[Column2] [int] NOT NULL,
	[Column3] [int] NULL,
	[Column4] [int] NOT NULL,
	[Column5] [varchar](50) NULL,
	[Column6] [int] NULL	
	)  ON [PRIMARY]
GO
ALTER TABLE dbo.Tmp_TableName SET (LOCK_ESCALATION = TABLE)
GO
SET IDENTITY_INSERT dbo.Tmp_TableName ON
GO
IF EXISTS(SELECT * FROM dbo.TableName)
	 EXEC('INSERT INTO dbo.Tmp_TableName ([Key],[Column1], [Column2],[Column3], [Column4], [Column5])
		SELECT [Key],[Column1], [Column2],[Column3], [Column4], [Column5] FROM dbo.TableName WITH (HOLDLOCK TABLOCKX)')
GO
SET IDENTITY_INSERT dbo.Tmp_TableName OFF
GO

DROP TABLE dbo.TableName
GO
EXECUTE sp_rename N'dbo.Tmp_TableName', N'TableName', 'OBJECT' 
GO
ALTER TABLE dbo.TableName ADD CONSTRAINT
	PK_TableName PRIMARY KEY CLUSTERED 
	(
	[Key]
	) WITH( STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]

GO

ALTER TABLE [dbo].[TableName]  WITH CHECK ADD  CONSTRAINT [FK_TableName_FKTable1] FOREIGN KEY([Column1])
REFERENCES [dbo].[FKTable1] ([Key])
GO

ALTER TABLE [dbo].[TableName]  WITH CHECK ADD  CONSTRAINT [FK_TableName_FKTable2] FOREIGN KEY([Column2])
REFERENCES [dbo].[FKTable2] ([Key])
GO

ALTER TABLE [dbo].[TableName]  WITH CHECK ADD  CONSTRAINT [FK_TableName_FKTable3] FOREIGN KEY([Column3])
REFERENCES [dbo].[FKTable3] ([Key])
GO

ALTER TABLE [dbo].[TableName]  WITH CHECK ADD  CONSTRAINT [FK_TableName_FKTable14] FOREIGN KEY([Column4])
REFERENCES [dbo].[FKTable4] ([Key])
GO

ALTER TABLE [dbo].[TableName]  WITH CHECK ADD  CONSTRAINT [FK_TableName_Table5] FOREIGN KEY([Column5])
REFERENCES [dbo].[FKTable5] ([Key])
GO


COMMIT



Find & Replace using Regular Expression in SQL Server Management Studio



Example:
Regular Expression to find and replace all occurrences of CAST(0x0000A1D7010C33B5 AS DateTime) in a .sql file using Find/Replace dialog of SQL Server Management Studio.