The SharePoint API has, for the most port, elegantly deprecated obsolete functionality from 2003 to 2007. This allows legacy code to continue to compile, but still urges you forward to use new methods and procedures. One of the areas of the API that has been heavily overhauled is the permission classes.
The two new ones, SPRoleAssignment and SPRoleDefinition, are more complicated - and therefore more robust - than their predecessors. They allow you to create a new permission level (a "role definition") for an SPWeb and assign (a "role assignment") principles (users and groups) to it.
Unfortunately, there are some fun "gotchas" that you need to be aware of when customizing granular permissions. (Which sucks: one of my favorite enhancements of 2007 is the fact that permissions are broken out much better than in 2003.)
If you jump right in with the API, here's what your code will probably look like this:
The convention is fairly straight forward. Create new role definition, pretty it up, string its permissions together, and add it to the web. Then a role assignment combines this definition with a principle, and that is added to web as well. This makes quick work out of customizing permissions pretty much however you want to.
But then try running this code! You'll have not one, but two obnoxious exceptions in your way. This is really what I want to get after in this post.
The first exception goes something like this: "You cannot customize permission levels in a web site with inherited permission levels." This is tantamount to the message box you get in the settings' permission pages on the UI telling you that you have to break permission inheritance before changing security.
I guess that makes sense, but come on! The implicit action of customizing security - which is a HUGE concept and a part of every single SharePoint gig I've ever done - shouldn't require an explicit declaration to do so. That would be like Visual Studio bitching about changing code in a derived class: just because I inherit something doesn't mean I need to say "Please" or "Yes, I'm sure" when I inevitably elect to extend it!
Once you get past this one, the second issue is even more nebulous. Here's the exception: "You cannot grant a user a permission level that is not attached to a web." Looking at the code above, this blows up on line #18. I assumed that the problem was that the objects (in particular, the web variable) needed to be persisted before the new permission level (the def variable) could be used. (Which was a dumb idea, since I've never had to do this before. However, this is always my initial panic knee-jerk reaction to mysterious API nuances.)
So I first tried calling web.update() first. Didn't work. I then let the web object go out of scope, and opened the site back up, attempting line #'s 17 - 19 using a brand new web object. Same exception. I even made sure that the principle used in the SPRoleAssignment already existed in the web's site users collection (which isn't required).
So I thought long and hard about the exact verbiage in the exception message. The word "attached" is what really bothered me. What does that mean? "...attached to a web." Perhaps it needs to actually be referenced from the web's SPRoleDefinition collection and not just a loose variable?
Well guess what, folks? That was it! Change line #18 above to this:
And it works! Son of a B!