diff options
author | Sean Hall <r.sean.hall@gmail.com> | 2022-04-28 21:02:23 -0500 |
---|---|---|
committer | Sean Hall <r.sean.hall@gmail.com> | 2022-04-29 00:23:14 -0500 |
commit | 5b04bce6567855325810bc4e6bcd2f6e05b329c7 (patch) | |
tree | a36e140d36c83a2ef7e81a88d941dd792bef3f55 /src/test/burn | |
parent | 681da11cfc9a266304b47b88843cb8a365015c63 (diff) | |
download | wix-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.cs | 348 | ||||
-rw-r--r-- | src/test/burn/WixTestTools/WixTestTools.csproj | 2 |
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 | |||
3 | namespace 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" /> |