jump to navigation

ASP.Net Windows Authentication: Authorization by Group January 20, 2011

Posted by codinglifestyle in ASP.NET, C#, CodeProject, Security.
Tags: , , , , , , , ,
4 comments

Often in this line of work it’s the simple things that take the longest time. A seemingly simple question came up yesterday on how to lock access to a customer’s website to a specific Windows group. There are a couple of simple gotchas worth documenting in a relatively simple solution presented below.

First, let’s start with the basics. By default ASP.NET executes code using a fixed account. Assuming you are using IIS 6 or greater, the identity is specified in the application pool. However, if we set impersonation to true ASP.NET assumes the user’s identity. Combined with Windows authentication, our code will run within the context of the user’s Windows identity.

To achieve this, in the web.config we set authentication to Windows and impersonate to true. Now we will have an authenticated Windows user, we next need to focus on authorization or what rights and restrictions apply to that user. Our requirement in this case is simple, if you belong to a specified Windows group you have access, otherwise you do not. When using Windows authentication, roles within ASP.NET translate to Windows groups. To allow a specific Windows group, allow that role within the authorization tag in the web.config. We could add additional lines to allow further roles or users. In this case, we simply want to deny everyone else, so notice the deny users * wildcard below.

Web.config

<system.web>
<authentication mode=Windows/>
<identity impersonate=true/>
<authorization>
<allow roles=BUILTIN\Administrators/>
<deny users=*/>
</authorization>
</system.web>

Here’s a handy tip: When testing use the whoami command. This will show you all the groups the logged in user belongs to which is handy when testing.

If you are making modifications to local or domain groups for the current user (probably your own account for testing) ensure that you see the group information you expect with the whoami /groups command. If you have just added yourself to a test group, remember that you must logout and log back in to see these changes.

Now you could stop here, you’re website is secured. However, if you do the user will get IIS’s rather nasty 401 error page. It would be much nicer to show our own custom error or possibly redirect the user to a registration page of some sort. The problem is we’ve restricted the entire website, so even a harmless error page requires authorization. What we need is an exception, which we can do be adding the snippet below the system.web closing tag.

<location path=AccessDenied.aspx>
<system.web>
<authorization>
<allow users=*/>
</authorization>
</system.web>
</location>

What this has done is specify a specific file to have different authorization requirements to the rest of the website.  Optionally we could have specified a directory where we can place CSS, image files, a masterpage, or other resources we may want to allow access to.  In this example, we are only allowing all users to see the AccessDenied.aspx page.

You might think that using the customErrors section in the web.config would be the last step to redirect the user to the AccessDenied.aspx page. However, IIS has other ideas!  IIS catches the 401 before it ever consults with ASP.Net and therefore ignores your customErrors section for a 401. Use the below workaround in your global.asax.cs to catch the 401 and redirect the user to our error page before IIS has a chance to interfere.

Global.asax.cs

protected void Application_EndRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.Response.Status.StartsWith(“401”))
{
HttpContext.Current.Response.ClearContent();
Server.Execute(AccessDenied.aspx);
}
}

If we have further security requirements, we can do further checks any time by getting the IPrincipal from Page.User or HttpContext.Current.User. We could enable a button in our UI with a simple statement such as:

adminButton.Enabled = Page.User.IsInRole(@“domain\domainadmins”);

While the solution seems obvious and elegant, it took a bit of searching and playing to get it to work just right. There are other ways of centrally enforcing security such as Application_AuthenticateRequest in global.asax. However in this case it is far more configurable to take full advantage of the settings available to us in the web.config.

SqlDataSource: Getting @@Identity after Insert August 20, 2007

Posted by codinglifestyle in ASP.NET, C#.
Tags: , ,
3 comments

Yesterday I was developing a simple form using a SqlDataSource to INSERT or UPDATE when saving the form.  The form consisted of a few controls and a button.  In the case of a new item, on clicking the save button I am executing SqlDataSource1.Insert().  For an existing item, I’m using an “ID” parameter on the query string which in turn is used by the UPDATE command.  So I need the ID of the row I’ve just inserted to allow the user to update the form.  I should mention my table has a column called “ID” of type int which is specified as the auto-incrementing identity column. 

 

I put together the form in less than 30 minutes and then spent twice that time trying to find the solution you are reading now.  I was sorely tempted to ditch the DataSource as this is straightforward when you’re executing your SQL commands directly in the code-behind.  But I was curious and persevered until I found the answer:

 

Firstly, let me describe how to set up the DataSource to do the insert.  The InsertCommandType is “Text”.  For the InsertQuery click the button to open the Command and Parameter dialog.  Use the query builder, if you like, to create your insert statement.  After the insert statement append “SET @Identity = @@Identity;” so our insert command looks something like this:

 

INSERT INTO TestTable (TestCol) VALUES (@TestValue); SET @Identity = @@Identity;

 

The statement above has 2 parameters.  One is TestValue we’re setting in to TestCol.  The other is Identity which will be the ID of the column we insert.  Click Add Parameter and type “TestValue”, Parameter source is control, and ControlID is a textbox.  Add another parameter called “Identity”.  This time click Show Advanced Properties, set Direction to “Output” and Type to “int” (leaving Parameter source as “None”). 

 

Now the trick is catching the DataSource at the right time so that our output parameter will have a value.  Don’t mistake the DataSource.InputParameters as the place to look as this merely describes the parameters we’ve configured above.  To actually see the parameters after execution you need to catch the OnInserted event.  Here we have SqlDataSourceStatusEventArgs which contains the command and its parameters after the command has executed.

 

    protected void SqlDataSource1_Inserted(object sender, SqlDataSourceStatusEventArgs e)

    {

        //Read the value of the @Identity OUTPUT parameter

        string sID = e.Command.Parameters[“@Identity”].Value.ToString();

 

        //Display new ID

        Label1.Text = sID;

    }

 

So with the ID known I can now redirect the user to “page.aspx?ID=” + sID such that they may update the form.