diff --git a/DnsServerCore/Auth/Permission.cs b/DnsServerCore/Auth/Permission.cs
new file mode 100644
index 00000000..05ac906b
--- /dev/null
+++ b/DnsServerCore/Auth/Permission.cs
@@ -0,0 +1,339 @@
+/*
+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 TechnitiumLibrary.IO;
+
+namespace DnsServerCore.Auth
+{
+ enum PermissionSection : byte
+ {
+ Unknown = 0,
+ Dashboard = 1,
+ Zones = 2,
+ Cache = 3,
+ Allowed = 4,
+ Blocked = 5,
+ Apps = 6,
+ DnsClient = 7,
+ Settings = 8,
+ DhcpServer = 9,
+ Administration = 10,
+ Logs = 11
+ }
+
+ [Flags]
+ enum PermissionFlag : byte
+ {
+ None = 0,
+ View = 1,
+ Modify = 2,
+ Delete = 4,
+ ViewModify = 3,
+ ViewModifyDelete = 7
+ }
+
+ class Permission : IComparable
+ {
+ #region variables
+
+ readonly PermissionSection _section;
+ readonly string _subItemName;
+
+ readonly ConcurrentDictionary _userPermissions;
+ readonly ConcurrentDictionary _groupPermissions;
+
+ readonly ConcurrentDictionary _subItemPermissions;
+
+ #endregion
+
+ #region constructor
+
+ public Permission(PermissionSection section, string subItemName = null)
+ {
+ _section = section;
+ _subItemName = subItemName;
+
+ _userPermissions = new ConcurrentDictionary(1, 1);
+ _groupPermissions = new ConcurrentDictionary(1, 1);
+
+ _subItemPermissions = new ConcurrentDictionary(1, 1);
+ }
+
+ public Permission(BinaryReader bR, AuthManager authManager)
+ {
+ switch (bR.ReadByte())
+ {
+ case 1:
+ _section = (PermissionSection)bR.ReadByte();
+
+ {
+ int count = bR.ReadByte();
+ _userPermissions = new ConcurrentDictionary(1, count);
+
+ for (int i = 0; i < count; i++)
+ {
+ User user = authManager.GetUser(bR.ReadShortString());
+ PermissionFlag flag = (PermissionFlag)bR.ReadByte();
+
+ if (user is not null)
+ _userPermissions.TryAdd(user, flag);
+ }
+ }
+
+ {
+ int count = bR.ReadByte();
+ _groupPermissions = new ConcurrentDictionary(1, count);
+
+ for (int i = 0; i < count; i++)
+ {
+ Group group = authManager.GetGroup(bR.ReadShortString());
+ PermissionFlag flag = (PermissionFlag)bR.ReadByte();
+
+ if (group is not null)
+ _groupPermissions.TryAdd(group, flag);
+ }
+ }
+
+ {
+ int count = bR.ReadByte();
+ _subItemPermissions = new ConcurrentDictionary(1, count);
+
+ for (int i = 0; i < count; i++)
+ {
+ string subItemName = bR.ReadShortString();
+ Permission subItemPermission = new Permission(bR, authManager);
+
+ _subItemPermissions.TryAdd(subItemName.ToLower(), subItemPermission);
+ }
+ }
+
+ break;
+
+ default:
+ throw new InvalidDataException("Invalid data or version not supported.");
+ }
+ }
+
+ #endregion
+
+ #region public
+
+ public void SetPermission(User user, PermissionFlag flags)
+ {
+ _userPermissions[user] = flags;
+ }
+
+ public void SyncPermissions(IReadOnlyDictionary userPermissions)
+ {
+ //remove non-existent permissions
+ foreach (KeyValuePair userPermission in _userPermissions)
+ {
+ if (!userPermissions.ContainsKey(userPermission.Key))
+ _userPermissions.TryRemove(userPermission.Key, out _);
+ }
+
+ //set new permissions
+ foreach (KeyValuePair userPermission in userPermissions)
+ _userPermissions[userPermission.Key] = userPermission.Value;
+ }
+
+ public void SetSubItemPermission(string subItemName, User user, PermissionFlag flags)
+ {
+ Permission subItemPermission = _subItemPermissions.GetOrAdd(subItemName.ToLower(), delegate (string key)
+ {
+ return new Permission(_section, key);
+ });
+
+ subItemPermission.SetPermission(user, flags);
+ }
+
+ public void SetPermission(Group group, PermissionFlag flags)
+ {
+ _groupPermissions[group] = flags;
+ }
+
+ public void SyncPermissions(IReadOnlyDictionary groupPermissions)
+ {
+ //remove non-existent permissions
+ foreach (KeyValuePair groupPermission in _groupPermissions)
+ {
+ if (!groupPermissions.ContainsKey(groupPermission.Key))
+ _groupPermissions.TryRemove(groupPermission.Key, out _);
+ }
+
+ //set new permissions
+ foreach (KeyValuePair groupPermission in groupPermissions)
+ _groupPermissions[groupPermission.Key] = groupPermission.Value;
+ }
+
+ public void SetSubItemPermission(string subItemName, Group group, PermissionFlag flags)
+ {
+ Permission subItemPermission = _subItemPermissions.GetOrAdd(subItemName.ToLower(), delegate (string key)
+ {
+ return new Permission(_section, key);
+ });
+
+ subItemPermission.SetPermission(group, flags);
+ }
+
+ public bool RemovePermission(User user)
+ {
+ return _userPermissions.TryRemove(user, out _);
+ }
+
+ public bool RemoveSubItemPermission(string subItemName, User user)
+ {
+ return _subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission) && subItemPermission.RemovePermission(user);
+ }
+
+ public bool RemovePermission(Group group)
+ {
+ return _groupPermissions.TryRemove(group, out _);
+ }
+
+ public bool RemoveSubItemPermission(string subItemName, Group group)
+ {
+ return _subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission) && subItemPermission.RemovePermission(group);
+ }
+
+ public bool RemoveAllSubItemPermissions(User user)
+ {
+ bool removed = false;
+
+ foreach (KeyValuePair subItemPermission in _subItemPermissions)
+ {
+ if (subItemPermission.Value.RemovePermission(user))
+ removed = true;
+ }
+
+ return removed;
+ }
+
+ public bool RemoveAllSubItemPermissions(Group group)
+ {
+ bool removed = false;
+
+ foreach (KeyValuePair subItemPermission in _subItemPermissions)
+ {
+ if (subItemPermission.Value.RemovePermission(group))
+ removed = true;
+ }
+
+ return removed;
+ }
+
+ public bool RemoveAllSubItemPermissions(string subItemName)
+ {
+ return _subItemPermissions.TryRemove(subItemName, out _);
+ }
+
+ public Permission GetSubItemPermission(string subItemName)
+ {
+ if (_subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission))
+ return subItemPermission;
+
+ return null;
+ }
+
+ public bool IsPermitted(User user, PermissionFlag flag)
+ {
+ if (_userPermissions.TryGetValue(user, out PermissionFlag userPermissions) && userPermissions.HasFlag(flag))
+ return true;
+
+ foreach (Group group in user.MemberOfGroups)
+ {
+ if (_groupPermissions.TryGetValue(group, out PermissionFlag groupPermissions) && groupPermissions.HasFlag(flag))
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool IsSubItemPermitted(string subItemName, User user, PermissionFlag flag)
+ {
+ return _subItemPermissions.TryGetValue(subItemName.ToLower(), out Permission subItemPermission) && subItemPermission.IsPermitted(user, flag);
+ }
+
+ public void WriteTo(BinaryWriter bW)
+ {
+ bW.Write((byte)1);
+ bW.Write((byte)_section);
+
+ {
+ bW.Write(Convert.ToByte(_userPermissions.Count));
+
+ foreach (KeyValuePair userPermission in _userPermissions)
+ {
+ bW.WriteShortString(userPermission.Key.Username);
+ bW.Write((byte)userPermission.Value);
+ }
+ }
+
+ {
+ bW.Write(Convert.ToByte(_groupPermissions.Count));
+
+ foreach (KeyValuePair groupPermission in _groupPermissions)
+ {
+ bW.WriteShortString(groupPermission.Key.Name);
+ bW.Write((byte)groupPermission.Value);
+ }
+ }
+
+ {
+ bW.Write(Convert.ToByte(_subItemPermissions.Count));
+
+ foreach (KeyValuePair subItemPermission in _subItemPermissions)
+ {
+ bW.WriteShortString(subItemPermission.Key);
+ subItemPermission.Value.WriteTo(bW);
+ }
+ }
+ }
+
+ public int CompareTo(Permission other)
+ {
+ return _section.CompareTo(other._section);
+ }
+
+ #endregion
+
+ #region properties
+
+ public PermissionSection Section
+ { get { return _section; } }
+
+ public string SubItemName
+ { get { return _subItemName; } }
+
+ public IReadOnlyDictionary UserPermissions
+ { get { return _userPermissions; } }
+
+ public IReadOnlyDictionary GroupPermissions
+ { get { return _groupPermissions; } }
+
+ public IReadOnlyDictionary SubItemPermissions
+ { get { return _subItemPermissions; } }
+
+ #endregion
+ }
+}