/// <summary> /// Writes on disk all the Users. /// </summary> /// <param name="users">The User list.</param> /// <remarks>This method does not lock resources, therefore a lock is need in the caller.</remarks> private void DumpUsers(UserInfo[] users) { lock (this) { BackupUsersFile(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < users.Length; i++) { LocalUserInfo u = (LocalUserInfo)users[i]; sb.Append(u.Username); sb.Append("|"); sb.Append(u.PasswordHash); sb.Append("|"); sb.Append(u.Email); sb.Append("|"); sb.Append(u.Active ? "ACTIVE" : "INACTIVE"); sb.Append("|"); sb.Append(u.DateTime.ToString("yyyy'/'MM'/'dd' 'HH':'mm':'ss")); // ADMIN|USER no more used in version 3.0 //sb.Append("|"); //sb.Append(u.Admin ? "ADMIN" : "USER"); if (!string.IsNullOrEmpty(u.DisplayName)) { sb.Append("|"); sb.Append(u.DisplayName); } sb.Append("\r\n"); } File.WriteAllText(GetFullPath(UsersFile), sb.ToString()); } }
/// <summary> /// Modifies a User. /// </summary> /// <param name="user">The Username of the user to modify.</param> /// <param name="newDisplayName">The new display name (can be <c>null</c>).</param> /// <param name="newPassword">The new Password (<c>null</c> or blank to keep the current password).</param> /// <param name="newEmail">The new Email address.</param> /// <param name="newActive">A value indicating whether the account is active.</param> /// <returns>The correct <see cref="T:UserInfo"/> object or <c>null</c>.</returns> /// <exception cref="ArgumentNullException">If <b>user</b> or <b>newEmail</b> are <c>null</c>.</exception> /// <exception cref="ArgumentException">If <b>newEmail</b> is empty.</exception> public UserInfo ModifyUser(UserInfo user, string newDisplayName, string newPassword, string newEmail, bool newActive) { if (user == null) { throw new ArgumentNullException("user"); } if (newEmail == null) { throw new ArgumentNullException("newEmail"); } if (newEmail.Length == 0) { throw new ArgumentException("New Email cannot be empty", "newEmail"); } lock (this) { LocalUserInfo local = LoadLocalInstance(user); if (local == null) { return(null); } UserInfo[] allUsers = GetUsers(); UsernameComparer comp = new UsernameComparer(); usersCache = null; for (int i = 0; i < allUsers.Length; i++) { if (comp.Compare(allUsers[i], user) == 0) { LocalUserInfo result = new LocalUserInfo(user.Username, newDisplayName, newEmail, newActive, user.DateTime, this, string.IsNullOrEmpty(newPassword) ? local.PasswordHash : Hash.Compute(newPassword)); result.Groups = allUsers[i].Groups; allUsers[i] = result; DumpUsers(allUsers); return(result); } } } return(null); }
/// <summary> /// Gets the complete list of Users. /// </summary> /// <returns>All the Users, sorted by username.</returns> public IEnumerable <UserInfo> GetUsers() { lock (this) { if (usersCache == null) { var groups = GetUserGroups(); var lines = File.ReadAllLines(GetFullPath(UsersFile)); var result = new UserInfo[lines.Length]; var splitter = new char[] { '|' }; string[] fields; for (var i = 0; i < lines.Length; i++) { fields = lines[i].Split(splitter, StringSplitOptions.RemoveEmptyEntries); // Structure (version 3.0 - file previously converted): // Username|PasswordHash|Email|Active-Inactive|DateTime[|DisplayName] var displayName = fields.Length == 6 ? fields[5] : null; result[i] = new LocalUserInfo(fields[0], displayName, fields[2], fields[3].ToLowerInvariant().Equals("active"), DateTime.Parse(fields[4]), this, fields[1]); result[i].Groups = GetGroupsForUser(result[i].Username, groups); } Array.Sort(result, new UsernameComparer()); usersCache = result; } return(usersCache); } }
/// <summary> /// Verifies the need for a data upgrade, and performs it when needed. /// </summary> private void VerifyAndPerformUpgrade() { // Load file lines // Parse first line (if any) with old (v2) algorithm // If parsing is successful, then the file must be converted // Conversion consists in removing the 'ADMIN|USER' field, creating the proper default groups and setting user membership // Structure v2: // Username|PasswordHash|Email|Active-Inactive|DateTime|Admin-User //string[] lines = File.ReadAllLines(GetFullPath(UsersFile)); // Use this method because version 2.0 file might have started with a blank line string[] lines = File.ReadAllText(GetFullPath(UsersFile)).Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if (lines.Length > 0) { bool upgradeIsNeeded = false; LocalUserInfo[] users = new LocalUserInfo[lines.Length]; bool[] oldStyleAdmin = new bool[lines.Length]; // Values are valid only if upgradeIsNeeded=true char[] splitter = new char[] { '|' }; for (int i = 0; i < lines.Length; i++) { string line = lines[i]; string[] fields = line.Split(splitter, StringSplitOptions.RemoveEmptyEntries); string displayName = null; if (fields.Length == 6) { if (fields[5] == "ADMIN" || fields[5] == "USER") { // Version 2.0 upgradeIsNeeded = true; oldStyleAdmin[i] = fields[5] == "ADMIN"; } else { // Version 3.0 with DisplayName specified oldStyleAdmin[i] = false; displayName = fields[5]; } } else { // Can be a version 3.0 file, with empty DisplayName oldStyleAdmin[i] = false; } users[i] = new LocalUserInfo(fields[0], displayName, fields[2], fields[3].ToLowerInvariant() == "active", DateTime.Parse(fields[4]), this, fields[1]); } if (upgradeIsNeeded) { // Dump users // Create default groups // Set membership for old users // Tell the host to set the permissions for the default groups string backupFile = GetFullPath(Path.GetFileNameWithoutExtension(UsersFile) + "_v2" + Path.GetExtension(UsersFile)); File.Copy(GetFullPath(UsersFile), backupFile); host.LogEntry("Upgrading users format from 2.0 to 3.0", LogEntryType.General, null, this); DumpUsers(users); UserGroup adminsGroup = AddUserGroup(host.GetSettingValue(SettingName.AdministratorsGroup), "Built-in Administrators"); UserGroup usersGroup = AddUserGroup(host.GetSettingValue(SettingName.UsersGroup), "Built-in Users"); for (int i = 0; i < users.Length; i++) { if (oldStyleAdmin[i]) { SetUserMembership(users[i], new string[] { adminsGroup.Name }); } else { SetUserMembership(users[i], new string[] { usersGroup.Name }); } } host.UpgradeSecurityFlagsToGroupsAcl(adminsGroup, usersGroup); } } }
/// <summary> /// Sets the group memberships of a user account. /// </summary> /// <param name="user">The user account.</param> /// <param name="groups">The groups the user account is member of.</param> /// <returns>The correct <see cref="T:UserGroup"/> object or <c>null</c>.</returns> /// <exception cref="ArgumentNullException">If <b>user</b> or <b>groups</b> are <c>null</c>.</exception> public UserInfo SetUserMembership(UserInfo user, string[] groups) { if (user == null) { throw new ArgumentNullException("user"); } if (groups == null) { throw new ArgumentNullException("groups"); } lock (this) { foreach (string g in groups) { if (FindGroup(g) == null) { return(null); } } LocalUserInfo local = LoadLocalInstance(user); if (local == null) { return(null); } UserGroup[] allGroups = GetUserGroups(); List <string> users; for (int i = 0; i < allGroups.Length; i++) { users = new List <string>(allGroups[i].Users); if (IsSelected(allGroups[i], groups)) { // Current group is one of the selected, add user to it if (!users.Contains(user.Username)) { users.Add(user.Username); } } else { // Current group is not bound with the user, remove user users.Remove(user.Username); } allGroups[i].Users = users.ToArray(); } groupsCache = null; usersCache = null; DumpUserGroups(allGroups); LocalUserInfo result = new LocalUserInfo(local.Username, local.DisplayName, local.Email, local.Active, local.DateTime, this, local.PasswordHash); result.Groups = groups; return(result); } }
/// <summary> /// Verifies the need for a data upgrade, and performs it when needed. /// </summary> private void VerifyAndPerformUpgrade() { // Load file lines // Parse first line (if any) with old (v2) algorithm // If parsing is successful, then the file must be converted // Conversion consists in removing the 'ADMIN|USER' field, creating the proper default groups and setting user membership // Structure v2: // Username|PasswordHash|Email|Active-Inactive|DateTime|Admin-User //string[] lines = File.ReadAllLines(GetFullPath(UsersFile)); // Use this method because version 2.0 file might have started with a blank line string[] lines = File.ReadAllText(GetFullPath(UsersFile)).Replace("\r", "").Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); if(lines.Length > 0) { bool upgradeIsNeeded = false; LocalUserInfo[] users = new LocalUserInfo[lines.Length]; bool[] oldStyleAdmin = new bool[lines.Length]; // Values are valid only if upgradeIsNeeded=true char[] splitter = new char[] { '|' }; for(int i = 0; i < lines.Length; i++) { string line = lines[i]; string[] fields = line.Split(splitter, StringSplitOptions.RemoveEmptyEntries); string displayName = null; if(fields.Length == 6) { if(fields[5] == "ADMIN" || fields[5] == "USER") { // Version 2.0 upgradeIsNeeded = true; oldStyleAdmin[i] = fields[5] == "ADMIN"; } else { // Version 3.0 with DisplayName specified oldStyleAdmin[i] = false; displayName = fields[5]; } } else { // Can be a version 3.0 file, with empty DisplayName oldStyleAdmin[i] = false; } users[i] = new LocalUserInfo(fields[0], displayName, fields[2], fields[3].ToLowerInvariant() == "active", DateTime.Parse(fields[4]), this, fields[1]); } if(upgradeIsNeeded) { // Dump users // Create default groups // Set membership for old users // Tell the host to set the permissions for the default groups string backupFile = GetFullPath(Path.GetFileNameWithoutExtension(UsersFile) + "_v2" + Path.GetExtension(UsersFile)); File.Copy(GetFullPath(UsersFile), backupFile); host.LogEntry("Upgrading users format from 2.0 to 3.0", LogEntryType.General, null, this); DumpUsers(users); UserGroup adminsGroup = AddUserGroup(host.GetSettingValue(SettingName.AdministratorsGroup), "Built-in Administrators"); UserGroup usersGroup = AddUserGroup(host.GetSettingValue(SettingName.UsersGroup), "Built-in Users"); for(int i = 0; i < users.Length; i++) { if(oldStyleAdmin[i]) { SetUserMembership(users[i], new string[] { adminsGroup.Name }); } else { SetUserMembership(users[i], new string[] { usersGroup.Name }); } } host.UpgradeSecurityFlagsToGroupsAcl(adminsGroup, usersGroup); } } }
/// <summary> /// Sets the group memberships of a user account. /// </summary> /// <param name="user">The user account.</param> /// <param name="groups">The groups the user account is member of.</param> /// <returns>The correct <see cref="T:UserGroup"/> object or <c>null</c>.</returns> /// <exception cref="ArgumentNullException">If <b>user</b> or <b>groups</b> are <c>null</c>.</exception> public UserInfo SetUserMembership(UserInfo user, string[] groups) { if(user == null) throw new ArgumentNullException("user"); if(groups == null) throw new ArgumentNullException("groups"); lock(this) { foreach(string g in groups) { if(FindGroup(g) == null) return null; } LocalUserInfo local = LoadLocalInstance(user); if(local == null) return null; UserGroup[] allGroups = GetUserGroups(); List<string> users; for(int i = 0; i < allGroups.Length; i++) { users = new List<string>(allGroups[i].Users); if(IsSelected(allGroups[i], groups)) { // Current group is one of the selected, add user to it if(!users.Contains(user.Username)) users.Add(user.Username); } else { // Current group is not bound with the user, remove user users.Remove(user.Username); } allGroups[i].Users = users.ToArray(); } groupsCache = null; usersCache = null; DumpUserGroups(allGroups); LocalUserInfo result = new LocalUserInfo(local.Username, local.DisplayName, local.Email, local.Active, local.DateTime, this, local.PasswordHash); result.Groups = groups; return result; } }
/// <summary> /// Modifies a User. /// </summary> /// <param name="user">The Username of the user to modify.</param> /// <param name="newDisplayName">The new display name (can be <c>null</c>).</param> /// <param name="newPassword">The new Password (<c>null</c> or blank to keep the current password).</param> /// <param name="newEmail">The new Email address.</param> /// <param name="newActive">A value indicating whether the account is active.</param> /// <returns>The correct <see cref="T:UserInfo"/> object or <c>null</c>.</returns> /// <exception cref="ArgumentNullException">If <b>user</b> or <b>newEmail</b> are <c>null</c>.</exception> /// <exception cref="ArgumentException">If <b>newEmail</b> is empty.</exception> public UserInfo ModifyUser(UserInfo user, string newDisplayName, string newPassword, string newEmail, bool newActive) { if(user == null) throw new ArgumentNullException("user"); if(newEmail == null) throw new ArgumentNullException("newEmail"); if(newEmail.Length == 0) throw new ArgumentException("New Email cannot be empty", "newEmail"); lock(this) { LocalUserInfo local = LoadLocalInstance(user); if(local == null) return null; UserInfo[] allUsers = GetUsers(); UsernameComparer comp = new UsernameComparer(); usersCache = null; for(int i = 0; i < allUsers.Length; i++) { if(comp.Compare(allUsers[i], user) == 0) { LocalUserInfo result = new LocalUserInfo(user.Username, newDisplayName, newEmail, newActive, user.DateTime, this, string.IsNullOrEmpty(newPassword) ? local.PasswordHash : Hash.Compute(newPassword)); result.Groups = allUsers[i].Groups; allUsers[i] = result; DumpUsers(allUsers); return result; } } } return null; }
/// <summary> /// Gets the complete list of Users. /// </summary> /// <returns>All the Users, sorted by username.</returns> public UserInfo[] GetUsers() { lock(this) { if(usersCache == null) { UserGroup[] groups = GetUserGroups(); string[] lines = File.ReadAllLines(GetFullPath(UsersFile)); UserInfo[] result = new UserInfo[lines.Length]; char[] splitter = new char[] { '|' }; string[] fields; for(int i = 0; i < lines.Length; i++) { fields = lines[i].Split(splitter, StringSplitOptions.RemoveEmptyEntries); // Structure (version 3.0 - file previously converted): // Username|PasswordHash|Email|Active-Inactive|DateTime[|DisplayName] string displayName = fields.Length == 6 ? fields[5] : null; result[i] = new LocalUserInfo(fields[0], displayName, fields[2], fields[3].ToLowerInvariant().Equals("active"), DateTime.Parse(fields[4]), this, fields[1]); result[i].Groups = GetGroupsForUser(result[i].Username, groups); } Array.Sort(result, new UsernameComparer()); usersCache = result; } return usersCache; } }