diff --git a/DnsServerCore/Auth/AuthManager.cs b/DnsServerCore/Auth/AuthManager.cs
new file mode 100644
index 00000000..9caa9120
--- /dev/null
+++ b/DnsServerCore/Auth/AuthManager.cs
@@ -0,0 +1,837 @@
+/*
+Technitium DNS Server
+Copyright (C) 2022 Shreyas Zare (shreyas@technitium.com)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+*/
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace DnsServerCore.Auth
+{
+ sealed class AuthManager : IDisposable
+ {
+ #region variables
+
+ readonly ConcurrentDictionary _groups = new ConcurrentDictionary(1, 4);
+ readonly ConcurrentDictionary _users = new ConcurrentDictionary(1, 4);
+
+ readonly ConcurrentDictionary _permissions = new ConcurrentDictionary(1, 11);
+
+ readonly ConcurrentDictionary _sessions = new ConcurrentDictionary(1, 10);
+
+ readonly ConcurrentDictionary _failedLoginAttempts = new ConcurrentDictionary(1, 10);
+ const int MAX_LOGIN_ATTEMPTS = 5;
+
+ readonly ConcurrentDictionary _blockedAddresses = new ConcurrentDictionary(1, 10);
+ const int BLOCK_ADDRESS_INTERVAL = 5 * 60 * 1000;
+
+ readonly string _configFolder;
+ readonly LogManager _log;
+
+ readonly object _lockObj = new object();
+ bool _pendingSave;
+ readonly Timer _saveTimer;
+ const int SAVE_TIMER_INITIAL_INTERVAL = 10000;
+
+ #endregion
+
+ #region constructor
+
+ public AuthManager(string configFolder, LogManager log)
+ {
+ _configFolder = configFolder;
+ _log = log;
+
+ _saveTimer = new Timer(SaveTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
+ }
+
+ #endregion
+
+ #region IDisposable
+
+ bool _disposed;
+
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ if (_saveTimer is not null)
+ _saveTimer.Dispose();
+
+ lock (_lockObj)
+ {
+ SaveConfigFileInternal();
+ }
+
+ _disposed = true;
+ }
+
+ #endregion
+
+ #region private
+
+ private void SaveTimerCallback(object state)
+ {
+ try
+ {
+ lock (_lockObj)
+ {
+ _pendingSave = false;
+ SaveConfigFileInternal();
+ }
+ }
+ catch (Exception ex)
+ {
+ _log.Write(ex);
+ }
+ }
+
+ private void CreateDefaultConfig()
+ {
+ Group adminGroup = CreateGroup(Group.ADMINISTRATORS, "Super administrators");
+ Group dnsAdminGroup = CreateGroup(Group.DNS_ADMINISTRATORS, "DNS service administrators");
+ Group dhcpAdminGroup = CreateGroup(Group.DHCP_ADMINISTRATORS, "DHCP service administrators");
+ Group everyoneGroup = CreateGroup(Group.EVERYONE, "All users");
+
+ SetPermission(PermissionSection.Dashboard, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Zones, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Cache, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Allowed, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Blocked, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Apps, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.DnsClient, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Settings, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.DhcpServer, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Administration, adminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Logs, adminGroup, PermissionFlag.ViewModifyDelete);
+
+ SetPermission(PermissionSection.Zones, dnsAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Cache, dnsAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Allowed, dnsAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Blocked, dnsAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Apps, dnsAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.DnsClient, dnsAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Settings, dnsAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Logs, dnsAdminGroup, PermissionFlag.View);
+
+ SetPermission(PermissionSection.Zones, dhcpAdminGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.DnsClient, dhcpAdminGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.DhcpServer, dhcpAdminGroup, PermissionFlag.ViewModifyDelete);
+ SetPermission(PermissionSection.Logs, dhcpAdminGroup, PermissionFlag.View);
+
+ SetPermission(PermissionSection.Dashboard, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.Zones, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.Cache, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.Allowed, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.Blocked, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.Apps, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.DnsClient, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.DhcpServer, everyoneGroup, PermissionFlag.View);
+ SetPermission(PermissionSection.Logs, everyoneGroup, PermissionFlag.View);
+
+ string adminPassword = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD");
+ string adminPasswordFile = Environment.GetEnvironmentVariable("DNS_SERVER_ADMIN_PASSWORD_FILE");
+
+ User adminUser;
+
+ if (!string.IsNullOrEmpty(adminPassword))
+ {
+ adminUser = CreateUser("Administrator", "admin", adminPassword);
+ }
+ else if (!string.IsNullOrEmpty(adminPasswordFile))
+ {
+ try
+ {
+ using (StreamReader sR = new StreamReader(adminPasswordFile, true))
+ {
+ string password = sR.ReadLine();
+ adminUser = CreateUser("Administrator", "admin", password);
+ }
+ }
+ catch (Exception ex)
+ {
+ _log.Write(ex);
+
+ adminUser = CreateUser("Administrator", "admin", "admin");
+ }
+ }
+ else
+ {
+ adminUser = CreateUser("Administrator", "admin", "admin");
+ }
+
+ adminUser.AddToGroup(adminGroup);
+ }
+
+ private void LoadConfigFileInternal(UserSession implantSession)
+ {
+ string configFile = Path.Combine(_configFolder, "auth.config");
+
+ try
+ {
+ bool passwordResetOption = false;
+
+ if (!File.Exists(configFile))
+ {
+ string passwordResetConfigFile = Path.Combine(_configFolder, "resetadmin.config");
+
+ if (File.Exists(passwordResetConfigFile))
+ {
+ passwordResetOption = true;
+ configFile = passwordResetConfigFile;
+ }
+ }
+
+ using (FileStream fS = new FileStream(configFile, FileMode.Open, FileAccess.Read))
+ {
+ ReadConfigFrom(new BinaryReader(fS));
+ }
+
+ if (implantSession is not null)
+ {
+ UserSession newSession;
+
+ using (MemoryStream mS = new MemoryStream())
+ {
+ implantSession.WriteTo(new BinaryWriter(mS));
+
+ mS.Position = 0;
+ newSession = new UserSession(new BinaryReader(mS), this);
+ }
+
+ _sessions.TryAdd(newSession.Token, newSession);
+ }
+
+ _log.Write("DNS Server auth config file was loaded: " + configFile);
+
+ if (passwordResetOption)
+ {
+ User adminUser = GetUser("admin");
+ if (adminUser is null)
+ {
+ adminUser = CreateUser("Administrator", "admin", "admin");
+ }
+ else
+ {
+ adminUser.ChangePassword("admin");
+ adminUser.Disabled = false;
+ }
+
+ adminUser.AddToGroup(GetGroup(Group.ADMINISTRATORS));
+
+ _log.Write("DNS Server reset password for user: admin");
+ SaveConfigFileInternal();
+
+ try
+ {
+ File.Delete(configFile);
+ }
+ catch
+ { }
+ }
+ }
+ catch (FileNotFoundException)
+ {
+ _log.Write("DNS Server auth config file was not found: " + configFile);
+ _log.Write("DNS Server is restoring default auth config file.");
+
+ CreateDefaultConfig();
+
+ SaveConfigFileInternal();
+ }
+ catch (Exception ex)
+ {
+ _log.Write("DNS Server encountered an error while loading auth config file: " + configFile + "\r\n" + ex.ToString());
+ _log.Write("Note: You may try deleting the auth config file to fix this issue. However, you will lose auth settings but, rest of the DNS settings and zone data wont be affected.");
+ throw;
+ }
+ }
+
+ private void SaveConfigFileInternal()
+ {
+ string configFile = Path.Combine(_configFolder, "auth.config");
+
+ using (MemoryStream mS = new MemoryStream())
+ {
+ //serialize config
+ WriteConfigTo(new BinaryWriter(mS));
+
+ //write config
+ mS.Position = 0;
+
+ using (FileStream fS = new FileStream(configFile, FileMode.Create, FileAccess.Write))
+ {
+ mS.CopyTo(fS);
+ }
+ }
+
+ _log.Write("DNS Server auth config file was saved: " + configFile);
+ }
+
+ private void ReadConfigFrom(BinaryReader bR)
+ {
+ if (Encoding.ASCII.GetString(bR.ReadBytes(2)) != "AS") //format
+ throw new InvalidDataException("DNS Server auth config file format is invalid.");
+
+ int version = bR.ReadByte();
+ switch (version)
+ {
+ case 1:
+ {
+ int count = bR.ReadByte();
+ for (int i = 0; i < count; i++)
+ {
+ Group group = new Group(bR);
+ _groups.TryAdd(group.Name.ToLower(), group);
+ }
+ }
+
+ {
+ int count = bR.ReadByte();
+ for (int i = 0; i < count; i++)
+ {
+ User user = new User(bR, this);
+ _users.TryAdd(user.Username, user);
+ }
+ }
+
+ {
+ int count = bR.ReadInt32();
+ for (int i = 0; i < count; i++)
+ {
+ Permission permission = new Permission(bR, this);
+ _permissions.TryAdd(permission.Section, permission);
+ }
+ }
+
+ {
+ int count = bR.ReadInt32();
+ for (int i = 0; i < count; i++)
+ {
+ UserSession session = new UserSession(bR, this);
+ if (!session.HasExpired())
+ _sessions.TryAdd(session.Token, session);
+ }
+ }
+ break;
+
+ default:
+ throw new InvalidDataException("DNS Server auth config version not supported.");
+ }
+ }
+
+ private void WriteConfigTo(BinaryWriter bW)
+ {
+ bW.Write(Encoding.ASCII.GetBytes("AS")); //format
+ bW.Write((byte)1); //version
+
+ bW.Write(Convert.ToByte(_groups.Count));
+
+ foreach (KeyValuePair group in _groups)
+ group.Value.WriteTo(bW);
+
+ bW.Write(Convert.ToByte(_users.Count));
+
+ foreach (KeyValuePair user in _users)
+ user.Value.WriteTo(bW);
+
+ bW.Write(_permissions.Count);
+
+ foreach (KeyValuePair permission in _permissions)
+ permission.Value.WriteTo(bW);
+
+ List activeSessions = new List(_sessions.Count);
+
+ foreach (KeyValuePair session in _sessions)
+ {
+ if (session.Value.HasExpired())
+ _sessions.TryRemove(session.Key, out _);
+ else
+ activeSessions.Add(session.Value);
+ }
+
+ bW.Write(activeSessions.Count);
+
+ foreach (UserSession session in activeSessions)
+ session.WriteTo(bW);
+ }
+
+ private void FailedLoginAttempt(IPAddress address)
+ {
+ _failedLoginAttempts.AddOrUpdate(address, 1, delegate (IPAddress key, int attempts)
+ {
+ return attempts + 1;
+ });
+ }
+
+ private bool LoginAttemptsExceedLimit(IPAddress address, int limit)
+ {
+ if (!_failedLoginAttempts.TryGetValue(address, out int attempts))
+ return false;
+
+ return attempts >= limit;
+ }
+
+ private void ResetFailedLoginAttempt(IPAddress address)
+ {
+ _failedLoginAttempts.TryRemove(address, out _);
+ }
+
+ private void BlockAddress(IPAddress address, int interval)
+ {
+ _blockedAddresses.TryAdd(address, DateTime.UtcNow.AddMilliseconds(interval));
+ }
+
+ private bool IsAddressBlocked(IPAddress address)
+ {
+ if (!_blockedAddresses.TryGetValue(address, out DateTime expiry))
+ return false;
+
+ if (expiry > DateTime.UtcNow)
+ {
+ return true;
+ }
+ else
+ {
+ UnblockAddress(address);
+ ResetFailedLoginAttempt(address);
+
+ return false;
+ }
+ }
+
+ private void UnblockAddress(IPAddress address)
+ {
+ _blockedAddresses.TryRemove(address, out _);
+ }
+
+ #endregion
+
+ #region public
+
+ public User GetUser(string username)
+ {
+ if (_users.TryGetValue(username.ToLower(), out User user))
+ return user;
+
+ return null;
+ }
+
+ public User CreateUser(string displayName, string username, string password, int iterations = User.DEFAULT_ITERATIONS)
+ {
+ username = username.ToLower();
+
+ User user = new User(displayName, username, password, iterations);
+
+ if (_users.TryAdd(username, user))
+ {
+ user.AddToGroup(GetGroup(Group.EVERYONE));
+ return user;
+ }
+
+ throw new DnsWebServiceException("User already exists: " + username);
+ }
+
+ public void ChangeUsername(User user, string newUsername)
+ {
+ if (user.Username.Equals(newUsername, StringComparison.OrdinalIgnoreCase))
+ return;
+
+ string oldUsername = user.Username;
+ user.Username = newUsername;
+
+ if (!_users.TryAdd(user.Username, user))
+ {
+ user.Username = oldUsername; //revert
+ throw new DnsWebServiceException("User already exists: " + newUsername);
+ }
+
+ _users.TryRemove(oldUsername, out _);
+ }
+
+ public bool DeleteUser(string username)
+ {
+ if (_users.TryRemove(username.ToLower(), out User deletedUser))
+ {
+ //delete all sessions
+ foreach (UserSession session in GetSessions(deletedUser))
+ DeleteSession(session.Token);
+
+ //delete all permissions
+ foreach (KeyValuePair permission in _permissions)
+ {
+ permission.Value.RemovePermission(deletedUser);
+ permission.Value.RemoveAllSubItemPermissions(deletedUser);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ public Group GetGroup(string name)
+ {
+ if (_groups.TryGetValue(name.ToLower(), out Group group))
+ return group;
+
+ return null;
+ }
+
+ public List GetGroupMembers(Group group)
+ {
+ List members = new List();
+
+ foreach (KeyValuePair user in _users)
+ {
+ if (user.Value.IsMemberOfGroup(group))
+ members.Add(user.Value);
+ }
+
+ return members;
+ }
+
+ public void SyncGroupMembers(Group group, IReadOnlyDictionary users)
+ {
+ //remove
+ foreach (KeyValuePair user in _users)
+ {
+ if (!users.ContainsKey(user.Key))
+ user.Value.RemoveFromGroup(group);
+ }
+
+ //set
+ foreach (KeyValuePair user in users)
+ user.Value.AddToGroup(group);
+ }
+
+ public Group CreateGroup(string name, string description)
+ {
+ Group group = new Group(name, description);
+
+ if (_groups.TryAdd(name.ToLower(), group))
+ return group;
+
+ throw new DnsWebServiceException("Group already exists: " + name);
+ }
+
+ public void RenameGroup(Group group, string newGroupName)
+ {
+ if (group.Name.Equals(newGroupName, StringComparison.OrdinalIgnoreCase))
+ {
+ group.Name = newGroupName;
+ return;
+ }
+
+ string oldGroupName = group.Name;
+ group.Name = newGroupName;
+
+ if (!_groups.TryAdd(group.Name.ToLower(), group))
+ {
+ group.Name = oldGroupName; //revert
+ throw new DnsWebServiceException("Group already exists: " + newGroupName);
+ }
+
+ _groups.TryRemove(oldGroupName.ToLower(), out _);
+
+ //update users
+ foreach (KeyValuePair user in _users)
+ user.Value.RenameGroup(oldGroupName);
+ }
+
+ public bool DeleteGroup(string name)
+ {
+ name = name.ToLower();
+
+ switch (name)
+ {
+ case "everyone":
+ case "administrators":
+ case "dns administrators":
+ case "dhcp administrators":
+ throw new InvalidOperationException("Access was denied.");
+
+ default:
+ if (_groups.TryRemove(name, out Group deletedGroup))
+ {
+ //remove all users from deleted group
+ foreach (KeyValuePair user in _users)
+ user.Value.RemoveFromGroup(deletedGroup);
+
+ //delete all permissions
+ foreach (KeyValuePair permission in _permissions)
+ {
+ permission.Value.RemovePermission(deletedGroup);
+ permission.Value.RemoveAllSubItemPermissions(deletedGroup);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ public UserSession GetSession(string token)
+ {
+ if (_sessions.TryGetValue(token, out UserSession session))
+ return session;
+
+ return null;
+ }
+
+ public List GetSessions(User user)
+ {
+ List userSessions = new List();
+
+ foreach (KeyValuePair session in _sessions)
+ {
+ if (session.Value.User.Equals(user) && !session.Value.HasExpired())
+ userSessions.Add(session.Value);
+ }
+
+ return userSessions;
+ }
+
+ public async Task CreateSessionAsync(UserSessionType type, string tokenName, string username, string password, IPAddress remoteAddress, string userAgent)
+ {
+ if (IsAddressBlocked(remoteAddress))
+ throw new DnsWebServiceException("Max limit of " + MAX_LOGIN_ATTEMPTS + " attempts exceeded. Access blocked for " + (BLOCK_ADDRESS_INTERVAL / 1000) + " seconds.");
+
+ User user = GetUser(username);
+
+ if ((user is null) || !user.PasswordHash.Equals(user.GetPasswordHashFor(password), StringComparison.Ordinal))
+ {
+ if (password != "admin")
+ {
+ FailedLoginAttempt(remoteAddress);
+
+ if (LoginAttemptsExceedLimit(remoteAddress, MAX_LOGIN_ATTEMPTS))
+ BlockAddress(remoteAddress, BLOCK_ADDRESS_INTERVAL);
+
+ await Task.Delay(1000);
+ }
+
+ throw new DnsWebServiceException("Invalid username or password for user: " + username);
+ }
+
+ ResetFailedLoginAttempt(remoteAddress);
+
+ if (user.Disabled)
+ throw new DnsWebServiceException("User account is disabled. Please contact your administrator.");
+
+ UserSession session = new UserSession(type, tokenName, user, remoteAddress, userAgent);
+
+ if (!_sessions.TryAdd(session.Token, session))
+ throw new DnsWebServiceException("Error while creating session. Please try again.");
+
+ user.LoggedInFrom(remoteAddress);
+
+ return session;
+ }
+
+ public UserSession CreateApiToken(string tokenName, string username, IPAddress remoteAddress, string userAgent)
+ {
+ if (IsAddressBlocked(remoteAddress))
+ throw new DnsWebServiceException("Max limit of " + MAX_LOGIN_ATTEMPTS + " attempts exceeded. Access blocked for " + (BLOCK_ADDRESS_INTERVAL / 1000) + " seconds.");
+
+ User user = GetUser(username);
+ if (user is null)
+ throw new DnsWebServiceException("No such user exists: " + username);
+
+ if (user.Disabled)
+ throw new DnsWebServiceException("Account is suspended.");
+
+ UserSession session = new UserSession(UserSessionType.ApiToken, tokenName, user, remoteAddress, userAgent);
+
+ if (!_sessions.TryAdd(session.Token, session))
+ throw new DnsWebServiceException("Error while creating session. Please try again.");
+
+ user.LoggedInFrom(remoteAddress);
+
+ return session;
+ }
+
+ public UserSession DeleteSession(string token)
+ {
+ if (_sessions.TryRemove(token, out UserSession session))
+ return session;
+
+ return null;
+ }
+
+ public Permission GetPermission(PermissionSection section)
+ {
+ if (_permissions.TryGetValue(section, out Permission permission))
+ return permission;
+
+ return null;
+ }
+
+ public Permission GetPermission(PermissionSection section, string subItemName)
+ {
+ if (_permissions.TryGetValue(section, out Permission permission))
+ return permission.GetSubItemPermission(subItemName);
+
+ return null;
+ }
+
+ public void SetPermission(PermissionSection section, User user, PermissionFlag flags)
+ {
+ Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key)
+ {
+ return new Permission(key);
+ });
+
+ permission.SetPermission(user, flags);
+ }
+
+ public void SetPermission(PermissionSection section, string subItemName, User user, PermissionFlag flags)
+ {
+ Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key)
+ {
+ return new Permission(key);
+ });
+
+ permission.SetSubItemPermission(subItemName, user, flags);
+ }
+
+ public void SetPermission(PermissionSection section, Group group, PermissionFlag flags)
+ {
+ Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key)
+ {
+ return new Permission(key);
+ });
+
+ permission.SetPermission(group, flags);
+ }
+
+ public void SetPermission(PermissionSection section, string subItemName, Group group, PermissionFlag flags)
+ {
+ Permission permission = _permissions.GetOrAdd(section, delegate (PermissionSection key)
+ {
+ return new Permission(key);
+ });
+
+ permission.SetSubItemPermission(subItemName, group, flags);
+ }
+
+ public bool RemovePermission(PermissionSection section, User user)
+ {
+ return _permissions.TryGetValue(section, out Permission permission) && permission.RemovePermission(user);
+ }
+
+ public bool RemovePermission(PermissionSection section, string subItemName, User user)
+ {
+ return _permissions.TryGetValue(section, out Permission permission) && permission.RemoveSubItemPermission(subItemName, user);
+ }
+
+ public bool RemovePermission(PermissionSection section, Group group)
+ {
+ return _permissions.TryGetValue(section, out Permission permission) && permission.RemovePermission(group);
+ }
+
+ public bool RemovePermission(PermissionSection section, string subItemName, Group group)
+ {
+ return _permissions.TryGetValue(section, out Permission permission) && permission.RemoveSubItemPermission(subItemName, group);
+ }
+
+ public bool RemoveAllPermissions(PermissionSection section, string subItemName)
+ {
+ return _permissions.TryGetValue(section, out Permission permission) && permission.RemoveAllSubItemPermissions(subItemName);
+ }
+
+ public bool IsPermitted(PermissionSection section, User user, PermissionFlag flag)
+ {
+ return _permissions.TryGetValue(section, out Permission permission) && permission.IsPermitted(user, flag);
+ }
+
+ public bool IsPermitted(PermissionSection section, string subItemName, User user, PermissionFlag flag)
+ {
+ return _permissions.TryGetValue(section, out Permission permission) && permission.IsSubItemPermitted(subItemName, user, flag);
+ }
+
+ public void LoadOldConfig(string password, bool isPasswordHash)
+ {
+ User user = GetUser("admin");
+ if (user is null)
+ user = CreateUser("Administrator", "admin", "admin");
+
+ user.AddToGroup(GetGroup(Group.ADMINISTRATORS));
+
+ if (isPasswordHash)
+ user.LoadOldSchemeCredentials(password);
+ else
+ user.ChangePassword(password);
+
+ lock (_lockObj)
+ {
+ SaveConfigFileInternal();
+ }
+ }
+
+ public void LoadConfigFile(UserSession implantSession = null)
+ {
+ lock (_lockObj)
+ {
+ _groups.Clear();
+ _users.Clear();
+ _permissions.Clear();
+ _sessions.Clear();
+
+ LoadConfigFileInternal(implantSession);
+ }
+ }
+
+ public void SaveConfigFile()
+ {
+ lock (_lockObj)
+ {
+ if (_pendingSave)
+ return;
+
+ _pendingSave = true;
+ _saveTimer.Change(SAVE_TIMER_INITIAL_INTERVAL, Timeout.Infinite);
+ }
+ }
+
+ #endregion
+
+ #region properties
+
+ public ICollection Groups
+ { get { return _groups.Values; } }
+
+ public ICollection Users
+ { get { return _users.Values; } }
+
+ public ICollection Permissions
+ { get { return _permissions.Values; } }
+
+ public ICollection Sessions
+ { get { return _sessions.Values; } }
+
+ #endregion
+ }
+}