Have you ever found yourself in a situation where you are the Farm administrator and you need to add a user (perhaps yourself) as a site collection administrator but you don’t want to be the site collection owner because you need a business user for that, you just want to be added as a site collection administrator? Well, if you’ve tried to use the stsadm command adduser or tried to use the browser to add yourself (or someone else) as the site collection administrator you probably quickly found out that you can’t do it.
For some reason Microsoft chose to not allow farm administrators to be able to add a user as a site administrator – now, this makes no sense because as a farm administrator I have the rights to set any user (including myself) as the site owner, at which point that user is now a site administrator who can now add other users as site administrators. So why didn’t Microsoft make it so that a farm administrator can add users as site administrators even if they themselves are not listed as site administrators?
This was really annoying to me and prevented me from being able to set the security on our site collections without having to go through a lot of hoops. So, in order to get around this issue I decided to create a new command called gl-addsiteadmin
. The command is pretty simple, it just takes in a user login, user name, and email and then adds the user as a site admin to the specified site collection. If the user you are trying to add as a site admin is not yourself then I just go ahead and temporarily assign your account as a site owner, add the specified user as a site admin, and then reset the site owner (of course if you are already a site admin then I just simply set the user as a site admin – but that scenario can be handled by the adduser command that already exists).
If you are attempting to add yourself as a site admin (and you don’t want to be a site owner) then I have to use an internal method called AdministratorOperationMode which sets the SPSite object into a special mode that allows administrative functions to be performed. If you are familiar with the SPSiteAdministration
object and have ever disassembled it you would see that the constructor of this object calls this internal method so that it can perform admin functions on the SPSite object. What’s really strange is that Microsoft doesn’t expose the resultant SPSite object via the SPSiteAdministration
object and the AdministratorOperationMode method is not public.
I hope that one day this changes as the only way to perform admin level functions on the SPSite object is to use reflection to call the method manually (unless of course the SPSiteAdministration
object already exposes the require property, which, in this case it does not). Because I’m calling an internal method directly via reflection use of this command could put your environment into an unsupported state according to Microsoft so make sure that you understand what the command is doing and what your support options with Microsoft are.
That being said, I feel that you are very safe with this command as the internal method still preserves all the security checks (it makes sure you are in fact a farm administrator) and it is exactly what the SPSiteAdministration
object is doing so I can’t see the use of this as causing any issues (and the reflection call only comes into play if you are attempting to add your own account as a site admin – if you used a different account to run the command to add your account then you’ll never hit the reflection call). The code to do all this is detailed below:
1public override int Run(string command, StringDictionary keyValues, out string output)
2{
3 output = string.Empty;
4
5 InitParameters(keyValues);
6
7 if (Params["role"].UserTypedIn && Params["group"].UserTypedIn)
8 throw new ArgumentException(SPResource.GetString("IncompatibleParametersSpecified", new object[] { "role", "group" }));
9
10 string url = Params["url"].Value.TrimEnd('/');
11 string login = Params["userlogin"].Value;
12 string email = Params["useremail"].Value;
13 string username = Params["username"].Value;
14
15 SPWeb web = null;
16
17 using (SPSite site = new SPSite(url))
18 using (SPSiteAdministration adminSite = new SPSiteAdministration(url))
19 try
20 {
21 web = site.OpenWeb();
22
23 login = Utilities.TryGetNT4StyleAccountName(login, web.Site.WebApplication);
24
25 if (SPFarm.Local.CurrentUserIsAdministrator() || web.CurrentUser.IsSiteAdmin)
26 {
27 // First lets get our user object.
28 SPUser user = null;
29 try
30 {
31 user = web.AllUsers[login];
32 }
33 catch (SPException) { }
34
35 if (user == null)
36 {
37 web.SiteUsers.Add(login, email, username, string.Empty);
38 user = web.AllUsers[login];
39 }
40 if (user == null)
41 throw new SPException("User cannot be found.");
42
43
44 if (web.CurrentUser.IsSiteAdmin)
45 {
46 // This is the easy part - the calling user is a site admin so we can simply add the user
47 user.IsSiteAdmin = true;
48 user.Update();
49 }
50 else
51 {
52 // This is the hard part - we need to trick the system to allow us to add the user
53 // (not sure why Microsoft didn't allow farm administrators to add site admins when
54 // they can change site owners which produces the same affect but what if you don't
55 // want to change the site owner?).
56
57 try
58 {
59 // Give it a try first (shouldn't work but what the heck)
60 user.IsSiteAdmin = true;
61 user.Update();
62 }
63 catch (SPException)
64 {
65 if (web.CurrentUser.ID != user.ID)
66 {
67 // If we are adding a user that is not ourselves then we can simply temporarily set
68 // ourselves as a site owner, add the user, and then remove ourselves as the site owner.
69 // The reason why we can't use this approach if we are setting ourselves as a site admin
70 // is that as soon as we attempt to remove ourselves as an owner it will also make it so
71 // we are not longer a site admin.
72
73 // Note that we only do this to avoid the reflection call to the internal method at all costs (see below).
74
75 string originalSiteOwner = adminSite.OwnerLoginName;
76 adminSite.OwnerLoginName = web.CurrentUser.LoginName;
77
78 // We've changed the properties of the site object so we need to close and re-open our SPWeb object.
79 web.Close();
80 web.Dispose();
81
82 web = site.OpenWeb();
83
84 // Now that we have a new SPWeb object we can re-retrieve the SPUser object which we now know will exist
85 // so no need for null checks as the above already certified that it exists.
86 user = web.AllUsers[login];
87
88 user.IsSiteAdmin = true;
89 user.Update();
90
91 // Now we need to reset the original owner.
92 adminSite.OwnerLoginName = originalSiteOwner;
93 }
94 else
95 {
96 // It didn't work because we're not a site admin and we are trying to set ourselves
97 // as a site admin which means we can't use the site owner trick from above.
98 // Fortunately, Microsoft provides a method that you can call to put the site object
99 // into an Admin mode (AdministratorOperationMode). This is what is called by the
100 // SPSiteAdministration object's constructor so that farm administrators can set
101 // the owner and other information. Unfortunately Microsoft doesn't expose the
102 // site object via the SPSiteAdministration object nor do they make the AdministratorOperationMode
103 // method public (it's marked internal). So, we have to use reflection to set this flag.
104 // In my opinion this is a safe call to make as the property re-checks all the security
105 // to make sure that you have appropriate rights to administer the site. If Microsoft
106 // would have made the SPSite object available from the SPSiteAdministration object
107 // this would not be necessary.
108
109 PropertyInfo adminOpModeProp = site.GetType().GetProperty("AdministratorOperationMode",
110 BindingFlags.NonPublic |
111 BindingFlags.Instance |
112 BindingFlags.InvokeMethod |
113 BindingFlags.GetProperty);
114 adminOpModeProp.SetValue(site, true, null);
115
116 // We've changed the properties of the site object so we need to close and re-open our SPWeb object.
117 web.Close();
118 web.Dispose();
119
120 web = site.OpenWeb();
121
122 // Now that we have a new SPWeb object we can re-retrieve the SPUser object which we now know will exist
123 // so no need for null checks as the above already certified that it exists.
124 user = web.AllUsers[login];
125
126 user.IsSiteAdmin = true;
127 user.Update();
128
129 }
130 }
131 }
132
133 if (Params["role"].UserTypedIn)
134 {
135 SPRoleDefinition roleDefinition = null;
136 try
137 {
138 roleDefinition = web.RoleDefinitions[Params["role"].Value];
139 }
140 catch (ArgumentException) {}
141
142 if (roleDefinition == null)
143 throw new SPException("The specified role does not exist.");
144
145 SPRoleDefinitionBindingCollection roleDefinitionBindings = new SPRoleDefinitionBindingCollection();
146 roleDefinitionBindings.Add(roleDefinition);
147 SPRoleAssignment roleAssignment = new SPRoleAssignment(user);
148 roleAssignment.ImportRoleDefinitionBindings(roleDefinitionBindings);
149 web.RoleAssignments.Add(roleAssignment);
150 }
151 else if (Params["group"].UserTypedIn)
152 {
153 SPGroup group = null;
154 try
155 {
156 group = web.SiteGroups[Params["group"].Value];
157 }
158 catch (ArgumentException) {}
159
160 if (group == null)
161 throw new SPException("The specified group does not exist.");
162
163 group.AddUser(user);
164 }
165 }
166 else
167 {
168 throw new Exception("You need to be a site collection administrator or farm administrator to set this user as a site administrator.");
169 }
170 }
171 finally
172 {
173 if (web != null)
174 web.Dispose();
175 }
176
177 return 1;
178}
The command I created is detailed below.
Using the command is reasonably similar to using the adduser command. The only real difference is that I’m not requiring a role or group name to be specified and you don’t have to specify the siteadmin switch (naturally). The syntax of the command can be seen below:
C:\>stsadm -help gl-addsiteadmin
stsadm -o gl-addsiteadmin
Adds a user as a site admin (must be a farm administrator or a current site admin).
Parameters:
-url <url of site collection>
-userlogin <DOMAIN\user>
-useremail <someone@example.com>
-username <display name>
[-role <role name> / -group <group name>]
Here’s an example of how to set a user as a site administrator and put them in the “Full Control” role (note that it’s usually better to put users in groups then it is to assign roles directly to them):
stsadm –o gl-addsiteadmin -url "http://intranet/" -userlogin "domain\user" -useremail "someone@example.com" -username "Gary Lapointe" -role "Full Control"