The Export-SPWeb and Import-SPWeb is a tool for moving sites around, but with all the changes that have occured with SharePoint 2010 over the past two years, it seems some of the plumbing isn't connected correctly.
One of the issues we were seeing is that after importing a site, users are able to get to the site, but not to libraries underneath the site. You would see ULS logs like:
OriginalPermissionMask check failed. asking for 0x00020000, have 0x00000000
Nice list of the perm masks for reference:
Exception: System.UnauthorizedAccessException: Attempted to perform an unauthorized operation..
Access Denied for SITEURL. StackTrace: at Microsoft.SharePoint.Utilities.SPUtility.HandleAccessDenied(HttpContext context) at Microsoft.SharePoint.Utilities.SPUtility.HandleAccessDenied(Exception ex) at Microsoft.SharePoint.SPWeb.GetWebPartPageContent(Uri pageUrl, Int32 pageVersion, PageView requestedView, HttpContext context, Boolean forRender, Boolean includeHidden, Boolean mainFileRequest, Boolean fetchDependencyInformation, Boolean& ghostedPage, Byte& verGhostedPage, String& siteRoot, Guid& siteId, Int64& bytes, Guid& docId, UInt32& docVersion, String& timeLastModified, Byte& level, Object& buildDependencySetData, UInt32& dependencyCount, Object& buildDependencies, SPWebPartCollectionInitialState& initialState, Object& oMultipleMeetingDoclibRootFolders, String& redirectUrl, Boolean& ObjectIsList, Guid& listId) at Microsoft.SharePoint.ApplicationRuntime.SPRequestModuleData.FetchWebPartPageInformationForInit(HttpContext context, SPWeb spweb, Boolean mainFileRequest, String path, Boolean impersonate, Boolean& fGhostedPage, Byte& verGhostedPage, Guid& docId, UInt32& docVersion, String& timeLastModified, SPFileLevel& spLevel, String& masterPageUrl, String& customMasterPageUrl, String& webUrl, String& siteUrl, Guid& siteId, Object& buildDependencySetData, SPWebPartCollectionInitialState& initialState, String& siteRoot, String& redirectUrl, Object& oMultipleMeetingDoclibRootFolders, Boolean& objectIsList, Guid& listId, Int64& bytes) at Microsoft.SharePoint.ApplicationRuntime.SPRequestModuleData.GetFileForRequest(HttpContext context, SPWeb web, Boolean exclusion, String virtualPath) at Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.InitContextWeb(HttpContext context, SPWeb web) at Microsoft.SharePoint.WebControls.SPControl.SPWebEnsureSPControl(HttpContext context) at Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.GetContextWeb(HttpContext context) at Microsoft.SharePoint.ApplicationRuntime.SPRequestModule.PostResolveRequestCacheHandler(Object oSender, EventArgs ea) at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error) at System.Web.HttpApplication.BeginProcessRequestNotification(HttpContext context, AsyncCallback cb) at System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr managedHttpContext, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr managedHttpContext, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(IntPtr managedHttpContext, IntPtr nativeRequestContext, IntPtr moduleData, Int32 flags) at System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(IntPtr managedHttpContext, IntPtr nativeRequestContext, IntPtr moduleDa…
Looking at the permissions for the library (Site Actions->Site Permissions OR Library->Library Permissions), you WOULD see what you are supposed to see as far as permissons are concerned (even the CheckPermissions tool shows what we want). However, looks are DECEIVEING! Also, when we try to break permissions, it would continue to show that the list was inheriting from the parent. No errors would be thrown in the UI. When using powershell and tracing the verbose calls in ULSViewer you would actually see some more revealing details:
05/10/2012 09:23:21.29 PowerShell.exe (0x1180) 0x0D3C SharePoint Foundation General bj8s Medium setSecurityScopeUnique: start
05/10/2012 09:23:21.29 PowerShell.exe (0x1180) 0x0D3C SharePoint Foundation General bj8t Medium setSecurityScopeUnique: already unique
05/10/2012 09:23:21.29 PowerShell.exe (0x1180) 0x0D3C SharePoint Foundation General bj8w Medium setSecurityScopeUnique: finished
Notice the second line: "Already unique"…WTF…no its not! The UI shows it is inheriting permissions! So what is going on here?
Turning on the handy SQL Profiler, I did the following powershell on a "working" list:
$web = get-spweb http://blah
$list = $web.lists["Project Documents"]
What you see in profiler is a call to:
declare @p15 uniqueidentifier
exec proc_SecChangeToUniqueScope @SiteId='EFAF8E96-FA9F-4816-A965-CE7A8C6ED8AC',@WebId='E504097A-2BA1-400F-BCC2-5DE6B32E8615',@OldScopeId='3FB9F8DA-A957-4B82-B4AA-FECB3B1B4551',@CopyFromScopeId='3FB9F8DA-A957-4B82-B4AA-FECB3B1B4551',@Url=N'asi/Shared Documents',@DocId='00000000-0000-0000-0000-000000000000',@bIsWeb=0,@UserId=1073741823,@CopyAnonymousMask=1,@CopyRoleAssignments=1,@ClearSubScopes=0,@bBreakBySiteOwner=0,@ReturnAuditMask=1,@MaxScopeInList=50000,@NewScopeId=@p15 output,@RequestGuid='00000000-0000-0000-0000-000000000000'
When performing the same operation on a "broken" list (remember, from an imported web), you DO NOT see this call made. So I decided, lets look at that stored procedure and see what it does. The SP will actually look at the "Perms" table for a "ScopeId", then it will replicate what it needs in all the related tables to create a copy of a parent scope (actually doesn't have to be a parent, but the SharePoint team doesn't let us do this through the UIs – wouldn't that be handy!).
So what is the problem? Well…the problem centers on that pesky "ScopeId". What is happening is that SharePoint has TWO ways to get permissions information:
- Get the scopeid from the AllLists table (using urls or ids)
- Get the scopeid from the perms table (using urls or ids)
UPDATE: There is also a scopeid on the AllDocs table that also gets out of whack!
In some places the object model and the "obfuscated" SPRequest COM objects are making two different calls! One will look at the AllLists table, the other will look at the Perms table. This is the BUG. ALL access APIs are using the "Perms" table, all UI APIs (including the CheckPermissions tool) are using the "AllLists" table. They should look at the same place! What is happening is the scopeID of imported objects is being set to the parent webs scopeID! So even though the UI shows you what you WANT to see, the internals think that the permissions are that of the web's parent web! THAT'S NOT GOOD!
How do you fix this? Your not going to like the answer…none of the object model calls would reset the scopeid of the lists or webs. I had to go into the database and reset the mapping of the scopeIds. Once I did this, BAM! Everything started working again…
Second big bug I have found in SharePoint 2010 in the last two weeks.
SQL Queries to help you find things:
Find List Scopes:
SELECT tp_Title, tp_ScopeId
tp_WebId = 'GUID'
Find Doc Scopes:
SELECT WebId, DirName, LeafName, scopeid
where DirName like '%blah%'
Find Web Scopes:
SELECT ID, FullUrl, scopeid
where fullurl like '%blah%'
Find Scope Permissions:
declare @scopeid as varchar(max)
set @scopeid = 'A3A5D2C8-5624-4E2A-BE7A-7D51C26DB081'
where scopeid in (@scopeid)
FROM RoleAssignment ra, Roles r
where ScopeId = @scopeid
and ra.RoleId = r.RoleId
Follow me on twitter: @givenscj