MOSS MVP

I've moved my blog to http://blog.falchionconsulting.com!. Please update your links. This blog is no longer in use--you can find all posts and comments at my new blog; I will no longer be posting to this site and comments have been disabled.

Wednesday, November 28, 2007

Personalization Service Permissions

While walking another IT user through the SSP admin interface the other day I discovered that even though the user was a Farm Administrator that user was not able to get into certain pages within the SSP admin site (such as the profile properties page). Turns out that there's additional permissions that must be granted via the "Personalization Service Permissions" page located under the "User Profiles and My Sites" section. As my goal is to make all changes necessary for our upgrade scriptable I ended up having to create a new command to give our admin group the appropriate permissions: gl-setsspacl. The challenge with this one is that the API to manipulate this information is not a public interface - almost everything is marked internal. I have no idea why this is the case - it's really annoying (what should have been about 40 lines of code turned into about 110 lines because of all the reflection calls). I continue to be frustrated when trying to programmatically manipulate the SSP - why on earth they made so much internal is beyond me. The code to set the permissions is below (avert your eyes if you're easily scared - using reflection is such a pain!):

   1: public override int Run(string command, StringDictionary keyValues, out string output)
   2: {
   3:  output = string.Empty;
   4:  
   5:  InitParameters(keyValues);
   6:  
   7:  string sspname = Params["sspname"].Value;
   8:  string username = Params["user"].Value;
   9:  SharedServiceRights rights = GetUserRights(Params["rights"].Value.Split(','));
  10:  
  11:  ServerContext current = ServerContext.GetContext(sspname);
  12:  
  13:  //SharedServiceAccessControlList acl = SharedServiceAccessControlList.GetInstance(current);
  14:  Type sharedServiceAccessControlListType = Type.GetType("Microsoft.Office.Server.Infrastructure.SharedServiceAccessControlList, Microsoft.Office.Server, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
  15:  
  16:  MethodInfo sharedServiceAccessControlListMethodInfo =
  17:   sharedServiceAccessControlListType.GetMethod("GetInstance",
  18:             BindingFlags.NonPublic | 
  19:             BindingFlags.Public |
  20:             BindingFlags.Instance | 
  21:             BindingFlags.InvokeMethod |
  22:             BindingFlags.FlattenHierarchy |
  23:             BindingFlags.Static, 
  24:             null,
  25:             new Type[] { typeof(ServerContext) }, null);
  26:  object acl = sharedServiceAccessControlListMethodInfo.Invoke(null, new object[] { current });
  27:  
  28:  //SharedServiceAccessControlEntry aclEntry = acl[username];
  29:  PropertyInfo itemProp = acl.GetType().GetProperty("Item",
  30:              BindingFlags.NonPublic |
  31:              BindingFlags.Instance |
  32:              BindingFlags.InvokeMethod |
  33:              BindingFlags.GetProperty |
  34:              BindingFlags.Public);
  35:  SharedServiceAccessControlEntry aclEntry = (SharedServiceAccessControlEntry)itemProp.GetValue(acl, new object[] { username });
  36:  
  37:  if (aclEntry == null)
  38:  {
  39:   if (rights == SharedServiceRights.None)
  40:   {
  41:    output += "User does not currently have any rights assiged - nothing to do.";
  42:    return 0;
  43:   }
  44:  
  45:   // Adding a new user
  46:   aclEntry = new SharedServiceAccessControlEntry(username, rights);
  47:  
  48:   MethodInfo add =
  49:    acl.GetType().GetMethod("Add",
  50:          BindingFlags.NonPublic |
  51:          BindingFlags.Public |
  52:          BindingFlags.Instance |
  53:          BindingFlags.InvokeMethod |
  54:          BindingFlags.FlattenHierarchy,
  55:          null,
  56:          new Type[] { typeof(SharedServiceAccessControlEntry) }, null);
  57:  
  58:   //acl.Add(aclEntry);
  59:   add.Invoke(acl, new object[] { aclEntry });
  60:  }
  61:  else
  62:  {
  63:   if (rights == SharedServiceRights.None)
  64:   {
  65:    // Remove an existing user
  66:    MethodInfo remove =
  67:     acl.GetType().GetMethod("Remove",
  68:           BindingFlags.NonPublic |
  69:           BindingFlags.Public |
  70:           BindingFlags.Instance |
  71:           BindingFlags.InvokeMethod |
  72:           BindingFlags.FlattenHierarchy,
  73:           null,
  74:           new Type[] {typeof (string)}, null);
  75:  
  76:    //acl.Remove(username);
  77:    remove.Invoke(acl, new object[] { username });
  78:   }
  79:   else
  80:   {
  81:    // Modifying an existing user
  82:    aclEntry = new SharedServiceAccessControlEntry(username, rights);
  83:  
  84:    MethodInfo updateAccessControlEntry =
  85:     acl.GetType().GetMethod("UpdateAccessControlEntry",
  86:           BindingFlags.NonPublic |
  87:           BindingFlags.Public |
  88:           BindingFlags.Instance |
  89:           BindingFlags.InvokeMethod |
  90:           BindingFlags.FlattenHierarchy,
  91:           null,
  92:           new Type[] {typeof (SharedServiceAccessControlEntry)},
  93:           null);
  94:  
  95:    //acl.UpdateAccessControlEntry(username, aclEntry);
  96:    updateAccessControlEntry.Invoke(acl, new object[] {aclEntry});
  97:   }
  98:  }
  99:  
 100:  MethodInfo update =
 101:   acl.GetType().GetMethod("Update",
 102:         BindingFlags.NonPublic |
 103:         BindingFlags.Public |
 104:         BindingFlags.Instance |
 105:         BindingFlags.InvokeMethod |
 106:         BindingFlags.FlattenHierarchy,
 107:         null,
 108:         new Type[] {}, null);
 109:  // acl.Update();
 110:  update.Invoke(acl, null);
 111:  
 112:  return 1;
 113: }

And just to show how much simpler this would have been if the classes were marked as public:

   1: public override int Run(string command, StringDictionary keyValues, out string output)
   2: {
   3:  output = string.Empty;
   4:  
   5:  InitParameters(keyValues);
   6:  
   7:  string sspname = Params["sspname"].Value;
   8:  string username = Params["user"].Value;
   9:  SharedServiceRights rights = GetUserRights(Params["rights"].Value.Split(','));
  10:  
  11:  ServerContext current = ServerContext.GetContext(sspname);
  12:  
  13:  SharedServiceAccessControlList acl = SharedServiceAccessControlList.GetInstance(current);
  14:  SharedServiceAccessControlEntry aclEntry = acl[username];
  15:  
  16:  if (aclEntry == null)
  17:  {
  18:   if (rights == SharedServiceRights.None)
  19:   {
  20:    output += "User does not currently have any rights assiged - nothing to do.";
  21:    return 0;
  22:   }
  23:  
  24:   // Adding a new user
  25:   aclEntry = new SharedServiceAccessControlEntry(username, rights);
  26:   acl.Add(aclEntry);
  27:  }
  28:  else
  29:  {
  30:   if (rights == SharedServiceRights.None)
  31:   {
  32:    // Remove an existing user
  33:    acl.Remove(username);
  34:   }
  35:   else
  36:   {
  37:    // Modifying an existing user
  38:    aclEntry = new SharedServiceAccessControlEntry(username, rights);
  39:    acl.UpdateAccessControlEntry(username, aclEntry);
  40:   }
  41:  }
  42:  acl.Update();
  43:  
  44:  return 1;
  45: }
The syntax of the command can be seen below:
C:\>stsadm -help gl-setsspacl

stsadm -o gl-setsspacl

Set the personalization services permissions for an SSP.  Specify 'None' for rights to remove an existing user.

Parameters:
        -sspname <SSP name>
        -rights <comma separated: All | None | CreatePersonalSite, ManageAnalytics, ManageAudiences, ManageUserProfiles, SetPermissions, UsePersonalFeatures>
        -user <DOMAIN\name>
Note that the user parameter can refer to a group or a user. Here's an example of how to give a group all permissions:
stsadm -o gl-setsspacl -sspname SSP1 -rights All -user "domain\group1"
If you wish to remove a user or group then simply specify "None" for the rights. You can specify multiple rights by comma separating the values:
stsadm -o gl-setsspacl -sspname SSP1 -rights "UsePersonalFeatures, CreatePersonalSite" -user "domain\group1"

17 comments:

K&M Reports said...

Hi Gary,
Thanks for sharing this stuff it's great.

I am running into this error when running this extension.

Do you know what may be the issue?

C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN>stsa
dm -o setsspacl -sspname "Prod_SSP2" -rights none -user "NT AUTHORITY\Authenticated Users"

Exception has been thrown by the target of an invocation.

Gary Lapointe said...

That error is actually just hiding the actual error - I need to wrap the code in a try/catch(TargetInvocationException) and throw the inner exception so you can see what the real error is. I have a build with this change made but I'm in the middle of something so I can't push it out at the moment without breaking another command. If you have a machine in which you can compile the code then just wrap the code in the try/catch - otherwise I'm hoping to have a build out today or tomorrow.

Gary Lapointe said...

I've just pushed a new build out - if you try again with the latest you should be able to see the actual error message.

K&M Reports said...

New build worked like a champ, thanks a bunch Gary.

There is so many great utilities here I really appreciate you sharing them.

I will be checking in from time to time to see if you add one to enhance the extendvsinwebfarm operation :)

Regards,
Dennis

jrabbit said...

Minor bug: if you enter an invalid SSP name, you get an "Object reference not set to an instance of an object" instead of a more specific error message.

p.s. Thanks for the fix to gl-createwebapp and the schedule audience recompilation commands.

Gary Lapointe said...

Thanks for the feedback - I just fixed it locally and will try to get it out with the next build.

Kevin Hughes said...

Unfortunately, whenever I attempt to run this command I get an error regarding attempting an unauthorized command.
The account under which context I am running the command is a member of the farm administrators group and has full control (via web app policy) over the SSP.

Any clue what is going on?

Gary Lapointe said...

The SSP has it's own set of permissions you need to configure via the personalization services permissions link.

Kevin Hughes said...

I thought the gl-setsspacl command was supposed to be setting the personalization services permissions. Are you saying I need to set the permissions before I can set the permissions?

Gary Lapointe said...

If you are using the account that you used to create the SSP then you will have the rights you need to set the permissions. If not then you will need to grant your current user rights to manage permissions.

Ani said...

Gary

Can you please tell me where I can get or download this extension for adding/removing personalized permission for mysite?

Gary Lapointe said...

You can find it on the downloads page.

Ani said...

What is the file name to download

Ani

Ani said...

I installed setsspacl command and tested with windows user name, it works perfectly.

But when I added with form based user, it give me error "The specified user ro group not found". It seems it wont support for formbased users, it support only windows based users?

Please confirm

Ani

Gary Lapointe said...

I've not done any FBA testing so it's a good chance it won't work.

mjcarrabine said...

Hi Gary,
Thanks for this site, it is invaluable to any SharePoint deployment.

I am using the deployment batch files from your site, and I am running the install from start to finish using the SPAdmin account with the permissions you specify, and everything goes smoothly until I get to this command. Then I get this error.

Cannot open database "SharePoint_SSP1_Content" requested by the login. The login
failed.
Login failed for user MyDomain\SPAdmin'.

This user does not have any rights in that database (should it?), but I can update the SSP permissions through central admin with this account without any issues. I am using NTLM, not kerberos if it makes any difference.

Any thoughts? I can always do this step through the UI, but I'd hate to break a perfect streak of scripting everything [made possible with the help of this site]

Gary Lapointe said...

mjcarrabine - I've seen this issue with SP2+ deployments and have not yet found a solution beyond just manually granting the rights and then rerunning the script from the point where it failed. Not sure if a CU fixes the issue or not as I haven't had time to look into it.