In previous DotNetNuke releases, the framework offered only a single security solution; the formsbased security that was included with the core release. That security worked well, but it limited the capability to implement DotNetNuke in a way that tightly integrates with other security mechanisms. Third-party developers provided enhancements that allow Windows authentication to be used, and DotNetNuke 4.0 supports Windows authentication directly in the core release.
Examining how security works in ASP.NET 2.0 will help you understand the challenges that the Core Team faced in implementing the Membership API, as well as the finer details of how security works in DotNetNuke today.
In ASP.NET 1.x, the native authentication and authorization services relied on external data stores or configuration in the web.config file. For example, in ASP.NET 1.1, an application can provide formsbased authentication. This requires the developer to create a login form and associated controls to acquire, validate, and manage user credentials. After authenticatation, authorization is provided through XML configurations in the web.config file.
In ASP.NET 2.0, the introduction of several new security enhancements expands on these services in three distinct ways:
Login and user controls: A new suite of login and user controls provides plenty of functionality out-of-the-box, reducing the need for each application to provide its own login and user controls. For example, it is easy to generate a set of pages for registering a new user, allowing an existing user to log in, and even handling forgotten passwords by simply placing the appropriate controls on a page and setting a few properties.
User management: ASP.NET 2.0 provides a configuration interface for each application that allows for easy management of the application. One feature of the configuration interface is the capability to manage security for the application. For example, you can easily create a new user and a new role and then add the user to the role, all within the ASP.NET 2.0 native configuration interface. As an alternative, security can be managed by writing a custom management tool to access the same functionality programmatically.
Membership Provider: This new provider is the conduit between the Presentation Layer (specifically the login/user controls and the configuration interface) and the persistence mechanism. It encapsulates all of the data access code required to manage users and roles.
Together these three components reduce the amount of code that is required to provide authentication and authorization services and persist the data to a data store.
To build an application that fully supports authentication in ASP.NET 2.0, the Membership Provider has to be integrated into the application. There are several benefits to using this provider in an application. First, it can reduce the amount of code that is written to bring these services to the application. This is true as long as the business requirements fall within the functionality that the default Membership Provider supplies. Second, implementing the Membership Provider promotes developer and consumer confidence because Microsoft has taken on the responsibility of ensuring that its provider follows optimal security standards and has been subjected to rigorous threat modeling and penetration testing.
When the Membership/Roles Provider was first introduced in DotNetNuke 3.0, it leveraged a backported version of this component created by Microsoft because ASP.NET 2.0 had not yet been released. That version conformed to the same API found in ASP.NET 2.0, except that it ran in ASP.NET 1.1. This was a great addition to DotNetNuke for many reasons, but one key benefit is that it allowed DotNetNuke to conform to several ASP.NET 2.0 specifications even before ASP.NET 2.0 was released. DotNetNuke version 4.0 uses the ASP.NET 2.0 Membership/Role Provider.
Security in DotNetNuke 4.0 was implemented with quite a bit of forward thinking. It combines the best features of prior versions of DotNetNuke with the features of the ASP.NET 2.0 Membership Provider. The result is an extensible security model that aligns DotNetNuke closely with best-practice security models in ASP.NET 2.0.
DotNetNuke supports running many portals from a single DotNetNuke installation. Each portal has its own users and roles that are not shared with other portals. A portal is identified by a unique key: the PortalID.
Because the default Membership Provider implementation is a generic solution, it does not natively support the concept of having multiple portals, each with its own users and roles. The default implementation was designed in a way that supports only a single portal site in a DotNetNuke installation. The Membership Provider refers to the DotNetNuke installation as an application, and without customization, that application can support only a single set of users and roles (a single portal instance).
To overcome this limitation, a wrapper was needed for the Membership Provider's SQL data providers. This customization allows application virtualization support. The end result is that the Membership Provider, as implemented in DotNetNuke, can support multiple applications (multiple portal instances in a single DotNetNuke installation).
To achieve the full benefit from the Membership Provider, it is important to recognize that user information can be externalized from DotNetNuke and held in a data store that is independent of the main data store. For instance, DotNetNuke may use Microsoft SQL Server as its database to store content and system settings, but the Membership Provider may use Windows authentication, LDAP, or another mechanism to handle authentication and authorization. Because security can be externalized using the Provider Model, it was important to ensure that the implementation of the Membership Provider didn't customize any code or database tables used by the provider. Those data tables had to be independent from the other core DotNetNuke tables. We could not enforce referential integrity between DotNetNuke data and the Membership Provider data, nor could we use cascade deletes or other data level synchronization methods — all of the magic had to happen in the business layer.
One challenge in implementing the Membership Provider was dealing with the fields that DotNetNuke internally supports but that the Membership Provider does not support. Ideally, we would have completely replaced the DotNetNuke authentication and authorization tables with the tables used by the Membership Provider. However, we could not achieve this goal because the authentication and authorization tables in DotNetNuke were already tied to so many existing and necessary features of the application. For instance, the DotNetNuke Users table has a UserID column, which holds a unique identifier for each user. This UserID is used in nearly all core and third-party modules. The most significant problem with UserID was that it doesn't exist in the Membership Provider. Instead, the Membership Provider uses the Username as the unique key for a user within an application. We needed a way to maintain the UserID to preserve the DotNetNuke functionality that depended on it. This is just one example of an attribute that cannot be handled by Microsoft's default Membership Provider.
Ultimately, we decided that we would need to maintain satellite tables to support the DotNetNuke attributes that could not be managed by the Membership Provider. The goal was to maintain enough information in the DotNetNuke tables so that functionality was not lost, and offload whatever data we can to the Membership Provider tables. The end result is a data model that mirrors the Membership Provider data tables as shown in Figure 7-6.
Notice that none of the tables on top in Figure 7-6 have database relationships to the any of the tables on the bottom. The lines connecting them simply show their relationship in theory, not an actual relationship in the database.
Because the data for portals, profiles, users, and roles is stored in multiple unrelated tables, the business layer is responsible for aggregating the data. For example, you cannot get a complete representation of a user without collecting data from both the aspnet_Users table (from the Membership Provider) and the Users table (native DotNetNuke table).
In addition to aggregation, data in the tables used by the Membership Provider and the native DotNetNuke tables must be automatically synchronized. Earlier in this chapter, you learned that the Membership Provider supports a wide array of data stores, and in ASP.NET 2.0, the data in those data stores can be managed through a common application configuration utility. If a user is added through that utility, the user is not added to the native DotNetNuke tables. Also, if your Membership Provider uses an LDAP implementation, for example, a user could be added to LDAP but would not be added to the native DotNetNuke tables. That's why synchronization services between the two data structures are provided.
DotNetNuke 4.0 fully leverages features of the ASP.NET 2.0 Membership API. Atypical of applications built on new platform releases, DotNetNuke 4.0 provides a tested and proven solution on the ASP.NET 2.0 framework based on the backported component utilized in DotNetNuke 3.0. These features bring demonstrated flexibility and extensibility to the security framework.