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!):
1public 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:
1public 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"