该例子包含的技巧如下:
1. 如何在Event Handler中获取List Item.
2. 如何impersonate另一个用户, 不使用RunWithElevatedPrivilages. 这里进行了包装, 拷贝类, 直接用就可以.
3. 修改一个item的permissions
4. 在web site中创建一个新的permission role
5. 检查一个role是否存在, 这是一个很棒的trick. 读取SPWeb.RoleDefinitions.Xml, 在其中寻找/Role[@Name='" + roleName + "']匹配的节点. 没找到就是没有.
6. 告诉你如何自己写log的小代码例子.
using System; using System.Globalization; using System.ComponentModel; using System.IO; using System.Data; using System.Text; using System.Xml; using System.Collections; using System.Configuration; using System.Diagnostics; using System.Web; using System.Security; using System.Security.Policy; using System.Security.Principal; using System.Security.Permissions; using System.Runtime.InteropServices; using Microsoft.SharePoint; namespace SharePointTips.SharePoint.Samples.EventHandlers { /// <summary> /// This is the event receiver that traps the item added event of the sharepoint list it is attached to. /// </summary> class ListItemSecuritySetter:SPItemEventReceiver { #region constants /// <summary> /// defines the permission set for editors. /// </summary> const SPBasePermissions c_EditorPermissions = SPBasePermissions.EditListItems | SPBasePermissions.ViewListItems; /// <summary> /// defines the permission set for readers /// </summary> const SPBasePermissions c_ReaderPermissions = SPBasePermissions.ViewListItems; /// <summary> /// The name of the role that will be created for the author /// </summary> const string c_AuthorRoleName = "Item Author and Editor"; /// <summary> /// The name of the role that will be created for the reader /// </summary> const string c_ReaderRoleName = "Item Reader"; /// <summary> /// Debug mode writes almost every action to the file log. In production switch this to false. /// Recommend changing that to read from a configuration file /// </summary> const bool DEBUG_MODE = true; /// <summary> /// The page where the log file will be created. Recommend changing that to read from a configuration file /// </summary> const string c_logFileFolder = @"c:\temp"; #endregion #region class properties /// <summary> /// Returns the name of the log file to be used (file name only - not path) /// The string returned will contain {0} and {1} that should be replaced with list name and site name. /// </summary> private string LogFileName { get { return "ListItemSecuritySetter-{0}-{1}-" + DateTime.Now.Year.ToString() + DateTime.Now.Month.ToString() + DateTime.Now.Day.ToString() + ".htm"; } } #endregion #region local variables HTMLFileLogging log; #endregion #region event handler event trapping /// <summary> /// The sharepoint event for ItemAdded /// </summary> /// <param name="properties"></param> public override void ItemAdded(SPItemEventProperties properties) { //run the default event handlers on the item base.ItemAdded(properties); //create the log handler object log = new HTMLFileLogging(c_logFileFolder, string.Format(LogFileName,properties.ListTitle,properties.OpenWeb().Title) , true, false); this.WriteToLog("ItemAdded was triggered",true); //if in debug mode, write all the properties in the item to the log if(DEBUG_MODE) WriteItemPropertiesToLog(properties.ListItem); this.WriteToLog("Setting permissions", true); try { //impersonate an administrator who can change permissions on list items in the list ImpersonationUtility imp = ImpersonationUtility.ImpersonateAdmin(); //open the spweb object to get the token for the user using (SPWeb webOrigUser = properties.OpenWeb()) { //get the token for the impersonation user (this will get the user that the ImpersonationUtility is using) SPUserToken token = webOrigUser.AllUsers[WindowsIdentity.GetCurrent().Name].UserToken; //reopen the spweb object with the new token - full impersonation! using (SPSite site = new SPSite(properties.SiteId, token)) { using (SPWeb web = site.OpenWeb(properties.RelativeWebUrl)) { //call the function that changes the permissions on the list item. //Do not use properties.ListItem since that will break impersonation! SetAuthorAsOnlyEditor(web.Lists[properties.ListId].GetItemById(properties.ListItemId)); } } } } catch (Exception ex) { this.WriteToLog("Setting permissions encountered an error: " + ex.Message + Environment.NewLine + ex.ToString(), true); } } #endregion #region custom functions /// <summary> /// Loops over the item properties and prints them to the log file. should only be called in debug mode! /// </summary> /// <param name="item">the list item that is currently handled</param> private void WriteItemPropertiesToLog(SPListItem item) { this.WriteToLog("Item Properties:",false); foreach(SPField field in item.Fields) { try { this.WriteToLog(field.Title + " : " + item[field.InternalName].ToString(), false); } catch { } } } /// <summary> /// Function sets the permission on the list item so that the author has edit permission on the item, /// and all other people with access to the document library can only read. /// Relies on the values in the constants c_EditorPermissions and c_ReaderPermissions. /// </summary> /// <param name="item">the list item that is currently handled</param> private void SetAuthorAsOnlyEditor(SPListItem item) { using (SPWeb currentWeb = item.Web) { this.WriteToLog("Getting author from item", true); //get the author from the item. 'Author' is a built-in property, so it should be in all lists. string authorValue = item["Author"].ToString(); SPFieldUserValue authorUserValue = new SPFieldUserValue(currentWeb, authorValue); SPUser authorUser = authorUserValue.User; this.WriteToLog("Got author name:'" + authorUser.Name + "', email: '" + authorUser.Email + "'", true); this.WriteToLog("Breaking role inheritance for the item", true); //break the security of the item from the list, but keep the permissions item.BreakRoleInheritance(true); //change the permissions of everyone with access to this item so they are readers only (c_ReaderPermissions) ChangeItemExistingRoles(item); this.WriteToLog("Creating role '" + c_AuthorRoleName + "' in the site if needed", true); //create a security definition in the web for an author-editor SPRoleDefinition def = CreateRoleInSite(currentWeb,c_AuthorRoleName,c_EditorPermissions); this.WriteToLog("Assigning role to the user", true); //Set the author user with the permissions defined SPRoleAssignment authorRole = new SPRoleAssignment(authorUser.LoginName, authorUser.Email, authorUser.Name, authorUser.Notes); this.WriteToLog("Binding the role assignment of the user to the definition", true); authorRole.RoleDefinitionBindings.Add(def); this.WriteToLog("Adding the role to the item", true); item.RoleAssignments.Add(authorRole); this.WriteToLog("Updating the item", true); item.Update(); this.WriteToLog("Success!", true); } } /// <summary> /// This function will make everyone with access to the item a reader. /// Loops over all the roles that exist in the item, removes the bindings and adds the reader permission definition to them /// </summary> /// <param name="item">the list item currently handled</param> private void ChangeItemExistingRoles(SPListItem item) { //get, and if necessary create, the reader role SPRoleDefinition readerDef = CreateRoleInSite(item.Web, c_ReaderRoleName, c_ReaderPermissions); foreach (SPRoleAssignment roleAssignment in item.RoleAssignments) { //delete the existing permissions roleAssignment.RoleDefinitionBindings.RemoveAll(); //add the reader permission roleAssignment.RoleDefinitionBindings.Add(readerDef); roleAssignment.Update(); item.Update(); } } /// <summary> /// Gets and if necessary creates the role in the site. /// </summary> /// <param name="web">The site where the role should be created</param> /// <param name="roleName">The name of the role to create</param> /// <param name="permissions">The permission set to give the role. Example: /// SPBasePermissions.EditListItems | SPBasePermissions.ViewListItems</param> /// <returns></returns> private SPRoleDefinition CreateRoleInSite(SPWeb web,string roleName,SPBasePermissions permissions) { this.WriteToLog("Checking if role '"+roleName+"' exists in the web", true); //check that the role exists if (RoleExists(web, roleName)) { this.WriteToLog("Role exists in the web", true); //role exists - return it return web.RoleDefinitions[roleName]; } else { //role does not exist in the site- create it and return. this.WriteToLog("Role does not exist in the web. creating a new role", true); //Create the role definition in the web by the name specified in c_AuthorRoleName SPRoleDefinition def = new SPRoleDefinition(); def.BasePermissions = permissions; def.Name = roleName; this.WriteToLog("Adding the role to the FirstUniqueRoleDefinitionWeb", true); web.FirstUniqueRoleDefinitionWeb.RoleDefinitions.Add(def); this.WriteToLog("Updating the web", true); web.FirstUniqueRoleDefinitionWeb.Update(); web.FirstUniqueRoleDefinitionWeb.Dispose(); this.WriteToLog("Reopening the current web object", true); web = web.Site.OpenWeb(); this.WriteToLog("Verifying role is in current web", true); if (RoleExists(web, roleName)) return web.RoleDefinitions[roleName]; else { throw new Exception("Role does not exist?"); } } } /// <summary> /// This function checks the spweb objec to see if a specific role exists (by name) /// </summary> /// <param name="web">the spweb object for the site to contain the role.</param> /// <param name="roleName">the name of the role searched for</param> /// <returns></returns> private bool RoleExists(SPWeb web, string roleName) { this.WriteToLog("Loading the RoleDefinitions xml string:", true); this.WriteToLog(web.RoleDefinitions.Xml, true); //read the xml of the roledefinitions XmlDocument doc = new XmlDocument(); doc.LoadXml(web.RoleDefinitions.Xml); this.WriteToLog("Searching for the role in the xml", true); //search for the role with the name in the xml XmlNode node = doc.SelectSingleNode("//Role[@Name='"+roleName+"']"); //if the search returned null, the role does not exist if (node == null) return false; else return true; } /// <summary> /// writes a message to the log file /// </summary> /// <param name="message">The message to write</param> /// <param name="debugOnly">If true, the message will only get written when the code runs in debug mode.</param> private void WriteToLog(string message,bool debugOnly) { if (DEBUG_MODE || !debugOnly) log.WriteToLogFile(message); } #endregion } /// <summary> /// Handles simple file logging. Recommend switching to trace log. /// </summary> class HTMLFileLogging { #region class properties private string logFolderPath = @"c:\logs"; public string LogFolderPath { get { return logFolderPath; } set { if (Directory.Exists(value)) { logFolderPath = value; if (logFolderPath.EndsWith("\\")) { logFolderPath = logFolderPath.Remove(logFolderPath.Length); } } else { throw new DirectoryNotFoundException(); } } } private string logFileName = ""; public string LogFileName { get { return logFileName; } set { logFileName = value; } } public string LogFilePath { get { return this.LogFolderPath + "\\" + this.LogFileName; } } #endregion #region CTOR /// <summary> /// Create a HTMLFileLogging object /// </summary> /// <param name="folderPath">The path of the folder that will hold the log file (no file name)</param> /// <param name="fileName">The name of the file to create</param> /// <param name="createPath">When this is set to true and the folder does not exist, the code will create the folder.</param> /// <param name="deleteFile">When this is set to true and the file exists, the code will delete the file and create a new one</param> public HTMLFileLogging(string folderPath, string fileName, bool createPath, bool deleteFile) { this.LogFileName = fileName; if (createPath && !Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } this.LogFolderPath = folderPath; if (File.Exists(this.LogFilePath) && deleteFile) { File.Delete(this.LogFilePath); } } #endregion #region custom code /// <summary> /// Writes a string to the log file. /// </summary> /// <param name="message">a string to write. supports html tags.</param> public void WriteToLogFile(string message) { try { StreamWriter sw = new StreamWriter(this.LogFilePath,true); sw.WriteLine("<p>"); sw.WriteLine("<date>" + DateTime.Now.ToShortDateString()+ "</date> <time>" + DateTime.Now.ToLongTimeString() + "</time> <br /> <message>" + message + "</message>"); sw.WriteLine("</p>"); sw.Flush(); sw.Close(); } catch (Exception ex) { } } #endregion } /// <summary> /// Thanks to impersonation example of Victor Vogelpoel [Macaw] /// http://dotnetjunkies.com/WebLog/victorv/archive/category/2032.aspx /// </summary> public sealed class ImpersonationUtility { private static string ADMINDOMAINACCOUNT = @"domain\user";//CHANGE THIS! private static string ADMINDOMAIN = "domain";//CHANGE THIS! private static string ADMINACCOUNT = "user";//CHANGE THIS! private static string ADMINPASSWORD = "password";//CHANGE THIS! private WindowsImpersonationContext _wiContext; public IntPtr token; /// <summary> /// Private ctor. /// </summary> private ImpersonationUtility() { } /// <summary> /// Start impersonating the administrator. /// </summary> /// <returns>an ImpersonationUtility instance.</returns> public static ImpersonationUtility ImpersonateAdmin() { ImpersonationUtility imp = new ImpersonationUtility(); imp._ImpersonateAdmin(); return imp; } /// <summary> /// Undo the impersonation. /// </summary> public void Undo() { if (this._wiContext != null) { this._wiContext.Undo(); this._wiContext = null; } } private void _ImpersonateAdmin() { token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; try { // Only start admin impersonation if we're not the admin... if (String.Compare(ADMINDOMAINACCOUNT, WindowsIdentity.GetCurrent().Name, true, CultureInfo.InvariantCulture) != 0) { // Temporarily stop the impersonation started by Web.Config. // WindowsIdentity.Impersonate() will store the current identity (the // account of the requestor) and return to the account of the ApplicationPool // which is "DOMAIN\SPAdmin". this._wiContext = WindowsIdentity.Impersonate(IntPtr.Zero); // But somehow the reverted account "DOMAIN\SPAdmin" still does // not have enough privileges to access SharePoint objects, so // we're logging in DOMAIN\SPAdmin again... if (NativeMethods.LogonUserA(ADMINACCOUNT, ADMINDOMAIN, ADMINPASSWORD, NativeMethods.LOGON32_LOGON_INTERACTIVE, NativeMethods.LOGON32_PROVIDER_DEFAULT, ref token) != 0) { if (NativeMethods.DuplicateToken(token, 2, ref tokenDuplicate) != 0) { WindowsIdentity wi = new WindowsIdentity(tokenDuplicate); // NOTE: Impersonate may fail if account that tries to impersonate does // not hold the "Impersonate after Authentication" privilege // See local security policy - user rights assignment. // Note that the ImpersonationContext from the Impersonate() call // is ignored. Upon the Undo() call, the original account // will be reinstated. wi.Impersonate(); } else { throw new Win32Exception(Marshal.GetLastWin32Error(), "Impersonation: Error duplicating token after logon for user \"DOMAIN\\SPAdmin\""); } } else { throw new Win32Exception(Marshal.GetLastWin32Error(), "Impersonation: Error logging on user \"DOMAIN\\SPAdmin\""); } } } finally { if (token != IntPtr.Zero) NativeMethods.CloseHandle(token); if (tokenDuplicate != IntPtr.Zero) NativeMethods.CloseHandle(tokenDuplicate); } } } /// <summary> /// Thanks to impersonation example of Victor Vogelpoel [Macaw] /// http://dotnetjunkies.com/WebLog/victorv/archive/category/2032.aspx /// </summary> internal sealed class NativeMethods { private NativeMethods() { } [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern bool CloseHandle(IntPtr handle); public const int LOGON32_PROVIDER_DEFAULT = 0; public const int LOGON32_LOGON_INTERACTIVE = 2; public const int LOGON32_LOGON_NETWORK = 3; [DllImport("advapi32.dll")] public static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool RevertToSelf(); }
摘自:
Sample Event Handler to set Permissions
http://www.sharepoint-tips.com/2007/03/sample-event-handler-to-set-permissions.html