You may have some knowledge about the security programming basics of using Windows and ASP. NET security, but how much do you know about the security protection added by Windows SharePoint Services 3.0 (WSS? In this office space column, I will focus on some new security terms and concepts introduced by WSS and show you a new world of Secure Programming Using WSS object models.
We recommend that you downloadArticleThe example project that is included at the end and provided according to other sections of this columnCodePerform the operation. This project has been configured to run a batch file after the build process is complete. This batch file will compile all the project components into a WSS solution package, install the package in the local WSS server farm. After creating a project and installing a solution, you can browse any website set and enable the "Security Demo" feature for the website set. Then you can navigate to the custom application through the "website operations" menu.ProgramPage, these pages demonstrate WSS security programming technology through some code.
External Security subject and spuser object
Most security models are based on security subjects. Each security subject represents a user or a group. The user has an account and uses these accounts for authentication. After the authentication is completed, each user receives an identity. When you use a Windows account for authentication, you can use system. microsoft.. NET Framework security class to retrieve the identity. This identity refers to a specific Windows Account and allows you to view the user's login name:
Copy code
Windowsidentity identity = windowsidentity. getcurrent ();
String windowslogin = identity. Name;
With windowsidentity, You can dynamically create a windowsprincipal object that allows you to test whether the current user belongs to an active directory group or a local Windows Group, as shown below:
Copy code
Windowsidentity identity = windowsidentity. getcurrent ();
Windowsprincipal principal = new windowsprincipal (identity );
If (principal. isinrole (@ "litwareinc \ allfte ")){
// Perform operation allowed for fulltime employees
}
ASP. NET supports both Windows Authentication and form-based authentication (FBA ). The user object in ASP. NET is modeled based on the iprincipal interface (instead of the windowsprincipal class) to free up the dependency on the Windows account. The ASP. Net Runtime Library dynamically creates different types of iprincipal objects based on whether the current user uses Windows accounts or FBA accounts for Identity Authentication:
Copy code
Iprincipal aspuser = httpcontext. Current. user;
String aspusername = aspuser. Identity. Name;
ASP. NET user object also provides the isinrole method to check whether a user belongs to a special role. For Windows users, the isinrole method allows you to check whether the current user is a member of the Active Directory group. If you are using the FBA account attached to the ASP. NET role provider, you can also use the isinrole method to check whether the FBA user has been added to a specific ASP. NET role:
Copy code
Iprincipal aspuser = httpcontext. Current. user;
If (aspuser. isinrole ("site administrators "){
// Perform privileged operation
}
An authentication operation generates a receipt in some form. The system uses this receipt at runtime to indicate the user's identity and track the membership in the group or role. If a user uses a Windows account for authentication, the authentication receipt is a Windows Security token. If you use an FBA account for authentication, the authentication receipt is an HTTP cookie created by the ASP. NET Runtime Library and a specific authentication provider.
It is important to understand that WSS does not support user authentication. However, WSS can use the underlying authentication components provided by various ASP. NET authentication providers. The value of WSS for website security protection lies in its ability to configure authorization and access control. WSS can track external security subjects (such as Windows users, FBA users, Windows groups, and ASP. NET roles) within the scope of the website set. With WSS, you can also configure the permissions assigned to these external entities. In fact, you are granted permissions to access WSS security objects (such as websites, lists, items, and documents.
Note that the website set plays an extremely important role in configuring authorization and access control. When tracking external subjects and configuration permissions, WSS regards each website set as an independent island. WSS's advanced design intentionally isolates each website set so that user security settings in one website set do not affect the permissions or access control policies in other websites.
1. spuser
The WSS object model uses the spuser object to represent external security subjects. You can use the current spweb object to retrieve the spuser object of the current user:
Copy code
Spweb site = spcontext. Current. Web;
Spuser user = site. currentuser;
String displayname = user. Name;
String login = user. loginname;
String email = user. Email;
String User Notes = user. notes;
The spuser object exposes various attributes of an external security subject, such as the login name, display name, and email address. When an external entity is added to a website, these attributes can usually be retrieved from the underlying user repository (such as the Active Directory domain. The spuser object also provides attributes for tracking WSS-specific metadata, such as the "comment" field.
WSS keeps the configuration file data of external users, groups, and roles in the hidden list (called "User Information List. Each time the WSS provides a new website set, the "user information list" is automatically created in the primary site as a hidden list. Then, when the subject is assigned a permission for the first time, or the subject accesses a security object through the security check for the first time, WSS adds a new configuration file for each external subject. Note that the user configuration files stored in the "user information list" cannot be expanded across website sets. After a user updates the configuration file settings in a website set, configuration File settings of the user in other websites are not changed.
Another obfuscation is that spuser objects do not always represent actual users. Spuser objects can also represent active directory groups and ASP. NET roles. WSS not only tracks the configuration files of each external entity in the "user information list", but also the configuration file data of external users.
Many programming aspects of the SharePoint security model are made public at the website level through spweb objects. If you want to check which users are members of the current website, you will find this. A spweb object can expose three different user sets, as shown in the following code segment:
Copy code
Spweb site = spcontext. Current. Web;
Spusercollection C1 = site. users;
Spusercollection C2 = site. allusers;
Spusercollection C3 = site. siteusers;
The users set contains the minimum number of members in the three sets. This collection contains all external entities on the current website that have explicitly assigned permissions.
The allusers set includes all members in the users set, and external users who have accessed objects on the website using implicit permissions through group or role membership. For example, assume that a user named Brian (login name litwareinc \ brianc) has never been explicitly granted permissions to access a website and view a specific list. However, he may still be able to view the list because the list view permission has been configured for the Active Directory group to which he belongs. When Brian visits the website for the first time or any of the objects (for example, using the implicit permission to view a list), he is added as a member of the allusers set, but it will not be added as a member of the users set.
The siteusers set contains an aggregation of members of each allusers set in the current website set. Members of this set include all external subjects assigned access permissions to all objects in the website set, and all external users who have been granted the permission to access all objects in the website set with implicit permissions.
Add Authenticated Users and external users
So, how do I create a new WSS user configuration file for users who use Active Directory accounts for identity authentication? If you need to create a custom user interface component so that standard users or website set owners can select users or groups from the Active Directory domain, you must understand the usage of the always writable icker control (see figure 1 ). This is a convenient and reusable control type attached to WSS. You can use the following control flag to add the control to the custom application page or user control:
Figure 1 Navigation Pane icker control (click the image to get a larger view)
Copy code
<SharePoint: peopleeditor
Id = "pickerprincipal"
Allowempty = "false"
Validatorenabled = "true"
Multiselect = "false"
Selectionset = "user, secgroup, DL"
Width = "280px"
Runat = "server"/>
In this example, I configured the zookeeper icker control and assigned three values for the selectset attribute: User, secgroup, and DL. By configuring these selectset settings, the control allows you to select and parse user, group, or distribution list Based on Active Directory.
You can access the logging handler icker control properties programmatically to retrieve the Logon account name of a basic account after you select one or more security subjects using the control. Then, you can write code to actually add these subjects as website Members and configure access permissions for them.
Now, I will introduce how to add external users or groups as website Members. After a brief understanding of the WSS object model, you may consider that you only need to add an external security subject directly to a spuser set (such as siteusers:
Copy code
Spweb site = spcontext. Current. Web;
Site. siteusers. Add (@ "litwareinc \ brianc ",
"Brianc@litwareinc.com ",
"Brian Cox ",
"Notes about Brian Cox ");
Site. siteusers. Add (@ "litwareinc \ allfte ",
"AllFTE@litwareinc.com ",
"All full-time employees ",
"Notes about fte dl ");
Although this method can indeed create a configuration file for an external subject in the "user information list", security is almost ineffective because permissions cannot be assigned. In contrast, a better way to add new external security subjects is to allocate permissions so that users have the right to access the current website. First, you need to learn how to create and assign permissions.
Permission level
The permission level is the permission naming set defined within the scope of the website. WSS has four built-in permissions: read, contribute, design, and full access ). You can use the WSS object model or the standard WSS Management page accessible to the website set owner to create your own custom permission levels.
The permission level is sometimes called a role. In The WSS object model, the sproledefinition object is usually used to indicate these permission levels. You can use the sproleassignment object to assign permissions to external users or groups. For example, here I assign the built-in contribute permission level to a Windows user whose login name is litwareinc \ brianc:
Copy code
Spweb site = spcontext. Current. Web;
Sproledefinition role = site. roledefinitions ["Contribute"];
Sproleassignment roleassignment;
Roleassignment = new sproleassignment (@ "litwareinc \ brianc ",
"Brianc@litwareinc.com ",
"Brian Cox ",
"Notes about Brian Cox ");
Roleassignment. roledefinitionbindings. Add (role );
Site. roleassignments. Add (roleassignment );
In this way, you do not have to add users to a spuser collection, because WSS has automatically completed this operation when the website first assigns permissions to external users or groups. The code you just saw will create a user configuration file (if it does not exist) in the "user information list" and add the user as a member of the users set of the current website.
WSS Group
The WSS security model not only expresses external security subjects as spuser objects, but also provides WSS groups to facilitate permission configuration within the scope of the website set. For example, you can design a series of WSS groups for specific user roles such as site members, content manager, and site administrators. After completing this operation, you only need to assign the permission level to the WSS group to configure Website Security Settings, without directly assigning the permission level to the spuser object.
The obvious advantage of creating a WSS group is to help eliminate the need to reconfigure permissions when adding or deleting new external users and groups. You only need to configure permissions when creating a website, so that you can do it once and for all. Then, you only need to add or delete external users and groups to or from the WSS group. WSS groups adopt the same design principles as Active Directory groups. The main difference between the two is that WSS groups are defined and exist only within the scope of a single website set.
In the WSS object model, WSS groups are represented as spgroup objects. The spweb object exposes two spgroup object sets: groups and sitegroups. The groups set includes all WSS groups on the current website that have been directly assigned permissions, while the sitegroups set is the superset of the groups set, including all WSS groups created in the current website set.
If you want to create a new WSS group, you should call the add method exposed by the sitegroups set and assign one or more permission levels to the new WSS group on the target website. Figure 2 shows an example of creating a new WSS group named site members and assigning it a built-in contribute permission level on the current website.
Figure 2 create a new WSS Group
Copy code
Spweb site = spcontext. Current. Web;
Spuser currentuser = site. currentuser;
// Create new group
Site. sitegroups. Add ("site members", currentuser, currentuser,
"Site group created at" + datetime. Now. tostring ());
// Assign permission level to new group
Spgroup newgroup = site. sitegroups ["site members"];
Sproleassignment roleassignment = new sproleassignment (newgroup );
Sproledefinition permlevel = site. roledefinitions ["contrilevel"];
Roleassignment. roledefinitionbindings. Add (permlevel );
Site. roleassignments. Add (roleassignment );
After creating a new WSS group, it is easy to add external users and groups as members. An adduser method is provided by a spgroup object. This method accepts a spuser object, and then you can add external users and groups:
Copy code
Spweb site = spcontext. Current. Web;
Spuser currentuser = site. currentuser;
Spgroup group = site. sitegroups ["site members"];
Spuser user1 = site. siteusers [@ "litwareinc \ brianc"];
Spuser user2 = site. siteusers [@ "litwareinc \ allfte"];
Group. adduser (user1 );
Group. adduser (user2 );
Identification, lifting, and Simulation
The worker process of the wss web application is controlled through the IIS application pool. The workflow identifier of a Web application ensures that you are concerned with the process. You can configure these identifiers through the SharePoint Management Center application. You should configure the workflow ID of the Web application according to the domain account (such as litwareinc \ sp_workerprocess), instead of the Local Account (such as network service.
Note that the workflow ID of a Web application must be a privileged Windows account that has been configured with SQL Server permissions to read and write one or more content databases. The workflow ID of the Web application running the SharePoint Management Center website must have more privileges, because it must have the permission to read and write the configuration database of the farm.
When the code on the Web part or custom application page responds to a user request and starts execution, the Code is not executed as the workflow ID that hosts the web application. Instead, WSS switches the Windows security context to another Windows account by simulating. In fact, if you view the Web. config file of the WSS web application, you will see the following items:
Copy code
<Configuration>
<System. Web>
<Identity impersonate = "true"/>
</System. Web>
</Configuration>
If a request is executed for a user who has used Windows account for authentication, the request simulates the Windows identity of the current user. However, the same method does not apply to FBA users because FBA authentication does not create a Windows Security token and does not have a Windows identity. Therefore, requests for users using FBA authentication simulate the identity of Windows accounts configured for anonymous access. The iuser_machinename account is assigned to this account by default in IIS, but you can (and should) reconfigure this account to point to the domain account.
Now, let's look back at more advanced WSS security programming. The WSS security model usually requires developers to differentiate the windows and WSS user identities. However, in a request, if both the current Windows identity and the current WSS user identity point to the same Windows login name, the above situation may not be obvious. In the case of FBA, things will become more complex. For example, The WSS user ID may point to the FBA user named Andrew, while the basic Windows identity is based on the iuser_machinename account. When your code tries to access the WSS object, WSS uses the user's wss id to run the access check. When your code tries to access external objects (such as files maintained by the Windows operating system) outside the WSS, the operating system uses the Windows identity (the code is executed using this identity) to run the access check.
Sometimes, to execute code, you must have higher permissions than the current user. For example, we can assume that your code must write data to a list when processing requests from users with only read-only permissions. By default, your code runs with the same permissions as the current user. In this case, you can call the runwithelevatedprivileges method of the spsecurity class to improve the security context of the Code. Note that calling runwithelevatedprivileges will also enhance the WSS user ID and Windows identity.
Now imagine a situation where a user authenticates using a Windows account using the login name litwareinc \ brianc. Calling runwithelevatedprivileges will promote the WSS user identity to the SharePoint \ SYSTEM account. The SharePoint \ SYSTEM account is built in the WSS Runtime Library and has full permissions in The WSS authorization model. Calling runwithelevatedprivileges also switches the Windows ID of the Code to run as the workflow ID of the current web application:
Copy code
// Before elevation
// WSS user identity = litwareinc \ brianc
// Windows identity = litwareinc \ brianc
Spsecurity. runwithelevatedprivileges (delegate (){
// After elevation
// WSS user identity = SharePoint \ System
// Windows identity = litwareinc \ sp_workerprocess
});
In some scenarios, you may choose to call the runwithelevatedprivileges method before attempting to access files in the Windows File System or SQL Server database to change the Windows identity currently called. Note that if you switch from a Windows identity to a Process Identity (for example, litwareinc \ sp_workerprocess), you do not have to configure the delegate in the Active Directory environment. This feature is very valuable when your custom web components can use Windows Integrated Identity Authentication to access data in a remote SQL Server database.
In other scenarios, you may need to call the runwithelevatedprivileges method to promote the WSS user identity to SharePoint \ System, so that your code can perform operations that are not permitted under the current user permission. Once the code can run as SharePoint \ system, you can perform almost any operation in the WSS authorization subsystem.
Calling runwithelevatedprivileges to upgrade to a Sharepoint \ SYSTEM account also has a relatively tricky aspect. For example, assume that you call runwithelevatedprivileges and attempt to access the objects in the current website set or website through the spcontext. Current attribute. You may not think of code errors, but the facts may surprise you:
Copy code
Spsecurity. runwithelevatedprivileges (delegate (){
Spsite sitecollection = spcontext. Current. Site;
// Next line fails if current user is contributor
String sitecollectionowner = sitecollection. owner;
});
Why does this sample code fail to be executed after the WSS user identity is upgraded to SharePoint \ system? This is related to the Creation Time of the spsite object. The spsite object and its sub-object spweb permissions do not depend on the current WSS user identity. It depends on the WSS user ID when the spsite object is created. Here, because the spsite object that can be accessed through spcontext. Current is created in the previous request, your code cannot switch its WSS user ID when this object is created.
Therefore, you need to use one technique to create a new spsite object after calling runwithelevatedprivileges and promoting the WSS user identity to SharePoint \ System:
Copy code
Spsecurity. runwithelevatedprivileges (delegate (){
Using (spsite elevatedsitecollection = new spsite (this. Site. ID )){
Using (spweb elevatedsite =
Elevatedsitecollection. openweb (this. Web. ID )){
// Access elevatedsitecollection and
// Elevatedsite as SharePoint \ System
}
}
});
In this way, you can open the website set and its websites so that your code can access objects as SharePoint \ system.
You may also find it necessary to simulate a specific WSS user ID. This method is very common when writing code for event handlers or custom workflow templates (in this case, the code runs as SharePoint \ System by default. For example, you may want to simulate a specific WSS user ID before creating a new object so that the WSS user can be recognized as the owner of the new object.
To simulate the WSS user identity, you must first create a spusertoken object. You can use the usertoken attribute of the spuser object to perform this operation. After the spusertoken object is created, you can use this object to create a new spsite object using the reload version of The spsite constructor. This method is shown in 3.
Figure 3 simulates WSS user identity
Copy code
Spweb sitecollection = spcontext. Current. Site;
Spweb site = spcontext. Current. Web;
// Get spuser object and acquire token
Spuser targetuser = site. siteusers [@ "litwareinc \ brianc"];
Spusertoken token = targetuser. usertoken;
// Create new spsite and spweb object to impersonate user
Using (spsite impersonatedsitecollection =
New spsite (sitecollection. ID, token )){
Using (spweb impersonatedsite =
Impersonatedsitecollection. openweb (site. ID )){
// WSS identity switched to impersonate brianc
// Windows identity does not change
}
}
When using WSS User Simulation, I need to point out some important precautions. First, simulating WSS users is different from calling runwithelevatedprivileges, because the former does not change the current Windows identity. For example, if a request is run as litwareinc \ sp_workerprocess's windows identity before simulating a WSS user, the Code continues to run as the same windows identity. WSS User Simulation does not change the current Windows identity to the identity of the simulated user.
It is also worth noting that the Code must be executed in a privileged state to simulate another user. However, you do not need to worry about this issue in the event handler or custom workflow template, because in such cases, the code runs as SharePoint \ System by default. However, the code on the Web part or custom application page may need to call runwithelevatedprivileges before it can simulate other WSS user identities.
Security object
The real advantage of WSS security configuration is the flexibility provided by security objects (such as websites, lists, and list items. Each security object can contain an access control list (ACL), which is a binary data structure, when running, WSS uses it to determine whether the security subject has been granted access permissions. By default, the primary website is the only security object with an ACL. Sub-objects (such as lists, list items, and sub-websites) inherit the ACL of their parent objects unless they refuse to inherit and provide a unique ACL.
The WSS object model contains an interface named isecurableobject, which is used to establish a secure object model on the WSS website (see figure 4 ). The isecurableobject interface is implemented through spweb objects, splist objects, and spitem objects. It provides the basis for executing access checks and configuring permissions during runtime.
Figure 4 isecurableobject interface (click the image to obtain a large view)
When you Configure permissions in a centralized website, it is very important to understand that all websites, lists, and list items constitute a single hierarchical structure of security objects. By default, only the primary website contains a unique ACL and defines the permission level allocation, specifying which permissions the user needs to access the object. The permissions of all sub-objects are inherited from the primary website. However, you can assign a unique permission level for a security object to configure access control more precisely. For example, with the code shown in figure 5, you can create a new document library and configure it with a unique set of permissions.
Figure 5 configure a unique set of Permissions
Copy code
Spweb site = spcontext. Current. Web;
Guid listid = site. Lists. Add ("proposals ",
"Library DESC ",
Splisttemplatetype. documentlibrary );
Spdocumentlibrary doclib = (spdocumentlibrary) site. Lists [listid];
Doclib. onquicklaunch = true;
Doclib. breakroleinheritance (false );
Spuser allftegroup = web. siteusers [@ "litwareinc \ allfte"];
Sproleassignment assignallftegroup = new sproleassignment (allftegroup );
Sproledefinition roledesign = This. Web. roledefinitions ["read"];
Assignallftegroup. roledefinitionbindings. Add (roledesign );
Doclib. roleassignments. Add (assignallftegroup );
Doclib. Update ();
The code in this example cancels the default permission inheritance relationship for the parent object by calling breakroleinheritance. If you call breakroleinheritance and send a true parameter value, the security object is initially configured with a copy ACL that is the same as the parent object ACL. If you call breakroleinheritance and send a false parameter value, the security object is initially configured with an empty ACL. That is to say, this document library does not provide access permissions for users who are neither the owner nor the website administrator.
Windows SharePoint Services 3.0 adds a popular security enhancement feature that allows you to configure permissions at the project or document level. You can achieve this through the WSS object model, because the splistitem object also implements the isecurableobject interface.
The code in Figure 6 creates a new document in the document library and uses a set of unique permissions different from its parent document library for configuration. Please note that this Code uses the tool method named writedocument to accept spdocumentlibrary references and file names. The implementation of this method uses the office open XML file format to create a Word document, and then writes it back to the document library. The writedocument method returns a spfile reference, which can then be used to access the splistitem related to the document. In this way, you can cancel the inheritance relationship and assign a group of unique permissions.
Figure 6 sets different permissions from its parent object
Copy code
Spweb site = spcontext. Current. Web;
Guid listid = site. Lists. Add ("proposals ",
"Library DESC ",
Splisttemplatetype. documentlibrary );
Spdocumentlibrary doclib = (spdocumentlibrary) web. Lists [listid];
Doclib. onquicklaunch = true;
Doclib. Update ();
Spfile doc1 = writedocument (doclib, "Adventure works merger.docx ");
Doc1.item. breakroleinheritance (false );
Spgroup group = web. Groups ["litware contact managers"];
Sproleassignment assigncontriment = new sproleassignment (group );
Sproledefinition rolecontitions = This. Web. roledefinitions ["Contribute"];
Assigncontribute. roledefinitionbindings. Add (rolecontiings );
Doc1.item. roleassignments. Add (assigncontriments );
Doc1.item. Update ();
Conclusion
I know that today's discussion is a little hasty, just to give you a general understanding of the WSS security model. I mainly introduced how WSS uses the configuration file in the "user information list" to track external security subjects at the website set layer, and how the WSS uses the spuser object to represent these external security subjects in the WSS object model. In addition, I demonstrated how WSS supports WSS groups and introduced some programming skills for Elevation of Privilege and simulating WSS users. These skills provide you with the flexibility and power you need when developing an application for practical work.
Although WSS relies on basic component systems for identity authentication, it does play its role in authorization and access control. The WSS authorization model is based largely on a permission naming set called a permission level or role. The permission level can be assigned to the spuser object. However, in actual applications, you should assign the permission level to the WSS group.
Browse the code online
Code download location:Officespace2008_02.exe(209 KB)