通过工作流程提供新网站集的权限

时间:2010-02-03 09:10:05

标签: access-denied

我陷入了一种非常奇怪的境地。

我有一个工作流程,用于在我的网络应用程序上配置新网站。此工作流使用一个自定义工作流活动来使用后续语句来配置站点。

---为了清楚起见省略了其他代码----

SPSiteCollection.Add()

当我的应用程序池帐户与Central Admin应用程序池帐户不同时,此语句会抛出followign异常。

  

访问被拒绝。 (HRESULT异常:0x80070005(E_ACCESSDENIED))   在   Microsoft.SharePoint.SPGlobal.HandleUnauthorizedAccessException(UnauthorizedAccessException   ex)at   Microsoft.SharePoint.Library.SPRequest.CreateSite(GUID   gApplicationId,String bstrUrl,Int32   lZone,Guid gSiteId,Guid gDatabaseId,   String bstrDat

经过大量的谷歌搜索和调查结果后,我已将其归结为Applicaiton Pool帐户权限。

工作流代码始终在系统帐户(应用程序池标识)下运行。为了创建新的SharePoint网站集,应用程序池需要访问“SharePoint_Config”数据库。

当我的Web应用程序在Central Admin的应用程序池凭据下运行时,它具有对配置数据库的所有访问权限。但是当我在任何其他具有较少权限的应用程序池身份下运行时。即使我将DBO权限授予配置数据库中的应用程序池帐户,它也会引发异常。

我的应用程序事件日志包含以下条目: -

  

事件来源:Windows SharePoint   服务3事件类别:数据库   事件ID:3760日期:2010年2月3日   时间:上午2:36:16用户:N / A.   电脑:SHAREPOINT20描述:   SQL数据库'SharePoint_Config'上   SQL Server实例'houspsr001'没有   找到。其他错误信息   来自SQL Server的内容包含在下面。

     

无法打开数据库   请求“SharePoint_Config”   登录。登录失败。登录失败   对于用户'DOMAIN \ WebAppPool'。

     

有关详细信息,请参阅“帮助和”   支持中心在   http://go.microsoft.com/fwlink/events.asp

我的问题是......在中央管理员的应用程序池帐户下运行此类代码是否可行。

对此有任何解决方法....?

我的问题

2 个答案:

答案 0 :(得分:1)

最后,访问被拒绝的问题已经解决。正如我在上一封电子邮件中提出的那样,问题是由于对我的应用程序池标识的许可不足。

  • 中央管理员在不同的应用程序池标识下运行
  • Web应用程序在不同的应用程序池标识下运行。

我的工作流程使用ElevatedPrevilages来设置网站集,并且它曾经从数据库中获取Access Denied,因为它没有修改SharePoint_Config数据库的权限。

分辨率 为了解决此问题,我必须模拟Central Admin的应用程序池标识。以下是模拟Central Admin应用程序池用户所需的方法。

   #region Application Pool Identity Impersonate

        protected static WindowsIdentity CreateIdentity(string User, string Domain, string Password)
        {
            // The Windows NT user token.
            IntPtr tokenHandle = new IntPtr(0);

            const int LOGON32_PROVIDER_DEFAULT = 0;
            const int LOGON32_LOGON_NETWORK = 3;

            tokenHandle = IntPtr.Zero;

            // Call LogonUser to obtain a handle to an access token.
            int returnValue = LogonUser(User, Domain, Password,LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT,out tokenHandle);

            //Check if the logon user method succeeded
            if (returnValue <= 0)
            {
                int ret = Marshal.GetLastWin32Error();
                throw new Exception("LogonUser failed with error code: " + ret);
            }

            //System.Diagnostics.Debug.WriteLine("Created user token: " + tokenHandle);

            //The WindowsIdentity class makes a new copy of the token.
            //It also handles calling CloseHandle for the copy.
            WindowsIdentity id = new WindowsIdentity(tokenHandle);
            CloseHandle(tokenHandle);
            return id;
        }

        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern int LogonUser(
            string lpszUsername,
            string lpszDomain,
            string lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            out IntPtr phToken
            );
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern int ImpersonateLoggedOnUser(
            IntPtr hToken
        );

        [DllImport("advapi32.dll", SetLastError = true)]
        static extern int RevertToSelf();

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern int CloseHandle(IntPtr hObject);

        #endregion

然后我创建网站集的代码如下: -

//Impersonate the logged in user, ApplicationUser, LoginDomain and Password are exposed as property of the class.

WindowsImpersonationContext wiContext = CreateIdentity(this.ApplicationPoolUser, this.LoginDomain, this.SystemPassword).Impersonate();



//Provision new site collection and update the property for new site collection url.

using (SPSite newSiteCollection = spSiteColl.Add(SUGGESTEDURL, TITLE, DESC, LCID, WEBTEMPLATE, PRIMARYOWNER.LoginName, PRIMARYOWNER.Name, PRIMARYOWNER.Email, SECONDARYOWNER.LoginName, SECONDARYOWNER.Name, SECONDARYOWNER.Email))

{

this.SUGGESTEDURL = newSiteCollection.Url;

}



//Reset the impersonation.

wiContext.Undo();

答案 1 :(得分:1)

由于我不允许对Sudhir的答案发表评论,我发表了我的评论作为答案。我使用了与Sudhir提出的解决方案基本相同的代码。模仿有效,但它有安全漏洞。如果将密码存储为纯文本(托管)字符串,则可以将其移动到内存中,甚至因为分页而存储到硬盘中。这使得非常规的人很容易窥探你的证件。

因此建议将SecureString用于此目的。可以在MSDN上查找如何将其与SecureString结合使用。与Sudhir解决方案的主要区别在于使用不同的LogonUser重载,即

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool LogonUser(String username, String domain, 
                                      IntPtr password, int logonType, 
                                      int logonProvider, ref IntPtr token);

并像这样使用它(此代码来自MSDN):

// Marshal the SecureString to unmanaged memory.
passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(pwdSecureString);

// Call LogonUser, passing the unmanaged (and decrypted) copy of
// the SecureString password.
returnValue = LogonUser(userName, domainName, passwordPtr,
                        LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, 
                        ref tokenHandle);

// Zero-out and free the unmanaged string reference.
Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);

这样,密码只在我们用它来进行用户登录之前加密。之后,明文密码立即从内存中释放出来。