Authentication, Authorization, and Identities


WSS does not perform any authentication on its own. Instead, WSS relies on either Internet Information Services (IIS) or the ASP.NET Framework to perform authentication. When WSS is configured to use Integrated Windows Authentication, IIS together with the operating system of the Web server authenticates the user against a Windows user account (which could be either a local or domain account). When WSS is configured to use Forms Authentication, an ASP.NET authentication provider authenticates the user against some other type of user accounts repository, such as a Microsoft SQL Server database.

Although WSS does not perform authentication, it does manage user identities and perform authorization. After authentication, WSS maintains a user security token that identifies the authentication mechanism and a list of groups and/or membership roles. WSS is able to read the groups and membership roles of the current user very efficiently at run time by examining this token. The structure of this token varies depending on whether the user is authenticated with Integrated Windows Authentication or Forms Authentication.

WSS supports the assignment of permissions to security principals. There are two types of security principals: users and groups. The WSS object model defines the SPPrincipal class that provides the base functionality for assigning permissions to a principal. SPPrincipal exposes the Roles SPRoleCollection property as well as the ParentWeb SPWeb property as WSS instantiates each principal in the context of a parent site. The WSS object model also defines two classes that derive from SPPrincipal-SPUser and SPGroup. These two classes extend this base class with their own unique methods and properties.

The request of an authenticated user runs under the content of an SPUser object and carries a security token. When you create an object reference to an SPSite site collection, WSS creates an instance of the SPUserToken and the SPUser. This always happens in the context of the site collection, and it is the user who creates the instance reference that WSS uses for authorization. As the code attempts to access resources, WSS checks this user’s security token against binary access control lists (ACLs) to determine whether it should grant or deny access. An ACL is simply a data structure associated with a target WSS object, such as a site, list, or list item that tracks the rights of users and groups.

WSS objects may either use their own ACL or inherit the rights of a parent object. By default, most items within the WSS object model inherit parent ACLs. For example, a newly created document library inherits the ACL of its parent site. A newly created document automatically inherits the ACL of its parent document library. However, it’s also possible to configure any document with its unique ACL to give it an access control policy that differs from other documents within the same document library. This can be done through either the user interface or custom code. To return the parent object containing the ACL used by any securable object in WSS, call its FirstUniqueAncestor property. To return the first parent SPWeb site that contains the ACL used by a child Web site, call the FirstUniqueAncestorWeb property.

It is important to note that WSS manages users and groups and enforces authorization at the scope of the site collection. Rights assigned to a user in one site collection never affect what the user can do in another site collection. It is by design that WSS treats each site collection as its own independent island with respect to authorization and access control.

WSS stores and maintains a user information profile for authenticated users at the site collection level. The user information profile can be seen and updated by selecting the My Settings menu item command from the Welcome menu at the top right of the home page for a site. Remember that there is only one user information profile per user that extends across all of the sites inside. However, the user information profile does not extend across site collections.

Using Forms Authentication

The previous version of WSS supported authentication only against Windows accounts. This restriction created a tight coupling with Active Directory directory service that made WSS 2.0 less than ideal for extranet applications. Most companies also determined that the requirement to maintain user accounts in Active Directory made WSS 2.0 an impractical platform for creating Internet-facing sites that required authentication.

Microsoft ASP.NET 2.0 introduced the Authentication Provider Framework, which makes it possible to plug in and substitute various authentication provider components, each of which has its own unique implementation of the code required to authenticate users. A significant benefit to this new model is that an authentication provider can be written to authenticate user accounts that are maintained in any type of repository, such as a database or a Lightweight Directory Access Protocol (LDAP) provider.

ASP.NET 2.0 ships with out-of-the-box authentication providers that can be used to authenticate users against user accounts created in a SQL Server database or in Active Directory. However, the main benefit of the authentication provider model in ASP.NET is to promote flexibility. If a company wishes to maintain its user accounts in a different type of database or in any type of LDAP provider, it can commission a developer to write a custom authentication provider. Once written, the custom authentication provider can be plugged in and configured to provide the authentication plumbing required for ASP.NET applications.

WSS 3.0 is designed and implemented to support the ASP.NET authentication providers in addition to Integrated Windows Security. This is a significant step forward from the previous version of WSS because user accounts no longer have to be maintained in Active Directory. Companies should now see WSS 3.0 as a viable platform for building Internet-facing sites, including those that allow unknown Internet users to register themselves as members.

The easiest path to using Forms Authentication with WSS 3.0 is to use the AspNetSqlMembershipProvider class that comes as a standard component of ASP.NET 2.0. To get up and running, you or a system administrator must seek out the proper documentation to create an aspnetdb database in SQL Server and add the proper configuration data to the machine.config file and/or various web.config files on each front-end Web server.

Note that although WSS 3.0 supports ASP.NET authentication providers and forms authentication, it provides no components or features that assist with the creation or management of user accounts. For example, if you want to develop an Internet-facing site with a registration link that says, “Click here to create a membership account,” you must then provide the code and user interface components that interact with the underlying authentication provider to create a new user account. Such code could be written into a standalone ASP.NET application or integrated into a custom application page or a Web Part.

Tip 

You should read the Security chapter of Essential ASP.NET 2.0, Second Edition, written by Fritz Onion and Keith Brown (Addison-Wesley Professional, 2007), if you would like more background information about how to interact with an ASP.NET membership provider or create your own custom membership provider.

WSS Security Context Versus Windows Security Context

When code runs within a custom WSS component, such as a Web Part or application page, it’s important that you distinguish between two different security and authorization subsystems. There is both a WSS security context and a Windows security context. Let us provide a little more background to illustrate the difference.

As you know, WSS uses a user security token created by either Windows authentication or forms authentication to establish its own security context. This WSS security context is used to conduct access control checks in internal WSS objects such as sites, lists, and items. However, just because WSS adds it own authorization layer doesn’t mean that the Windows authorization layer goes away.

The WSS components you write, such as Web Parts and application pages, often must access external resources. Access control to these external resources is controlled not by WSS, but instead by the Windows operating system itself, which means that you also must be aware of the current Windows security context as well as the WSS security context.

The standard web.config file for WSS Web applications has the following entry:

 <identity impersonate="true" />

By setting the impersonate attribute to true, WSS instructs the ASP.NET runtime to process all requests under the Windows security context of the current user. When you write code for a Web Part or application page that attempts to access an external resource, such as a file system resource, database call, or Web service call, it runs under the impersonated Windows identity. This enables the Windows authorization subsystem (local or remote) to determine whether it should grant or deny access.

Understanding the Windows security context is fairly straightforward when you are using the default Windows Authentication provider because its identity is synchronized to the same user account as the identity of the WSS security context. However, things aren’t so obvious when you are using forms authentication. Because forms authentication doesn’t involve authenticating against a Windows account, the Windows security content takes on the identity of the IUSR_MACHINENAME account, or the account specified in the Authentication Methods dialog box of IIS (the same dialog box that enables IIS anonymous access).

Tip 

Forms authentication impersonates the anonymous access Windows account. As credentials leave WSS and transition to the Windows authentication protocol, this anonymous user account is used.

Users and Groups

The WSS object model tracks user identities by using the SPUser class. If you want to access the SPUser object for the current user, you use the CurrentUser property of the SPWeb object associated with the current site. The following simple example shows you how to access some of the properties available through the SPUser class.

 SPUser currentUser = SPContext.Current.Web.CurrentUser; string userName = currentUser.Name; string userLogin = currentUser.LoginName; string userEmail = currentUser.Email;

The current user is always the user who was authenticated when the SPSite site collection object was created. If your code is running in the WSS Web site context, this is the user who authenticated to either WSS or the ASP.NET authentication provider. If your code is running in the context of a console application, the current user is the user whose Windows principal was used to create the initial SPSite reference. You cannot switch the security context of the site collection or its objects after it is created; it is always the user principal who first accessed the site collection that is the current user. We will look at elevation of privilege, delegation, and impersonation later in this chapter to further illustrate this point.

Assigning permissions directly to users is usually not a scalable and maintainable solution, especially across large enterprises with many users and sites. Besides the maintenance issues, as ACLs grow larger, they can bog down performance of WSS. This is not an issue unique to WSS, for it is the same issue solved by Active Directory users and groups.

WSS supports the creation of groups within a site collection to ease the configuration of authorization and access control. Groups are never created in the context of the site-they are always created in the context of the site collection and assigned to a site. For example, assume that we have a site located at /litware/sales, and that the /litware/sales site reference is the current context returned from SPContext.Current.Web. Given this environment, site.Groups would return the group collection of the sales site. This would be a subset of the groups available in the site collection, which is available through the site.SiteGroups property. For example, the following code would return the groups Team Site Members, Team Site Owners, and Team Site Visitors.

 SPSite siteCollection = new SPSite("http://localhost/litware/sales/"); SPWeb site = siteCollection.OpenWeb(); foreach(SPGroup group in site.Groups){   Console.WriteLine(group.Name); }

Groups cannot directly be added to a site-they must be added to the site collection. If you try to add a group to the site’s Groups collection, you get an exception stating, “You cannot add a group directly to the Groups collection. You can add a group to the SiteGroups collection.” This situation occurs because the SPGroup is always created at the Site Collection level and assigned to the site. The following code is valid and adds the LitwareSecurityGroup to the site collection groups.

 // Adds a new group to the site collection groups: site.SiteGroups.Add("LitwareSecurityGroup", site.CurrentUser,   site.CurrentUser, "A group to manage Litware Security");

However, this still does not associate the group with our site, nor would it be useful within the site without any permissions. To add the group to the site, create a new SPRoleAssignment by associating an SPRoleDefinition with the SPGroup, and then add that role assignment to the site, as in the following code sample:

 SPGroup secGroup = site.SiteGroups["LitwareSecurityGroup"]; SPRoleAssignment roleAssignment = new SPRoleAssignment(secGroup); SPRoleDefinition roleDefinition = site.RoleDefinitions["Full Control"]; roleAssignment.RoleDefinitionBindings.Add(roleDefinition); site.RoleAssignments.Add(roleAssignment);

As with Groups and SiteGroups, multiple collections can be used to access site users. Table 10-1 lists user-related properties of the SPWeb site object and when to use them.

Table 10-1: SPWeb User Properties
Open table as spreadsheet

Property

Description

AllUsers

Used to access any user who has accessed the site as a member of a domain group that is a site member or any user who is explicitly a member of the site. For example, the user Mike Fitzmaurice (LITWARE\mikefitz) may be a member of the LITWARE\sales group. If LITWARE\sales has access to the Sales site and Mike has visited the site (as a member of the LITWARE\sales group), he would be accessed through the AllUsers collection. Because it is the largest collection of users available (being a combination of the SiteUsers, Users, and group membership), you generally use the AllUsers collection when you want to access a user.

CurrentUser

Returns the current user who created the reference to the SPSite site collection. This is generally the user accessing the WSS Web site.

SiteUsers

Used to access the collection of users in the site collection. This is a subset of the AllUsers collection.

Users

The smallest collection of users, containing only the users explicitly added to aWSS site.

Application Pool Identities

The application pool identity plays a large role in WSS applications. Besides running the Web application, this account is used as the Windows account that connects to the WSS Content and Configuration databases and is the Windows account used when running code in the SPSecurity.RunWithElevatedPrivileges method. Although you can use an application pool identity of Network Services in a single-server deployment, it usually makes more sense to use a domain account. Using a domain account is a requirement in a Web farm environment where it’s important for multiple processes running on different machines to all share the same identity.

Ideally, one account should be created to run only the Central Administration application and should be assigned the dbcreator and securityadmin permissions in the SQL database server. WSS assigns group membership to the IIS_WPG, Power Users, Users, WSS_ADMIN_WPG, WSS_RESTRICTED_WPG, and WSS_WPG security groups. These groups are required for the Central Administration system account, but they have more privileges than the actual WSS Web applications should be granted. With this administration account in place, WSS can manage the required permissions for additional accounts.

When you create a new Web application through the WSS Central Administration application, you should create it to run inside a new or existing application pool that is separate from the Central Administration application pool. Moreover, application pools for Web applications that are accessible to end users should be configured with a domain account that is not as privileged as the user account for the Central Administration application pool. For example, there is no reason why WSS code running within any application pool other than the Central Administration application pool would ever need to create a new content database or configure database security permissions.

Consider what happens when you create a new Web application through the WSS Central Administration application. When you do this, you get to determine whether WSS creates a new application pool for this Web application or uses an existing application pool. If you tell WSS to create a new application pool, you must supply the name and password of a valid Windows user account. When WSS creates the new content database, it grants this user account the dbowner role for that content database. WSS also grants the database roles public and WSS_Content_Application_Pools to this user account in the configuration database. You should note that user accounts that provide application pool identities must also be added to two local groups named IIS_WPG and WSS_WPG so that they have the proper permissions to access WSS system files as well as specific locations within the Windows Registry and IIS Metabase.

Tip 

Application pool identities could be local (machine) accounts if the machine is not a member of a domain.

SharePoint System Account

The SHAREPOINT\system account is an identity to which WSS maps internally when code is running under the identity of the hosting application pool. The SHAREPOINT\system account is not recognized by Windows because it exists only within the content of the WSS runtime environment. This enables WSS to use a statically named account for system-related activity regardless of whatever Windows user account has been configured for the hosting application pool.

For example, if you switch the application pool from LITWARE\SP_WorkerProcess1 to LITWARE\SP_WorkerProcess2, code running as system code still acts and is audited as the SHAREPOINT\system account. However, it is also important to realize that SHAREPOINT\system is not recognized by the Windows security subsystem. Therefore, code in WSS running as system code is recognized under the identity of the hosting application pool when it attempts to access external resources, such as the local file system or a SQL Server database.

Escalation of Privilege

The SPSecurity class provides a static method named RunWithElevatedPrivileges that enables code to execute as system code running under the identity of SHAREPOINT\system. This allows code to run in an escalated security context to perform actions as the system. This method should be used with care and should not expose direct access to system resources, but rather should be used when you need to perform actions on behalf of the system. The method is simple. You can either create a delegate to a public void method or simply write code within an inline delegate. The signature looks like the following:

 SPSecurity.RunWithElevatedPrivileges(delegate() {   // Code runs as the "SharePoint\system" user });

Code within the delegate runs under the Windows SHAREPOINT\system security principal. As covered in the previous section titled “Application Pool Identities,” this account uses the application pool identity when passing credentials to external resources, but it uses the system account internally. However, if you use code similar to the following, you would notice a bug in your code that seems to indicate that the security context has not been switched.

 // Bad code example: SPSecurity.RunWithElevatedPrivileges(   delegate() {     SPListItem record = visitorList.Items.Add();     // still the calling user:     record["User"] = SPContext.Current.Web.CurrentUser;     // uses authorization of the calling user, NOT the system:     record.Update();   } );

To modify WSS content under the System credentials, you need to create a new SPSite site collection that generates a new security context for objects referenced from the site, as in the following example. You cannot switch the security context of the SPSite once it has been created, but must instead create a new SPSite reference to switch user contexts. The following code uses the system credentials to add a list item using the profile data of the current Web user:

 SPSecurity.RunWithElevatedPrivileges(   delegate() {     using (SPSite site = new SPSite(web.Site.ID)) {       using (SPWeb web2 = site.OpenWeb()) {         SPList theList = web2.Lists["visitors"];         SPListItem record = theList.Items.Add();         record["User"] = SPContext.Current.Web.CurrentUser;         record.Update();      }    } );

Code running with the escalated privilege should use a new SPSite object for code running as the system and use the SPContext.Current property to access the actual calling user’s identity. The ElevatedPrivilegeWebPart shown in Listing 10-3 demonstrates the importance of the SPSite site collection object in generating a security context.

Listing 10-3: The Elevated Privilege Web Part demonstrates the security context of the SPSite object.

image from book
  Elevated Privilege and SPSite Example using System; using System.Web.UI.WebControls.WebParts; using Microsoft.SharePoint; using System.Security.Principal; using System.Security; using System.Security.Permissions; namespace LitwareSecurity {   [PermissionSet(SecurityAction.Demand)]   public sealed class ElevatedPrivilegesWebPart : WebPart {     protected override void RenderContents(System.Web.UI.HtmlTextWriter writer) {       base.RenderContents(writer);       SPWeb site = SPContext.Current.Web;       // Impersonates SHAREPOINT\system:       SPSecurity.RunWithElevatedPrivileges(delegate() {         // The windows user is SHAREPOINT\system:         writer.Write("Elevated privilege Windows user: {0}<br/>",         WindowsIdentity.GetCurrent().Name);         // The site context is still the calling user's:         writer.Write("Elevated privilege user: {0}<br/>",           site.CurrentUser.Name);         // Open a new site security context using SHAREPOINT\system:         using (SPSite siteCollection = new SPSite(site.Site.ID)) {           using (SPWeb site2 = siteCollection.OpenWeb()) {             // The new site context is now SHAREPOINT\system:             writer.Write("New site elevated privilege user: {0}<br/>",               site2.CurrentUser.Name);           }        }      });     }   } } 
image from book

Tip 

Create a new SPSite reference while impersonating to perform actions in the impersonated context.

Elevated privilege is useful for either writing to read-only lists or using the application pool credentials to access Windows authentication–secured Web services. Listing 10-4 demonstrates the use of the system account to track visitors by writing to a read-only list. Regardless of the site privileges of the user, the system enters a visitor record in the Visitor list with the calling user’s identity profile while using the system account security principal for authorization. Note that the use of the PermissionSet attribute’s SecurityAction.Demand parameter forces a stack walk, ensuring that any code that is executing this method has the correct CAS policy.

Listing 10-4: The Visitor Tracker Web Part demonstrates the RunWithElevatedPrivileges security method.

image from book
  Visitor Tracking with Escalation of Privilege using System; using System.Collections.Generic; using System.Text; using System.Web.UI.WebControls.WebParts; using Microsoft.SharePoint; using System.Security.Permissions; namespace LitwareSecurity {   [PermissionSet(SecurityAction.Demand)]   public sealed class VisitorTrackerWebPart : WebPart {     [PermissionSet(SecurityAction.Demand)]     protected sealed override void OnLoad(EventArgs e) {       base.OnLoad(e);       SPWeb site = SPContext.Current.Web;       SPUser user = site.CurrentUser;       const string listName = @"visitors";       SPList visitorList = null;       foreach (SPList alist in site.Lists) {         if (alist.Title.Equals(listName,           StringComparison.InvariantCultureIgnoreCase)) {           visitorList = alist;           break;         }       }       if (visitorList == null) {         SPSecurity.RunWithElevatedPrivileges(           delegate() {             using (SPSite siteCollection =               new SPSite(this.Page.Request.Url.ToString())) {                 using (SPWeb systemSite = siteCollection.OpenWeb()) {                   systemSite.AllowUnsafeUpdates = true;                   Guid listID = systemSite.Lists.Add(listName,                     "Site Visitors", SPListTemplateType.GenericList);                 visitorList = systemSite.Lists[listID];                 visitorList.Fields.Add("User", SPFieldType.User, true);                 visitorList.WriteSecurity = 4;                 visitorList.Update();                 systemSite.Update();               }             }           }         );       }       // Uses the SHAREPOINT\system creds       SPSecurity.RunWithElevatedPrivileges(delegate() {         using (SPSite siteCollection =           new SPSite(this.Page.Request.Url.ToString())) {             using (SPWeb systemSite = siteCollection.OpenWeb()) {               systemSite.AllowUnsafeUpdates = true;               SPList theList = systemSite.Lists[listName];               SPListItem record = theList.Items.Add();               record["User"] = user;               record["Title"] = string.Format("{0} {1} {2}", user.Name,               DateTime.Now.ToShortDateString(), DateTime.Now.ToShortTimeString());               record.Update();             }           }       });     }   } } 
image from book

Tip 

The RunWithElevatedPrivileges method applies only to authorization, not CAS policy, and it requires the SharePoint security permission SharePointPermission to execute.

Delegating User Credentials

Within application code running in the WSS Web application, the code runs under the credentials of the application pool while impersonating the calling user. This condition enables WSS to secure objects, including sites, lists, and list items, by using the calling user’s identity. Identity is configured automatically through the web.config setting <identity impersonate="true" />. This is true for both the Web application and Web service endpoints. When calling Web services, you can use this identity to authenticate to remote endpoints by setting the credentials to the Default Credentials. Note that to pass credentials to backend services, the WSS server must be set up with the rights to delegate Kerberos or NTLM credentials in Active Directory. For Web service requests to the same box, delegation is not required.

Tip 

Credential delegation can be tricky to set up and works differently when accessed from a remote box versus the local development server. When testing security delegation, be sure to use a remote box to access the WSS server.

The following code example uses the credentials of the current user to authenticate a Web request against a Web data source.

 WebRequest xmlReq = WebRequest.CreateDefault(xmlUri); xmlReq.Credentials = CredentialCache.DefaultCredentials;

In addition to the current user’s credentials, you can access the application pool identity by using the SPSecurity method RunWithEscalatedPriveleges.

 SPSecurity.RunWithElevatedPrivileges(delegate() {   WebRequest xmlReq = WebRequest.CreateDefault(xmlUri);   // Uses the app pool credentials:   xmlReq.Credentials = CredentialCache.DefaultCredentials; });




Inside Microsoft Windows Sharepoint Services Version 3
Inside Microsoft Windows Sharepoint Services Version 3
ISBN: 735623201
EAN: N/A
Year: 2007
Pages: 92

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net