diff --git a/TShockAPI/DB/GroupManager.cs b/TShockAPI/DB/GroupManager.cs index 1486a5f7..0afb4da6 100644 --- a/TShockAPI/DB/GroupManager.cs +++ b/TShockAPI/DB/GroupManager.cs @@ -19,6 +19,7 @@ namespace TShockAPI.DB var table = new SqlTable("GroupList", new SqlColumn("GroupName", MySqlDbType.VarChar, 32) { Primary = true }, + new SqlColumn("Parent", MySqlDbType.VarChar, 32), new SqlColumn("Commands", MySqlDbType.Text), new SqlColumn("ChatColor", MySqlDbType.Text) ); @@ -26,11 +27,11 @@ namespace TShockAPI.DB creator.EnsureExists(table); //Add default groups - AddGroup("trustedadmin", "admin,maintenance,cfg,butcher,item,heal,immunetoban,ignorecheatdetection,ignoregriefdetection,usebanneditem,manageusers"); - AddGroup("admin", "newadmin,ban,unban,whitelist,causeevents,spawnboss,spawnmob,managewarp,time,tp,pvpfun,kill,logs,immunetokick,tphere"); - AddGroup("newadmin", "default,kick,editspawn,reservedslot"); AddGroup("default", "canwater,canlava,warp,canbuild"); - AddGroup("vip", "default,canwater,canlava,warp,canbuild,reservedslot"); + AddGroup("newadmin", "default", "kick,editspawn,reservedslot"); + AddGroup("admin", "newadmin", "ban,unban,whitelist,causeevents,spawnboss,spawnmob,managewarp,time,tp,pvpfun,kill,logs,immunetokick,tphere"); + AddGroup("trustedadmin", "admin", "maintenance,cfg,butcher,item,heal,immunetoban,ignorecheatdetection,ignoregriefdetection,usebanneditem,manageusers"); + AddGroup("vip", "default", "canwater,canlava,warp,canbuild,reservedslot"); String file = Path.Combine(TShock.SavePath, "groups.txt"); if (File.Exists(file)) @@ -81,37 +82,53 @@ namespace TShockAPI.DB return true; - foreach (Group g in groups) - { - if (g.Name.Equals(group)) - return true; - } - - return false; + return groups.Any(g => g.Name.Equals(group)); } /// /// Adds group with name and permissions if it does not exist. /// /// name of group + /// parent of group /// permissions - public String AddGroup(String name, String permissions, String ChatColor = "255,255,255") + public String AddGroup(String name, string parentname, String permissions, String chatcolor) { String message = ""; if (GroupExists(name)) return "Error: Group already exists. Use /modGroup to change permissions."; + var group = new Group(name, null, chatcolor); + group.permissions.Add(permissions); + if (!string.IsNullOrWhiteSpace(parentname)) + { + var parent = groups.FirstOrDefault(gp => gp.Name == parentname); + if (parent == null) + { + var error = "Invalid parent {0} for group {1}".SFormat(group.Name, parentname); + Log.ConsoleError(error); + return error; + } + group.Parent = parent; + } + string query = (TShock.Config.StorageType.ToLower() == "sqlite") ? - "INSERT OR IGNORE INTO GroupList (GroupName, Commands, ChatColor) VALUES (@0, @1, @2);" : - "INSERT IGNORE INTO GroupList SET GroupName=@0, Commands=@1"; - if (database.Query(query, name, permissions, ChatColor) == 1) + "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);" : + "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@1, ChatColor=@1"; + if (database.Query(query, name, parentname, permissions, chatcolor) == 1) message = "Group " + name + " has been created successfully."; - Group g = new Group(name, null, ChatColor); - g.permissions.Add(permissions); - groups.Add(g); + + groups.Add(group); return message; } + public String AddGroup(String name, String permissions) + { + return AddGroup(name, "", permissions, "255,255,255"); + } + public String AddGroup(String name, string parent, String permissions) + { + return AddGroup(name, parent, permissions, "255,255,255"); + } public String DeleteGroup(String name) { @@ -161,24 +178,36 @@ namespace TShockAPI.DB public void LoadPermisions() { - groups = new List(); - groups.Add(new SuperAdminGroup()); + //Create a temporary list so if there is an error it doesn't override the currently loaded groups with broken groups. + var tempgroups = new List(); + tempgroups.Add(new SuperAdminGroup()); + + if (groups == null || groups.Count < 2) + groups = tempgroups; try { + var groupsparents = new List>(); using (var reader = database.QueryReader("SELECT * FROM GroupList")) { while (reader.Read()) { - Group group = null; string groupname = reader.Get("GroupName"); - group = new Group(groupname); + var group = new Group(groupname); //Inherit Given commands String[] commands = reader.Get("Commands").Split(','); - for (int i = 0; i < commands.Length; i++) + foreach (var t in commands) { - group.AddPermission(commands[i].Trim()); + var str = t.Trim(); + if (str.StartsWith("!")) + { + group.NegatePermission(str.Substring(1)); + } + else + { + group.AddPermission(str); + } } String[] chatcolour = (reader.Get("ChatColor") ?? "").Split(','); if (chatcolour.Length == 3) @@ -187,9 +216,30 @@ namespace TShockAPI.DB byte.TryParse(chatcolour[1], out group.G); byte.TryParse(chatcolour[2], out group.B); } - groups.Add(group); + + groupsparents.Add(Tuple.Create(group, reader.Get("Parent"))); } } + + foreach (var t in groupsparents) + { + var group = t.Item1; + var parentname = t.Item2; + if (!string.IsNullOrWhiteSpace(parentname)) + { + var parent = groupsparents.FirstOrDefault(gp => gp.Item1.Name == parentname); + if (parent == null) + { + Log.ConsoleError("Invalid parent {0} for group {1}".SFormat(group.Name, parentname)); + return; + } + group.Parent = parent.Item1; + } + tempgroups.Add(group); + } + + + groups = tempgroups; } catch (Exception ex) { diff --git a/TShockAPI/Group.cs b/TShockAPI/Group.cs index 5e85eeaf..a7fbddb3 100644 --- a/TShockAPI/Group.cs +++ b/TShockAPI/Group.cs @@ -23,44 +23,43 @@ namespace TShockAPI public class Group { public readonly List permissions = new List(); - private readonly List negatedpermissions = new List(); + public readonly List negatedpermissions = new List(); - public string Name { get; protected set; } - public Group Parent { get; protected set; } + public string Name { get; set; } + public Group Parent { get; set; } public int Order { get; set; } public byte R = 255; public byte G = 255; public byte B = 255; - public Group(string groupname, Group parentgroup = null, string ChatColor = "255,255,255") + public Group(string groupname, Group parentgroup = null, string chatcolor = "255,255,255") { Name = groupname; Parent = parentgroup; - byte.TryParse(ChatColor.Split(',')[0], out R); - byte.TryParse(ChatColor.Split(',')[1], out G); - byte.TryParse(ChatColor.Split(',')[2], out B); + byte.TryParse(chatcolor.Split(',')[0], out R); + byte.TryParse(chatcolor.Split(',')[1], out G); + byte.TryParse(chatcolor.Split(',')[2], out B); } public virtual bool HasPermission(string permission) { - if (string.IsNullOrEmpty(permission)) - return true; - if (negatedpermissions.Contains(permission)) - return false; - if (permissions.Contains(permission)) - return true; - if (Parent != null) - return Parent.HasPermission(permission); - bool ret = false; - foreach( string g in permissions ) + var cur = this; + var traversed = new List(); + while (cur != null) { - if (TShock.Groups.GroupExists(g)) + if (string.IsNullOrEmpty(permission)) + return true; + if (cur.negatedpermissions.Contains(permission)) + return false; + if (cur.permissions.Contains(permission)) + return true; + if (traversed.Contains(cur)) { - ret = checkGroupForPermission(g, permission); - if (ret) - return true; + throw new Exception("Infinite group parenting ({0})".SFormat(cur.Name)); } + traversed.Add(cur); + cur = cur.Parent; } return false; } @@ -74,32 +73,6 @@ namespace TShockAPI { permissions.Add(permission); } - - private bool checkGroupForPermission(String g, String permission ) - { - bool ret = false; - if( !TShock.Groups.GroupExists( g ) ) - { - if( g.Equals(permission) ) - return true; - } - else - { - Group group = Tools.GetGroup(g); - foreach (String perm in group.permissions) - { - if( perm.Equals( permission ) ) - { - return true; - } - ret = checkGroupForPermission( perm, permission ); - - if( ret ) - return ret; - } - } - return ret; - } } public class SuperAdminGroup : Group