That quirky SharePoint Object Model...
I had another chance yesterday to do battle with something in the SharePoint Object Model. These kinds of things are great fun :) You write some code in a Web Part (or something), it works when you are an admin … but if you are a contributor or a reader you get a login prompt :(
Ok, so you figure out that you do need Site admin rights to do the operation(s) you want to do … so you have a couple of options:
1. Impersonate an administrator (not nice as you need to get the credentials from somewhere and most just stick them in code = insecure/inflexible etc…)
2. Revert back to the Identity of the IIS Application Pool (which is more likely to have the permissions)
So you choose to implement option 2.
For how to do this see: Even Better Impersonation:
In some cases this will be enough, BUT in some cases this will not be enough. In your case it still does not work! Grrr....
Now you are hitting a known quirk in the SharePoint object model. Some operations in the object model will always use the credentials of the original calling context. If you are calling this code in an ASP.Net application the object model will revert back to the identity from the original HTTP Context and it will revert back to that identity all the time! There is no way to stop it from doing this … but there is a pretty nifty way to trick the object model to get around this quirk …
This problem does not occur when you use the Object Model outside of an Http Context E.g. in a console application. Why? … because the object model sees that there isn’t a http context and therefore does not have the original identity. SO …. How to exploit this?
AppDomains. By creating an additional AppDomain under the identity of your elevated user it isolates the object model from the http context. Thus thinking you are running without one and allowing the code to run under the identity that the AppDomain is running as.
By no means am I claiming this technique as my own :) There are excellent write-ups by Maurice Prather and Lois & Clarke:
Personally I think “Id, Ego and Superego” is easier to follow to begin with, and then be sure to read Maurice’s post as it is more detailed.
So what was I doing that caused me all this grief? I was writing an ASP.Net server control that I added to the AllItems.aspx page in a Document Library template I had customised. In there I wanted to get an object model reference to the document library that the AllItems.aspx page was for. Simple enough you would think ! :)
I tried getting the name of the document library from the URL to start with (a dirty filthy hack at the best of times), however this didn’t work as the name of the document library might be different from that in the URL if someone has re-named the document library.
So I drummed up this bit of code that I needed to run in the AppDomain as per above:
SPFile currentFile = currentWeb.GetFile(< use the value from control.PageUri.AbsolutePath as a param here>);
Guid documentLibraryID = currentFile.ParentFolder.ContainingDocumentLibrary;
The first line uses the URL to get a SPFile object for the AllItems.aspx page. Then it gets the GUID for the document library from the SPFile.ParentFolder. This gives you the GUID that you need to get the document library in the last line.
SPDocumentLibrary lib = (SPDocumentLibrary)currentWeb.Lists[documentLibraryID];
It is in the call to currentFile.ParentFolder that the object model reverts back to the original identity for some reason.
Hope this helps.