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