aboutsummaryrefslogtreecommitdiff
path: root/src/test/burn
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2022-04-28 21:02:23 -0500
committerSean Hall <r.sean.hall@gmail.com>2022-04-29 00:23:14 -0500
commit5b04bce6567855325810bc4e6bcd2f6e05b329c7 (patch)
treea36e140d36c83a2ef7e81a88d941dd792bef3f55 /src/test/burn
parent681da11cfc9a266304b47b88843cb8a365015c63 (diff)
downloadwix-5b04bce6567855325810bc4e6bcd2f6e05b329c7.tar.gz
wix-5b04bce6567855325810bc4e6bcd2f6e05b329c7.tar.bz2
wix-5b04bce6567855325810bc4e6bcd2f6e05b329c7.zip
Port UtilExtension.UserTests from wix3.
Diffstat (limited to 'src/test/burn')
-rw-r--r--src/test/burn/WixTestTools/UserVerifier.cs348
-rw-r--r--src/test/burn/WixTestTools/WixTestTools.csproj2
2 files changed, 350 insertions, 0 deletions
diff --git a/src/test/burn/WixTestTools/UserVerifier.cs b/src/test/burn/WixTestTools/UserVerifier.cs
new file mode 100644
index 00000000..b5218a79
--- /dev/null
+++ b/src/test/burn/WixTestTools/UserVerifier.cs
@@ -0,0 +1,348 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixTestTools
4{
5 using System;
6 using System.Text;
7 using System.DirectoryServices;
8 using System.DirectoryServices.AccountManagement;
9 using System.Security.Principal;
10 using Xunit;
11
12 /// <summary>
13 /// Contains methods for User account verification
14 /// </summary>
15 public static class UserVerifier
16 {
17 public static class SIDStrings
18 {
19 // Built-In Local Groups
20 public static readonly string BUILTIN_ADMINISTRATORS = "S-1-5-32-544";
21 public static readonly string BUILTIN_USERS = "S-1-5-32-545";
22 public static readonly string BUILTIN_GUESTS = "S-1-5-32-546";
23 public static readonly string BUILTIN_ACCOUNT_OPERATORS = "S-1-5-32-548";
24 public static readonly string BUILTIN_SERVER_OPERATORS = "S-1-5-32-549";
25 public static readonly string BUILTIN_PRINT_OPERATORS = "S-1-5-32-550";
26 public static readonly string BUILTIN_BACKUP_OPERATORS = "S-1-5-32-551";
27 public static readonly string BUILTIN_REPLICATOR = "S-1-5-32-552";
28
29 // Special Groups
30 public static readonly string CREATOR_OWNER = "S-1-3-0";
31 public static readonly string EVERYONE = "S-1-1-0";
32 public static readonly string NT_AUTHORITY_NETWORK = "S-1-5-2";
33 public static readonly string NT_AUTHORITY_INTERACTIVE = "S-1-5-4";
34 public static readonly string NT_AUTHORITY_SYSTEM = "S-1-5-18";
35 public static readonly string NT_AUTHORITY_Authenticated_Users = "S-1-5-11";
36 public static readonly string NT_AUTHORITY_LOCAL_SERVICE = "S-1-5-19";
37 public static readonly string NT_AUTHORITY_NETWORK_SERVICE = "S-1-5-20";
38 }
39
40 /// <summary>
41 /// Create a local user on the machine
42 /// </summary>
43 /// <param name="userName"></param>
44 /// <param name="password"></param>
45 /// <remarks>Has to be run as an Admin</remarks>
46 public static void CreateLocalUser(string userName, string password)
47 {
48 DeleteLocalUser(userName);
49 UserPrincipal newUser = new UserPrincipal(new PrincipalContext(ContextType.Machine));
50 newUser.SetPassword(password);
51 newUser.Name = userName;
52 newUser.Description = "New test User";
53 newUser.UserCannotChangePassword = true;
54 newUser.PasswordNeverExpires = false;
55 newUser.Save();
56 }
57
58 /// <summary>
59 /// Deletes a local user from the machine
60 /// </summary>
61 /// <param name="userName">user name to delete</param>
62 /// <remarks>Has to be run as an Admin</remarks>
63 public static void DeleteLocalUser(string userName)
64 {
65 UserPrincipal newUser = GetUser(String.Empty, userName);
66 if (null != newUser)
67 {
68 newUser.Delete();
69 }
70 }
71
72 /// <summary>
73 /// Verifies that a user exisits or not
74 /// </summary>
75 /// <param name="domainName">domain name for the user, empty for local users</param>
76 /// <param name="userName">the user name</param>
77 public static bool UserExists(string domainName, string userName)
78 {
79 UserPrincipal user = GetUser(domainName, userName);
80
81 return null != user;
82 }
83
84 /// <summary>
85 /// Sets the user information for a given user
86 /// </summary>
87 /// <param name="domainName">domain name for the user, empty for local users</param>
88 /// <param name="userName">the user name</param>
89 /// <param name="passwordExpired">user is required to change the password on first login</param>
90 /// <param name="passwordNeverExpires">password never expires</param>
91 /// <param name="disabled">account is disabled</param>
92 public static void SetUserInformation(string domainName, string userName, bool passwordExpired, bool passwordNeverExpires, bool disabled)
93 {
94 UserPrincipal user = GetUser(domainName, userName);
95
96 Assert.False(null == user, String.Format("User '{0}' was not found under domain '{1}'.", userName, domainName));
97 user.PasswordNeverExpires = passwordNeverExpires;
98 user.Enabled = !disabled;
99 if (passwordExpired)
100 {
101 user.ExpirePasswordNow();
102 }
103 else
104 {
105 // extend the expiration date to a month
106 user.AccountExpirationDate = DateTime.Now.Add(new TimeSpan(30, 0, 0, 0, 0));
107 }
108 user.Save();
109 }
110
111 /// <summary>
112 /// Adds the specified user to the specified local group
113 /// </summary>
114 /// <param name="userName">User to add</param>
115 /// <param name="groupName">Group to add too</param>
116 public static void AddUserToGroup(string userName, string groupName)
117 {
118 DirectoryEntry localMachine;
119 DirectoryEntry localGroup;
120
121 localMachine = new DirectoryEntry("WinNT://" + Environment.MachineName.ToString());
122 localGroup = localMachine.Children.Find(groupName, "group");
123 Assert.False(null == localGroup, String.Format("Group '{0}' was not found.", groupName));
124 DirectoryEntry user = FindActiveDirectoryUser(userName);
125 localGroup.Invoke("Add", new object[] { user.Path.ToString() });
126 }
127
128 /// <summary>
129 /// Find the specified user in AD
130 /// </summary>
131 /// <param name="UserName">user name to lookup</param>
132 /// <returns>DirectoryEntry of the user</returns>
133 private static DirectoryEntry FindActiveDirectoryUser(string UserName)
134 {
135 var mLocalMachine = new DirectoryEntry("WinNT://" + Environment.MachineName.ToString());
136 var mLocalEntries = mLocalMachine.Children;
137
138 var theUser = mLocalEntries.Find(UserName);
139 return theUser;
140 }
141
142 /// <summary>
143 /// Verifies the user information for a given user
144 /// </summary>
145 /// <param name="domainName">domain name for the user, empty for local users</param>
146 /// <param name="userName">the user name</param>
147 /// <param name="passwordExpired">user is required to change the password on first login</param>
148 /// <param name="passwordNeverExpires">password never expires</param>
149 /// <param name="disabled">account is disabled</param>
150 public static void VerifyUserInformation(string domainName, string userName, bool passwordExpired, bool passwordNeverExpires, bool disabled)
151 {
152 UserPrincipal user = GetUser(domainName, userName);
153
154 Assert.False(null == user, String.Format("User '{0}' was not found under domain '{1}'.", userName, domainName));
155
156 Assert.True(passwordNeverExpires == user.PasswordNeverExpires, String.Format("Password Never Expires for user '{0}/{1}' is: '{2}', expected: '{3}'.", domainName, userName, user.PasswordNeverExpires, passwordNeverExpires));
157 Assert.True(disabled != user.Enabled, String.Format("Disappled for user '{0}/{1}' is: '{2}', expected: '{3}'.", domainName, userName, !user.Enabled, disabled));
158
159 DateTime expirationDate = user.AccountExpirationDate.GetValueOrDefault();
160 bool accountExpired = expirationDate.ToLocalTime().CompareTo(DateTime.Now) <= 0;
161 Assert.True(passwordExpired == accountExpired, String.Format("Password Expired for user '{0}/{1}' is: '{2}', expected: '{3}'.", domainName, userName, accountExpired, passwordExpired));
162 }
163
164 /// <summary>
165 /// Verify that a givin user is member of a local group
166 /// </summary>
167 /// <param name="domainName">domain name for the user, empty for local users</param>
168 /// <param name="userName">the user name</param>
169 /// <param name="groupNames">list of groups to check for membership</param>
170 public static void VerifyUserIsMemberOf(string domainName, string userName, params string[] groupNames)
171 {
172 IsUserMemberOf(domainName, userName, true, groupNames);
173 }
174
175 /// <summary>
176 /// Verify that a givin user is NOT member of a local group
177 /// </summary>
178 /// <param name="domainName">domain name for the user, empty for local users</param>
179 /// <param name="userName">the user name</param>
180 /// <param name="groupNames">list of groups to check for membership</param>
181 public static void VerifyUserIsNotMemberOf(string domainName, string userName, params string[] groupNames)
182 {
183 IsUserMemberOf(domainName, userName, false, groupNames);
184 }
185
186 /// <summary>
187 ///
188 /// </summary>
189 /// <param name="SID">SID to search for</param>
190 /// <returns>AccountName</returns>
191 public static string GetLocalUserNameFromSID(string sidString)
192 {
193 SecurityIdentifier sid = new SecurityIdentifier(sidString);
194 NTAccount account = (NTAccount)sid.Translate(typeof(NTAccount));
195 return account.Value;
196 }
197
198 /// <summary>
199 /// Get the SID string for a given user name
200 /// </summary>
201 /// <param name="Domain"></param>
202 /// <param name="UserName"></param>
203 /// <returns>SID string</returns>
204 public static string GetSIDFromUserName(string Domain, string UserName)
205 {
206 string retVal = null;
207 string domain = Domain;
208 string name = UserName;
209
210 if (String.IsNullOrEmpty(domain))
211 {
212 domain = System.Environment.MachineName;
213 }
214
215 try
216 {
217 DirectoryEntry de = new DirectoryEntry("WinNT://" + domain + "/" + name);
218
219 long iBigVal = 5;
220 byte[] bigArr = BitConverter.GetBytes(iBigVal);
221 System.DirectoryServices.PropertyCollection coll = de.Properties;
222 object obVal = coll["objectSid"].Value;
223 if (null != obVal)
224 {
225 retVal = ConvertByteToSidString((byte[])obVal);
226 }
227 }
228 catch (Exception ex)
229 {
230 retVal = String.Empty;
231 Console.Write(ex.Message);
232 }
233
234 return retVal;
235 }
236
237 /// <summary>
238 /// converts a byte array containing a SID into a string
239 /// </summary>
240 /// <param name="sidBytes"></param>
241 /// <returns>SID string</returns>
242 private static string ConvertByteToSidString(byte[] sidBytes)
243 {
244 short sSubAuthorityCount;
245 StringBuilder strSid = new StringBuilder();
246 strSid.Append("S-");
247 try
248 {
249 // Add SID revision.
250 strSid.Append(sidBytes[0].ToString());
251
252 sSubAuthorityCount = Convert.ToInt16(sidBytes[1]);
253
254 // Next six bytes are SID authority value.
255 if (sidBytes[2] != 0 || sidBytes[3] != 0)
256 {
257 string strAuth = String.Format("0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}",
258 (short)sidBytes[2],
259 (short)sidBytes[3],
260 (short)sidBytes[4],
261 (short)sidBytes[5],
262 (short)sidBytes[6],
263 (short)sidBytes[7]);
264 strSid.Append("-");
265 strSid.Append(strAuth);
266 }
267 else
268 {
269 long iVal = (int)(sidBytes[7]) +
270 (int)(sidBytes[6] << 8) +
271 (int)(sidBytes[5] << 16) +
272 (int)(sidBytes[4] << 24);
273 strSid.Append("-");
274 strSid.Append(iVal.ToString());
275 }
276
277 // Get sub authority count...
278 int idxAuth = 0;
279 for (int i = 0; i < sSubAuthorityCount; i++)
280 {
281 idxAuth = 8 + i * 4;
282 uint iSubAuth = BitConverter.ToUInt32(sidBytes, idxAuth);
283 strSid.Append("-");
284 strSid.Append(iSubAuth.ToString());
285 }
286 }
287 catch (Exception ex)
288 {
289 Console.WriteLine(ex.Message);
290 return "";
291 }
292 return strSid.ToString();
293 }
294
295 /// <summary>
296 /// Verify that a given user is member of a local group
297 /// </summary>
298 /// <param name="domainName">domain name for the user, empty for local users</param>
299 /// <param name="userName">the user name</param>
300 /// <param name="shouldBeMember">whether the user is expected to be a member of the groups or not</param>
301 /// <param name="groupNames">list of groups to check for membership</param>
302 private static void IsUserMemberOf(string domainName, string userName, bool shouldBeMember, params string[] groupNames)
303 {
304 UserPrincipal user = GetUser(domainName, userName);
305 Assert.False(null == user, String.Format("User '{0}' was not found under domain '{1}'.", userName, domainName));
306
307 bool missedAGroup = false;
308 string message = String.Empty;
309 foreach (string groupName in groupNames)
310 {
311 try
312 {
313 bool found = user.IsMemberOf(new PrincipalContext(ContextType.Machine), IdentityType.Name, groupName);
314 if (found != shouldBeMember)
315 {
316 missedAGroup = true;
317 message += String.Format("User '{0}/{1}' is {2} a member of local group '{3}'. \r\n", domainName, userName, found ? String.Empty : "NOT", groupName);
318 }
319 }
320 catch (System.DirectoryServices.AccountManagement.PrincipalOperationException)
321 {
322 missedAGroup = true;
323 message += String.Format("Local group '{0}' was not found. \r\n", groupName);
324 }
325
326 }
327 Assert.False(missedAGroup, message);
328 }
329
330 /// <summary>
331 /// Returns the UserPrincipal object for a given user
332 /// </summary>
333 /// <param name="domainName">Domain name to look under, if Empty the LocalMachine is assumned as the domain</param>
334 /// <param name="userName"></param>
335 /// <returns>UserPrinicipal Object for the user if found, or null other wise</returns>
336 private static UserPrincipal GetUser(string domainName, string userName)
337 {
338 if (String.IsNullOrEmpty(domainName))
339 {
340 return UserPrincipal.FindByIdentity(new PrincipalContext(ContextType.Machine), IdentityType.Name, userName);
341 }
342 else
343 {
344 return UserPrincipal.Current;//.FindByIdentity(new PrincipalContext(ContextType.Domain,domainName), IdentityType.Name, userName);
345 }
346 }
347 }
348}
diff --git a/src/test/burn/WixTestTools/WixTestTools.csproj b/src/test/burn/WixTestTools/WixTestTools.csproj
index 7b726560..49e14f25 100644
--- a/src/test/burn/WixTestTools/WixTestTools.csproj
+++ b/src/test/burn/WixTestTools/WixTestTools.csproj
@@ -9,6 +9,8 @@
9 9
10 <ItemGroup> 10 <ItemGroup>
11 <PackageReference Include="Microsoft.Win32.Registry" /> 11 <PackageReference Include="Microsoft.Win32.Registry" />
12 <PackageReference Include="System.DirectoryServices" />
13 <PackageReference Include="System.DirectoryServices.AccountManagement" />
12 <PackageReference Include="System.Security.Principal.Windows" /> 14 <PackageReference Include="System.Security.Principal.Windows" />
13 <PackageReference Include="WixBuildTools.TestSupport" /> 15 <PackageReference Include="WixBuildTools.TestSupport" />
14 <PackageReference Include="WixToolset.Data" /> 16 <PackageReference Include="WixToolset.Data" />