包含了很多技巧的SharePoint Event Handler的例子

该例子包含的技巧如下:

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

posted on 2010-02-05 11:48  中道学友  阅读(1256)  评论(0编辑  收藏  举报

导航

技术追求准确,态度积极向上