Wednesday, October 06, 2010

Secure Store Service: Create Site-Collections avoiding UnauthorizedAccessException

The MSDN forums is full of questions on how to create a new site-collection from code, especially on what permissions are required to avoid the UnauthorizedAccessException "Access is denied" HRESULT: 0x80070005 error. Your code might work fine when running as you on your development server, and of course under the identity of the SharePoint farm account; but not when running under another application pool identity, or as a normal end-user or even as the SHAREPOINT\System account in workflows or event receivers using SPSecurity.RunWithElevatedPrivileges.

The simple answer is that CreateSite requires the same permissions as the SharePoint farm account. You certainly do not want to give all your end-users those permissions, so typically you would do a revert-to-self using RunWithElevatedPrivileges and run the site provisioning code as your SharePoint web-application application pool identity. Alas, adding the app-pool identity to the farm administrator group, giving it "full control" and "run as system" in web-app User Permission Policy, adding it to server local groups such as WSS_WPG or even to SQL Server db_owner and WSS_Content_Application_Pools database roles in the config and content databases, will not provide a configuration that works across all scenarios from web-parts to event receivers, workflows and integration tests.

Always try to assign a managed service account for the SP web-app application pool identity using Central Admin first or run GrantAccessToProcessIdentity in PowerShell. Still, getting access to the content database might not be enough for creating new site-collections, access to the configuration database is also needed for the process account.

The simple solution to this problem is to run all SharePoint web-applications using the farm account. This is definitely not recommended, and the Central Admin Health Analyzer will report the "The server farm account should not be used for other services" error in the security category.

A better alternative is to do a real RevertToSelf using WindowsImpersonationContext rather than the SharePoint RunWithElevatedPrivileges abstraction. This will require that your code knows the login name and password of the farm account. It is not best practice to store these in clear text in your code or config files, so I recommend using the SharePoint 2010 Secure Store Service to provide the farm account credentials to your code. Here's the code to implement impersonation in ASP.NET and the code to get credentials from the default secure store provider to get you started.

Use a Secure Store target application of type "Group" to store the Windows credentials of the farm account. Add only the required app-pool identities as target application "Members" when setting the target application access permissions, and then use RunWithElevatedPrivileges when retrieving the farm account credentials from the Secure Store Service.

So the final solution is this: retrieve the farm account credentials in your code using the app-pool identity, impersonate the farm account using those credentials, and then create a new site-collection from your code.

Note that there are also issues with claims-based authentication (CBA) when adding sites from code. The known workaround is to enable self-service site creation (SSSC) and use SelfServiceCreateSite from code.

Need to provision lists, pages and other stuff to your new site? Check out the SharePoint Site Configurator Feature over at CodePlex. Its a great resource for learning how to configure SharePoint sites.