N-Layered Web Applications with ASP.NET 3.5 Part 6: Security


NOTE: the concepts presented in this article are now considered obsolete possibly because better alternatives are available.

Note: this is the last part in a series of six. If you rather read this entire series off-line, you can buy the series as a convenient PDF document that comes with the full source. Besides the convenience, buying the PDF will also make you feel good as it shows your appreciation for the articles and helps me pay the bills for my server and hosting so I can keep running imar.spaanjaars.com and continue to provide you with great content. For more details, check out this post that shows you how you can buy the entire series right now.

This is part 6 of a six-part series of articles on N-Layer design. This article series builds on top of my three part series on N-Layer design that I released in early 2007. If you haven’t already done so, be sure to check out these articles first, as a lot of code and concepts used in this new series is explained in detail in the older series.

In this last article in the series, I'll deal with security in your N-Layered web application.

After you’ve read the previous series, be sure to check out part one as well as it describes the new application’s architecture. Additionally, you may want to read the earlier parts that deal with Validation, Sorting, Paging, Filtering and concurrency. 

Quick Links to All Articles

Previous Series
Building Layered Web Applications with Microsoft ASP.NET 2.0 - Part 1
Building Layered Web Applications with Microsoft ASP.NET 2.0 - Part 2
Building Layered Web Applications with Microsoft ASP.NET 2.0 - Part 3
Custom Sorting with N-Layer Design Classes and the GridView

Current Series
N-Layered Web Applications with ASP.NET 3.5 Part 1: General Introduction
N-Layered Web Applications with ASP.NET 3.5 Part 2: Introducing the Validation Framework
N-Layered Web Applications with ASP.NET 3.5 Part 3: Advanced Validation Topics
N-Layered Web Applications with ASP.NET 3.5 Part 4: Sorting, Paging and Filtering
N-Layered Web Applications with ASP.NET 3.5 Part 5: Dealing with Concurrency
N-Layered Web Applications with ASP.NET 3.5 Part 6: Security

Security by itself is a major topic on which many books have already been written. This article is not intended to show you all the ins and outs, or do's and don’ts on security, but instead will focus on a few key areas. In particular, I’ll look at the following:

  • Database security
  • URL authorization in ASP.NET
  • Declarative role based security using ASP.NET Membership and the RoleManager
  • Imperative role based security using ASP.NET Membership and the RoleManager

Being an article series on N-Layer design, I’ll touch upon security in all three layers: the UI, the Business Layer and the database. My main focus will be on the business layer though. Many articles have already been written on database security and on ASP.NET URL authorization (where access to the UI is controlled by ASP.NET). Instead, I’ll refer to relevant articles and books where appropriate.

Database Security

Database security is a powerful concept. All modern database systems have security deeply integrated giving you fine control over who can access your system and who can’t. For example, Microsoft SQL Server allows you to control who can access a database, what tables can be read or modified, what stored procedures a user can access and so on. This is a very granular system that even allows you to configure security at the column level of a table (although you usually don’t want to go that route, and use views or stored procedures instead).

This model of security works really well when you know who your users are. For example, consider a desktop application that runs inside an Active Directory domain. Only users with an AD account can access the application and its underlying database. That in turn means you can configure users (or groups) to access specific parts (tables, schemas, stored procedures and so on) to your database.

This, however, turns out to be problematic in a web application. Who are your users in an ASP.NET web site? If you have a popular site, you may have thousands or even hundreds of thousands of users accessing your application. Clearly, it’s not an option to give all of them an Active Directory account to access the database. Instead, you typically configure your ASP.NET web site to run under a specific user account and grant access to that account. On most versions of IIS / ASP.NET, this account is called Network Service (Windows Vista, Server 2003 and 2008) although you’ll also find the ASPNET account.

One problem with this strategy is that the configured account needs the combined permissions of all the users in your system. So, if an anonymous visitor is only allowed to read from the News table (through the web application) but an Editor also needs to be able to create new or change existing news articles, it means that the account used by the web server now needs to be able to read from and write to the News table. So, how do you block your random visitors from messing with your News table? The answer is: URL authorization and Role Based Security in your Business Layer. These mechanisms allow you to block access to the functionality that is able to change the database. If a user is not able to access an ASPX file that writes to the database, you effectively took away the option to alter this table altogether.

I’ll briefly dig into URL Authorization first, followed by a deeper look at Role Based Security.

References

For more information about configuring SQL Server to work with ASP.NET 2.0 and 3.5 applications, check out the following article:

URL Authorization in ASP.NET

URL authorization is probably one of the easiest to implement security mechanisms for ASP.NET web sites. It’s pretty easy to configure (using tools like the Web Site Administration Tool that comes with Visual Web Developer), very visible (you can see the rules that apply in various .config files in your site) and it’s pretty secure as it ships out of the box with ASP.NET and as such requires no manual code from you, other than some initial configuration.

To implement URL authorization, you typically perform the following steps:

  1. Enable ASP.NET Membership and optionally the RoleManager functionality in the web.config file.
  2. Create at least one user and optionally one or more roles.
  3. Control access to one or more files in one or more web.config files.
  4. Provide a means for your users to log in.

Enable ASP.NET Membership and the RoleManager

By default, ASP.NET Membership is already activated. All you need to do to get started with ASP.NET is drag one of the Login controls like the Login box or the CreateUserWizard control on a page, view the page in a browser and try to login in. As soon as you do this, ASP.NET will create a new database for you in the App_Data folder called aspnetdb.mdf which it then configures for Membership. Any new accounts you create using the CreateUserWizard control are stored in the database automatically. Login attempts using the Login control are also executed against this database.
In order to use roles in your application, you need to enable the <roleManager /> by setting its enabled attribute to true.

If you want a bit more control over the various settings of the Membership and Role providers, you need to reconfigure the <membership /> and <roleManager /> elements in web.config. That way, you can override all the configuration settings that are defined by default. The following code snippet shows a typical configuration for these two providers with slightly more relaxed settings than the default (for example, the password requirements have been changed to accept passwords with a minimum length of 5):

	<membership defaultProvider="AspNetSqlMembershipProvider">
  <providers>
    <clear />
    <add
      name="AspNetSqlMembershipProvider"
      type="System.Web.Security.SqlMembershipProvider, 
            System.Web, Version=2.0.3600.0, 
            Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
      connectionStringName="LocalSqlServer"
      applicationName="/"
      requiresUniqueEmail="true"
      passwordFormat="Hashed"
      enablePasswordRetrieval="false"
      enablePasswordReset="true"
      requiresQuestionAndAnswer="false"
      passwordStrengthRegularExpression=""
      minRequiredPasswordLength="5"
      minRequiredNonalphanumericCharacters="0"
      passwordAttemptWindow="10"
      maxInvalidPasswordAttempts="5"
      description="Stores and retrieves membership data from a 
              local Microsoft SQL Server database."
    />
  </providers>
</membership>

<roleManager
  defaultProvider="SqlProvider"
  enabled="true"
  cacheRolesInCookie="true"
  cookieName=".ASPROLES"
  cookieTimeout="30"
  cookiePath="/"
  cookieRequireSSL="false"
  cookieSlidingExpiration="true"
  cookieProtection="All"
> 
  <providers>
    <add
      name="SqlProvider"
      type="System.Web.Security.SqlRoleProvider"
      connectionStringName="LocalSqlServer"
      applicationName="/"
    />
  </providers>
</roleManager>

Once you have the database and configuration in place you’re ready for the next step: creating users and roles inside your database.

If you want to combine your own database tables with those of ASP.NET in a single database, follow these steps:

  1. Optionally, attach your database to SQL Server using SQL Server Management Studio. This allows you to use the GUI version of the aspnet_regsql tool.
     
  2. Run the tool aspnet_regsql from your .NET Framework folder (located at %windir%\Microsoft.NET\Framework\v2.0.50727) against your database. This adds support for ASP.NET features like Membership, Roles and Profile.

    If you want to modify a database you have not attached (for example, a database in the App_Data folder of your web site), execute the tool at the command line like this:
    			aspnet_regsql -A all -C "Data Source=.\SqlExpress;Integrated 
        Security=True;User Instance=True"
        -d "C:\Projects\Spaanjaars.ContactManager.Web\App_Data\aspnetdb.mdf" 
  3. Update the <providers> elements of the configuration for the Membership, Roles and Profile in web.config. You'll need to set the connectionStringName attribute to the name of a connection string in the same web.config that references the database you modified in step two.

  4. Optionally, detach the database from SQL Server again (this is necessary if you use AttachDBFileName attribute in the query string that is used to attach database on the fly.)

References

For more information on security and the aspnet_regsql tool, check out the following references:

Creating Users and Roles using the WSAT or the CreateUserWizard

When you’re in Visual Web Developer, adding new users and roles is very simple. Choose WebSite | ASP.NET Configuration from the main menu to open the Website Administration Tool (the WSAT). From there you can configure security, add roles and new users and then assign your users to the various roles.
Alternatively, you can let new users sign up for an account using the CreateUserWizard control, you can create them programmatically (in your Global.asax for example) and you can manage them using the new IIS manager available on Windows Vista, Windows Server 2008 and later versions of Windows).

References

For more information, check out the following articles:

Control Access to Files in Web.Config

To control access to specific files or folders you can use <location /> elements in the central web.config file or use separate web.config files in sub folder and then use the <authorization /> element. I prefer to centralize stuff in the main web.config file so I typically have rules like this to block access to specific folders for all users except for one or more roles:

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

This blocks access to the Management folder for all users except for those in the Administrators role.

Allow Users to Login

Once you have Membership and Roles setup, the rest is pretty easy. In the most basic form, all you really need to allow a user to login is this code in a page:

	<asp:Login id=”Login1” runat=”server” />

With this control in a page, your users will get a Login control with a user name and password field that allows them to sign in. Based on the roles they have been assigned to, they get access to specific areas of the applications. For example, if you’d log in as an Editor, you still wouldn’t be able to access the /Management folder as it’s only accessible to users in the Administrators role.

For many applications, URL authorization is the way to go. It’s easy to implement and very powerful. IIS 7 and later (available for Windows Vista and Server 2008 at the time of writing) make the concept even more powerful by applying the validation rules to content other than “registered ASP.NET files”, including images, documents and even PHP and ASP files, allowing you to use features like Membership and the RoleManager for these types of content as well.

However, just like database security, this is pretty much an all or nothing solution. You either get access to a specific file or folder, or you don’t. But what if you want adapt your pages to the user who’s logged in? What if you want to remove the Edit button for users who are not in the Editors role? Or what if you want to ensure only Administrators are able to call the Delete method on your AddressManager class? The answer is declarative and imperative Role Based Security, the topics of the next two sections.

References

For more information on integrating ASP.NET and IIS 7, check out the following references:

Declarative Role Based Security

With declarative role based security, you apply attributes to methods or classes that restrict access to a limited set of users or roles. This allows you to stop users from calling these methods, or accessing or constructing instances of a class. Consider the following example:

	[PrincipalPermission(SecurityAction.Demand, Role = "Administrators")]
public class Person
{

}

By applying the PrincipalPermission attribute to the Person class, only administrators are allowed to use this class. If you’re not an administrator, you can’t create a new instance or access any of its shared methods.

While blocking access to the entire class might be useful in some cases, a more common scenario is to block access to certain methods instead. For example, anonymous readers might be allowed to read a specific item, but in order to Insert, Update or Delete an item you have to be an authenticated user. You could rewrite the Manager classes to support this as follows:

	using System.Security.Permissions;
using Spaanjaars.ContactManager.BusinessEntities;

public static class NewAddressManager
{
  public static AddressCollection GetList(int contactPersonId) { … }

  public static Address GetItem(int id) { … }

  [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
  public static int Save(Address myAddress) { … }

  [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
  public static bool Delete(Address myAddress) { }
}

By setting the named parameter Authenticated to true, only logged in users can call these methods. The GetItem and GetList methods have no permission attributes applied and as such can be called by anyone.

Declarative role based security is a powerful concept and pretty easy to apply. All you need to do is add a few attributes to your methods and classes, set some of the parameters, like the users or roles that are allowed to access your code and you’re done. On top of that, it’s a .NET feature, not just an ASP.NET feature. That means you can use the same principles and knowledge in other .NET applications like Windows Forms and Console Applications to enforce security. It also nicely integrates with the standard ASP.NET Membership and Roles features and as such is thus not limited to Windows accounts only. The examples above work out-of-the-box with the standard ASP.NET Membership authentication mechanisms.

Simple and secure as declarative role based security may be, it has one big drawback: a run-time exception is thrown when an unauthorized user is trying to access the protected code. This could have some severe implications for the calling code you write as you’ll need to add a lot of try/catch statements to intercept the exception message. So usually I implement declarative role based security as a last line of defense to stop unauthorized code from calling methods that it’s not allowed to call. This is particularly useful in middle layer code, like the code in the Business and Data Access Layers. For example, when code in those layers is used in a different application – say a Win Forms application – and the developer using the code doesn’t know about the security restrictions, he’ll find out pretty soon what the code allows you to do and what not. This way the business layer code is safe from abuse and it’s up to the developer of the UI to implement the correct authorization mechanisms.

Besides declarative role based security that blocks entire classes and methods, .NET also supports imperative role based security that enables you to secure only parts of your method’s code. After I have discussed imperative role based security in the next section, I’ll show you how to use the ASP.NET Membership and RoleManager features in your ASPX pages to adapt the UI to the user who is currently logged on.

References

Check out the following references to learn more about role based security:

Imperative Role Based Security

Imperative role based security allows you to overcome the problem with the run-time exceptions that are thrown by the declarative model. Rather than blocking entire methods, you can protect specific portions of a method and then wrap the code in a try/catch block to catch any security exceptions. This in turn allows you to choose how to handle the exception. You can wrap the caught exception in one of your own exceptions and forward it to the calling code for example. Or you simply ignore the exception and refuse to perform the requested operation, return null or another value indicating the method didn’t do what it was supposed to do. Finally, it allows you log the exceptions so you can keep track of what attempts have been made to bypass your security.

To implement imperative role based security, you can create an instance of the PrincipalPermission class and then call its Demand method. The following example shows you how to stop users from calling the Delete method in the AddressManager class if they are not an Administrator:

	public static bool Delete(Address myAddress)
{
  try
  {
    PrincipalPermission myPrincipalPermission = 
                  new PrincipalPermission(null, "Administrators");
    myPrincipalPermission.Demand();
    return AddressDB.Delete(myAddress.Id);
  }
  catch (SecurityException)
  {
    // Log error here
    throw;
  }
  return AddressDB.Delete(myAddress.Id);
}

When Demand is called, .NET checks the permissions for the current user. If the user is an Administrator, all is fine and the code continues to call the Delete method on the AddressDB class. But if the user is not an Administrator, Demand raises a SecurityException, effectively stopping the code from calling Delete.

This current implementation has the same effect as its declarative counterpart: it throws a run-time error when the user is not an Administrator. However, because you can wrap the call to Demand in a try/catch block, you can handle the error in any way you see fit.

Besides the Demand method of the PrincipalPermission class has a few other useful methods related to enforcing security. For example, the Intersect and Union methods enable you to create more complex permissions rules where a user needs to match one or more security rules. For more information, check out the links in the following references section:

References

For more information on the PrincipalPermission class, its methods and its underlying interface, check out the following resources:

While declarative and imperative role based security are very useful, they are not always enough to create a secure and usable application. Since they block access to code in such a abrupt way, you need other measures to prevent this code from being called by an unauthorized user in the first place.
Fortunately, this is very easy to do with the ASP.NET Membership and RoleManager features as you’ll see next.

Creating Adaptive UI’s with Membership and RoleManager - Security in the Contact Manager Application

Consider for a moment the Contact Manager Application in the context of security. What would you allow your users to do? The first thing to consider is your user base. Are users from the internet accessing your site? Or does the site run in a controlled environment like a company’s intranet? Depending on the target audience you may need to be more restrictive when it comes to implementing security.

Let’s say that the Contact Manager Application is used in an intranet application or a controlled public internet web site to share information about your contacts throughout your organization. Let’s also say you want to implement the following security features:

  1. Regular (e.g. non authenticated) users can view the contact details, but they are not allowed to add new or change existing records.
  2. Users in the groups Editors and Administrators can add new and edit existing contact people and alter their contact information.
  3. Only Administrators are able to delete contact people from the system.

Sounds like a lot of work? Not so with the ASP.NET membership and role features.

The first thing you need to do is block access to the AddEditContactPerson.aspx page in the root of the site to unauthenticated users. This is easy with .NET’s URL authorization. All you need to do is add a <location /> element to your web.config file like this:

	<location path="AddEditContactPerson.aspx">
  <system.web>
    <authorization>
      <deny users="?"/>
    </authorization>
  </system.web>
</location>

As soon as an unauthenticated users (a user who hasn’t logged in to the system yet) tries to access this URL, she’s taken to the Login.aspx where she’s asked for her credentials.

Note: Rather than specifying a gazillion <location /> elements for each and every page in your application, it’s a good practice to combine related pages in one or more directories, and configure access for those directories instead.

The next step is hiding the Create new Contact Person button that appears at the top of the Default.aspx page. You can do this with a single line of code in the Page_Load event handler in the Code Behind of Default.aspx:

	protected void Page_Load(object sender, EventArgs e)
{
  btnNew.Visible = Membership.GetUser() != null;
}

The GetUser method without arguments of the Membership class returns null when the current request is not associated with a logged in user. So the expression Membership.GetUser() != null returns false for non authenticated users and thus the button btnNew is hidden. Once you’re logged in, GetUser() returns a valid user and the button is visible again.

To comply with the second and the last requirement (only Editors and Administrators can edit and only Administrators can delete records) you need to hook into the RowCreated event of the GridView, find the Edit and Delete LinkButton controls in the GridView and hide or show them depending on the user’s group membership. To make it easy to find the LinkButton controls, I converted both columns in the GridView for editing and deleting to a TemplateColumn. By doing that, you can give each LinkButton a unique name, making it easier to find the control in Code Behind. The two columns for the LinkButton controls now look like this:

	<asp:TemplateField ShowHeader="False">
  <ItemTemplate>
    <asp:LinkButton ID="lnkEdit" runat="server" CausesValidation="false" 
                 CommandName="Edit" Text="Edit"></asp:LinkButton>
  </ItemTemplate>

</asp:TemplateField>
<asp:TemplateField ShowHeader="False">
  <ItemTemplate>
    <asp:LinkButton ID="lnkDelete" runat="server" CausesValidation="False" 
             CommandName="Delete" Text="Delete" 
             OnClientClick="return confirm('Are you sure you want 
                                      to delete this contact person?');">
    </asp:LinkButton>
  </ItemTemplate>
</asp:TemplateField>

With this code, it’s now easy to find the LinkButton controls and determine whether they should be visible or not:

	protected void gvContactPersons_RowCreated(object sender, GridViewRowEventArgs e)
{
  if (e.Row.RowType == DataControlRowType.DataRow)
  {
    bool userIsAdmin = Roles.IsUserInRole("Administrators");
    bool userIsEditor = Roles.IsUserInRole("Editors");
    LinkButton editButton = e.Row.FindControl("lnkEdit") as LinkButton;
    if (editButton != null)
    {
      editButton.Visible = userIsAdmin || userIsEditor;
    }
    LinkButton deleteButton = e.Row.FindControl("lnkDelete") as LinkButton;
    if (deleteButton != null)
    {
      deleteButton.Visible = userIsAdmin;
    }
  }
}

This code first checks whether the current row is a DataRow (and not a Header or a Footer for example). It then determines whether the current user is an Administrator and / or an Editor. It does this by calling Roles.IsUserInRole and passing in the name of the role (note: to make it easier to maintain this application, you shouldn’t use hard coded values like this; instead you could define the role names in a file with constants, or add them to the application’s configuration file). The Roles class is part of the ASP.NET RoleManager and provides static methods like IsUserInRole to determine the role membership of the current user. Naturally, when the user is not even logged in, this method returns false. Otherwise, it checks whether the user is assigned to the role specified.

The final part of the code finds the Edit and Delete LinkButton controls and then determines their visibility based in the user’s group membership.

Security in the Contact Manager Application

I put most of the security implementation in the Contact Manager Application for demo purposes only. If you look at the application that comes with the article, you’ll find security in the classes and ASPX files as discussed in this article. If you want to see the sample code at work, check out the following files and classes:

  1. The ContactPersonManager class in the Spaanjaars.ContactManager.Bll  project that uses PrincipalPermission.Demand to stop non administrators from deleting contact people.
  2. Default.aspx in the web application where the Create new Contact Person button is hidden in Page_Load.
  3. Default.aspx where the Edit and Delete links are hidden during the RowCreated event of the rows in the GridView that displays the contact people.

Of course you can use the exact same principles again to secure other parts of your application, including creating new contact data, deleting addresses and so on.

Summary and Wrapping Up

Security in Web Applications is a very large topic. What I have shown you is just the beginning. There’s a lot more that you need to be aware of in order to create secure and yet accessible applications.

To learn more about security in your ASP.NET Applications, consider getting a copy of the following books:

Additionally, if you’re new to security in ASP.NET in general, and need to read up on the security features of ASP.NET including the controls like Login, CreateUserWizard, ChangePassword and so on, pick up a copy of my book Beginning ASP.NET 3.5 in C# and VB.NET and check out chapters 15 and 16.

With security, you have also come to the end of the article series on N-Layer design. Over the past six articles I covered a lot of different topics regarding N-Layer design, including:

  1. The modified architecture based on my V1 article series.
  2. The new project layout using multiple class library projects which is now a lot easier to implement since Visual Web Developer supports class library projects.
  3. The Validation Framework including the base classes that your entities can inherit from and the validation attributes that you can write yourself to enhance the set of validation rules.
  4. Sorting, paging and filtering at different levels of the application: in the UI, the Business Layer and at the database. You’ve seen how to implement these concepts in the Business Layer and at the database and you’ve seen a discussion of advantages and disadvantages of each.
  5. In part five of this series I showed you how to deal with concurrency issues to avoid two or more users from updating the same data at the same time.
  6. The series closed with an introduction to security in ASP.NET: you saw how to implement URL authorization and how to implement declarative and imperative role based security. These concepts allow you to create a fine-grained, flexible and yet very secure web site where you control who can access what.

Just as with the previous article series, I have covered a lot of topics. However, I also skipped many other topics that might be relevant in the scope of this N-Layer design article series. Feel something important is missing? Have an idea for a change in implementation? Make yourself heard by using the Talk Back feature below.

Downloads


Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Sunday, March 08, 2009 5:21:37 PM Shuaib said:
Greattttttttttttt article again!!!
Thanks ALOT!!!
On Monday, March 09, 2009 4:56:59 PM Jeff said:
Imar,
In your portion on Declaraitive Role based syntax you show that you can protect the Delete method like so:[br /]
[PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
public static bool Delete(Address myAddress) { }
[br /][br /]
If I wanted to further restrict access to the Delete method so that only administrators have permissions to it would I do that like this:[br /]
[PrincipalPermission(SecurityAction.Demand, Role = "Administrators" Authenticated = true)]
  public static bool Delete(Address myAddress) { }
On Tuesday, March 10, 2009 5:19:21 PM Imar Spaanjaars said:
Hi Jeff,

You don't need "Authenticated = true"; you cannot be an administrator if you're not authenticated. So this should do it:

[PrincipalPermission(SecurityAction.Demand, Role="Administrators")]

Hope this helps,

Imar
On Thursday, March 12, 2009 6:22:26 PM Shuaib said:
Hi Imar,
I have build an application with exact same desin as you N-Layer articles. Now this is a WebService application where several sties connect to get data the same data from this WebService passing SiteID (each site has different promotions and Users). This part works fine. The only item I need advise with is, the Membership. The consuming sites do not have any DB. So all data is stored in this WebService. I can add [WebMethod] to enable ASP.NET Membership as a web Service but how can I take care of the "application" values for the Membership? Let's say  user "simson" signs up with www.site1.com and the same user sings up with www.site2.com. Both records go to the same ASPNETDB. How to handle this? Any suggestion?  
On Thursday, March 12, 2009 9:21:17 PM Shuaib said:
Hi Imar,
I got the solution for it. I created a custom Membership which inherits from MembershipProvider. I have set the ApplicationName as property and set that in the Webservice call. :-)
If you can suggest a better solution please let me knwo.

Thanks
On Friday, March 13, 2009 7:39:40 PM Imar Spaanjaars said:
Hi Shuaib,

Yes, that's how you typically do it. By synchronising the applicationName both sites effectively use the same database.

Cheers,

Imar
On Monday, March 16, 2009 5:10:30 PM Andrew said:
Hi Imar,

I really enjoyed your 2.0 articles so I am looking forward to reading through the 3.5 articles (I have read some already).  I have also downloaded the code and got it up and running.  I did need to fiddle around a bit to get the aspnet security working correctly( I needed to remove the LocalSqlServer connection string and then add my own LocalSqlServer connection string that pointed at my aspnetDB).

Now that the site is up and running and I have had a bit of a browse around I thought I'd give some feedback.

1) I seem to be getting some sort of error in the Ajax XMLHttp code when I enter invalid email addresses or phone numbers, I don't know if this has something to do with my setup or if this is a bug in your code.

2) You have stuck to using the same embedded calendar from the 2.0 Contacts application.  I know that this is just a sample application to demonstrate how you may go about putting together an N-Layer application, but it would have been great to see the calendar replaced with either the Ajax extensions calendar or maybe even the JQuery calendar.  Both have features to allow you to quickly navigate to dates some years back which makes sense when you are speaking in terms of birth dates.

3) It can be a  little confusing as to what you are looking at if you click to add say a new email address, and then click on the pager and the add email address button is still visible.

Andrew
On Monday, March 16, 2009 5:33:40 PM Imar Spaanjaars said:
Hi Andrew,

1) Yes correct. There is no validation for these types; to keep the code clean I haven't added validation code so you just get errors. You can remove the AJAX panels to get better error descriptions so you can add the necessary validation code.

2) As you point out; it's about N-Layers, not fancy UI stuff. There are already many articles available on AJAX calendars so I didn't want to clutter up the code with an AJAX framework or extenders.

3) Right. Would probably a good idea to hide the button on paging. Again, not an N-Layer thing, but worth fixing for a next release.

Cheers,

Imar
On Tuesday, April 14, 2009 1:42:51 AM CW said:
Imar, this was an amazing resource. I've been struggling to find a way to add my own Membership/Role provider for days now. I only wish I had found your guide sooner!

Thank you so much!!!
On Friday, April 17, 2009 8:24:50 AM gargamel said:
great article.
This is great :[PrincipalPermission(SecurityAction.Demand, Role="Administrators")]
but it has a little problem.It doesn't work like this:
[PrincipalPermission(SecurityAction.Demand, Role="Administrators,AnotherRole")]
My solution was this:
public class MyClass
{
  public ContractList MyMethod(string param)
        {
            ContractList response = null;try
            {
                PrincipalPermissionHelper.DemandRoles(AppRoles.MyMethodRoles);
                response = DataLayerHelper.INSTANCE.GetContracts(param);
            }
            catch (Exception ex)
            {
                if (ex is SecurityException)
                {
                    throw new BusinessLayerException("securityExceptionMsg");
                }bool rethrow = ExceptionPolicy.HandleException(ex, ExceptionPolicies.BusinessLayer);
                if (rethrow) throw;
            }
            return response;
        }
    }
    and

    public static class AppRoles
      {
          public static readonly string MyMethodRoles = "Role1,Role2";
      ...
        
      }
    public sealed class PrincipalPermissionHelper
    {
      public static void DemandRoles(string rolesName)
          {
              char[] splitter = { ',' };
              
              string[] arrRoles = rolesName.Split(splitter);
              if (arrRoles.Length > 0)
              {
                  PrincipalPermission permission = new PrincipalPermission(null, arrRoles[0]);
                  for (int i = 1; i < arrRoles.Length; i++)
                  {
                      permission = (PrincipalPermission)permission.Union(new PrincipalPermission(null, arrRoles[i]));
                  }

                  permission.Demand();
              }
          }
    }

I think this is the most convenience way to deal with roles, have them in one static class to change them easier and not look in the entire code for a change.Hope this helps anybody.
On Friday, April 17, 2009 8:30:28 AM gargamel said:
sorry for the text formating...imar can you please edit it a little so it can be easier to read?Thanks
On Friday, April 17, 2009 8:54:21 AM Mark Pawelek said:
I downloaded the source code and I was puzzled why you used a 'web site' project rather than a 'web application'.
On Friday, April 17, 2009 6:10:09 PM Imar Spaanjaars said:
Hi gargamel,

Looks like a useful solution (although I haven't tested it).

Note that for an AND condition, you can apply the attribute twice:

[PrincipalPermission(SecurityAction.Demand, Role="Administrators")]
[PrincipalPermission(SecurityAction.Demand, Role="SomeOtherRole")]


Cheers,

Imar
On Friday, April 17, 2009 6:12:13 PM Imar Spaanjaars said:
Hi Mark,

Why not? The concepts work in both Web Site Projects as in Web Application Projects. I initially started the previous article series targeting the VWD Express Edition which at the time didn't support WAPs.

BTW: the VB version *is* a Web Application Project so you can have your pick.

Cheers,

Imar
On Friday, April 17, 2009 8:11:52 PM Michael said:
What if you don't use the Membership database for users and roles, aka you are trying to retrofit some of these theories into a system that already has users and roles?  Would you have to roll your own membership provider and security principal?
On Friday, April 17, 2009 8:18:50 PM Imar Spaanjaars said:
Hi Michael,

Not necessarily as these concepts are supported in Windows authentication or FormsAuthentication scenarios as well. ,

For an existing user database, writing a custom provider isn't too hard:
http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=404

Cheers,

Imar
On Saturday, April 18, 2009 4:26:18 AM kyawoo said:
I am learning in asp.net. So I need to some projects web site.
Can you send you me some projects or sample examples.
Thanks.
Mr.Kyawoo
On Saturday, April 18, 2009 7:06:19 AM Imar Spaanjaars said:
Hi there,

Check out my books "Beginning ASP.NET 3.5 in C# and VB" and "ASP.NET 2.0 Instant Results". Both books feature sample applications; in the case of the Instant Results book they ship with the book on a CD.

http://imar.spaanjaars.com/AboutMyBooks.aspx?aboutitem=booksoverview

Cheers,

Imar
On Tuesday, April 21, 2009 7:19:09 AM gargamel said:
"you can apply the attribute twice"
I knew that but it is good that you mentioned it. As I told you earlier I like to have the roles in one static class or a properties file, because I think it is easier to modify it. But anyway we must keep all the options... and again very good article. keep up the good work!!!
On Friday, June 26, 2009 11:59:37 AM paulm said:
Imar,

Fantastic series!!

You mentioned using partial classes to avoid modifying the generated code,  what would you recommend for adding imperative roles and validation attributes outwith the generated code.  I have looked at partial methods but these seem to restrict you to void as method type whereas the delete for example returns bool.

Is System.ComponentModel.DataAnnotations worth a look?

Paul
On Saturday, June 27, 2009 9:47:34 AM Imar Spaanjaars said:
Hi paulm,

Unfortunately, you can't apply attributes in a partial class to code elements defined in another partial class. You have to apply them directly on top of the code element in the same file. This is something that Dynamic Data is struggling with as well. They came up with the MetadataType attribute in the System.ComponentModel.DataAnnotations namespace you mentioned (http://www.dotnetattributes.com/System/ComponentModel/DataAnnotations/MetadataTypeAttribute).

However, you cannot use this directly; you'll need to rewrite the Validation mechanism in order to support this. You could look at the type being validated, look for the MetadataType attribute, and then access the validation attributes of this Meta data class.

Would be interesting to see if this works; haven't tried it myself, but please post back here if you can make it work.

Cheers,

Imar
On Monday, June 29, 2009 9:39:04 AM paulm said:
Imar, thanks for your prompt and detailed reply.

I'm not going to go there and even try and modify your validation classes.

I think the way forward for me is via this article:

http://www.codeproject.com/KB/database/dbdriventemplates.aspx?display=Print

and to use extended properties in sql server to add key pairs like Sec_Save: AddressEditor, Sec_Delete: AddressAdmin to define the security role in the tables properties for BLL and Val_RequiredField: Name is Required! on columns to define validation attribute and message in the BusinessEntities.

This guy also touched upon a headache of plural table names mapped to singular class names which he has a solution for with these properties.
On Tuesday, November 03, 2009 10:06:02 AM mohammad janmohammadi said:
Thanks for very informative document. I see that roles are hard-coded and can not be dynamic. Let's assume I have a web site that allows the Admin role to define other roles, by grouping a bunch of permissions from a fixed list. In this case Admin selects several permissions and sets for each "No-Access,Read-only,Read&write,Remove" attributes. In this way he defines new roles in the system. Therefore these roles should be mapped to page access (fully & partial). How this can be implemented. May I name it Dynamic Role-based Security?
On Tuesday, November 03, 2009 10:27:08 AM Imar Spaanjaars said:
Hi mohammad ,

Take a look at the post from gargamel on 4/17/2009 10:24:50 AM. Maybe you can use that for dynamic roles.

Cheers,

Imar
On Tuesday, November 24, 2009 7:37:27 PM CJ said:
Id like to bypass having users create a user account and password.  Instead, I would like to have a list of windows ids (domain\userid) that can access the site.  Should I keep this list in the webconfig or aspnetdb?  

Whats the best way to do this and still be able to take advantage of all the asp.net security features?  Id like to avoid having to check the windowsid against a feature list or page list if possible, and I would like to be able to limit access to classes and methods like you have done above.

Any ideas would be appreciated.  Thanks!
On Wednesday, November 25, 2009 7:32:54 AM Imar Spaanjaars said:
Hi CJ,

You can take a look here: http://imar.spaanjaars.com/QuickDocId.aspx?quickdoc=409 to see how to modify the Login control to show a list of users. How you build up that list is up to you.

You can then use FormsAuthentication.RedirectFromLoginPage to set a cookie for the selected user and log him in: http://msdn.microsoft.com/en-us/library/ka5ffkce.aspx

Hope this helps,

Imar
On Wednesday, November 25, 2009 3:37:41 PM CJ said:
Thanks for the article suggestion.  Easier than I would have thought.  Based on your experience, how feasible do you think it would be for me to:

1.  Add users to the aspnetdb (domain\userid) based on a selection of our AD users.
2.  Use Page.User.Identity.Name when the default.aspx is loaded to capture the domain\userid.
3.  Compare this to the users in the aspnetdb and if there is a match, then use FormsAuthentication.RedirectFromLoginPage to set a cookie for the selected user and log him in.

Im essentially trying to avoid having any form of password to remember.  I want it to be as seamless as possible for the user.  

Thanks for the great articles!
On Wednesday, November 25, 2009 3:48:41 PM Imar Spaanjaars said:
Hi CJ,

Feasible, and easy.

But maybe the Active Directory Membership Provider is even better?

http://blogs.msdn.com/gduthie/archive/2005/08/17/452905.aspx

Cheers,

Imar
On Friday, December 11, 2009 1:32:29 PM vijay bhaskar said:
Hi Imar Spaanjaars,

The articles which you posted for Building projects for N Tier Web Applications are really very nice. I have got an idea and conident enough to design a project on my own.

It would be very nice if you share the information for building projects using Linq to SQL and Linq to Entity and Linq to XML.

On Monday, March 29, 2010 6:38:08 AM Thorsten said:
Hi Imar,

great series!
But one thing is a little puzzling for me.
In your example you use the PrincipalPermisson class from the namespace System.Security.Permissions in your BLL. That makes total sense.
Now in my application I also have to use the Membership class in my BLL. Because the DAL returns a Membership object when I request a login account. But the Membership class resides in the System.Web.Security namespace, which IMO sounds as if it is supposed to be used only in the presentation layer.
Why did MS put it into the Web.Security namespace? Does it mean I can't use it for Windows applications?
Do you usually use the Membership object in your BLL or do you create your own custom Membership entity for the BL?

Thanks for your help
On Monday, March 29, 2010 6:53:26 AM Imar Spaanjaars said:
Hi Thorsten,

You can certainly use Membership in Win Forms applications. In short, here's what you need to do:

1. Reference the System.Web or System.Web.ApplicationServices (.NET 4) assembly

2. Copy the relevant config information to app.config

Here's an article that explains this in a bit more detail:

http://devpinoy.org/blogs/comgen/archive/2007/08/15/use-membership-api-in-winforms.aspx

Also, Google has more on this: http://www.google.com/#hl=en&source=hp&q=use+membership+winforms+application Other than the awkward feeling you get when referencing something like System.Web in a Win Forms application, this should work.

Cheers,

Imar
On Thursday, May 06, 2010 12:14:37 PM kurt schroeder said:
I'm reposting to be sure to get comments on this series!!
On Thursday, November 04, 2010 6:59:22 PM E. Brito said:
I just found this series and it's just awesome.
Thanks a lot.
One question though.
I downloaded the source code and the pdf is not included.
How do I get the article in pdf? I got I link but it just the table of content.
On Thursday, November 04, 2010 7:40:18 PM Imar Spaanjaars said:
Hi Brito,

You can buy the PDF using PayPal as described here: http://imar.spaanjaars.com/482/first-part-of-new-article-series-on-n-layer-design-in-aspnet-35-published

Cheers,

Imar
On Thursday, September 29, 2011 11:43:44 AM Andrew Hunt said:
I have found your articles well presented and very informative. I will be using some of the methodology and implementation in my projects.  Just  a note security - use application pools linked to a minimum user with hardly any rights on the server then use that same user on the server DB with no rights only to execute the SPROCS you want them to use.
On Saturday, February 04, 2012 1:12:55 PM Faizan said:
Hi Imaar,

I have found both of the series great :) Thumbs up for effort!

Now, next step, I wish to integrate 'workflow' with in my application built on your guidelines given in these articles.

Can you give your word of wisdom which approach to use? Custom Workflow framework or Windows Workflow Foundation?

Regards,
Faizan
On Saturday, February 04, 2012 1:34:47 PM Imar Spaanjaars said:
Hi Faizan,

I woud go with Windows Workflow Foundation as it nicely integrates with Visual Studio and provides a flexible and powerful framework.

Cheers,

Imar
On Monday, March 12, 2012 1:39:17 PM Faizan said:
Imar,

I'm loving using your suggested framework to develop applications. I've built 2 sample applications so far to master the approach. It is working extremely well in both of the cases.

Which are:

1. Web-based Asset Management & Procurement System
2. Desktop Version of Human Resource Management System

I'm using Collection.List[BusinessObject]  instead of DataTables and found it nice experience. List's robust Find() method made search extremely easy. In the next step

In both projects, I'm wondering to maintain log of every activity of user to enable the application to provide audit and control feature.

Can you please suggest any other approach than using triggers on database layer?

Regards,
Faizan
On Monday, March 12, 2012 2:13:51 PM Imar Spaanjaars said:
Hi Faizan,

You can use SQL Server Audit:

http://www.bradmcgehee.com/2010/03/an-introduction-to-sql-server-2008-audit/
https://www.google.com/#hl=en&output=search&sclient=psy-ab&q=audit+SQL+Server

Alternatively, you could log stuff from the business / data layer, or from within your procedures.

Cheers,

Imar
On Sunday, April 15, 2012 6:48:01 AM Faizan said:
Hi Imar,

I am planning to use L2S as Data Access Layer for the demo applications that I have designed using the principles your have shared in your article series. It takes lot of time to write each class one by one along with their properties.

What kind of challenges are most expected while using the similar approach on various applications?

How much flexibility does it provide to extend the framework based on various Lines of Business applications?

What strategy you would recommend while reconciling changes from database back to the LTS Classes?

What is your suggestion on using EF instead of L2S?

Cheers,
Faizan



On Sunday, April 15, 2012 8:02:20 AM Imar Spaanjaars said:
Hi Faizan,

If I were you, I would look into Entity Framework Code First (http://www.codeguru.com/csharp/article.php/c19233/Introduction-to-Entity-Framework-Code-First.htm). Using Code First, you're free to design your own classes (as in my model) without the need for a lot of manual code.

I am working on a new article series on N-Layer design using Code First; ETA unknown.

Cheers,

Imar

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.