/* 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) { byte version = bR.ReadByte(); switch (version) { case 1: case 2: _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; if (version >= 2) count = bR.ReadInt32(); else 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)2); 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(_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 } }