Commit f5fe4e2b authored by Nico Eckes's avatar Nico Eckes

PermIO rework, linear inheritance tree, players have only one group at a time,...

PermIO rework, linear inheritance tree, players have only one group at a time, no priorities, new permission structure, new api, players suffixes.
parent 88cc01e5
Pipeline #753 passed with stage
in 3 minutes and 1 second
package com.unitedworldminers.permio.api;
import com.unitedworldminers.permio.api.exceptions.InheritanceLoopException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
public interface IGroup extends IPermissionHolder {
@NotNull String getGroupId();
void setGroupId(@Nullable String newName);
void addInheritance(@Nullable String groupId) throws InheritanceLoopException;
void removeInheritance(@Nullable IGroup group);
@NotNull Set<? extends IGroup> getOwnInheritances();
@NotNull Set<IGroup> getAllInheritances();
@NotNull Set<IPlayer> getPlayers();
}
package com.unitedworldminers.permio.api;
import com.unitedworldminers.permio.api.exceptions.InheritanceLoopException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
public interface IPermissionHolder {
// LOCAL
void addPermission(@Nullable String permission);
void removePermission(@Nullable String permission);
@NotNull Set<String> getOwnPermissions();
@NotNull Set<String> getAllPermissions();
@NotNull default Set<String> getChilds(@Nullable String permission) {
String perm = permission + '.';
return getAllPermissions().stream().filter(p->p.startsWith(perm)).collect(Collectors.toSet());
}
boolean hasPermission(@Nullable String permission);
@Nullable String getPrimaryChild(@Nullable String permission);
void setPrefix(@Nullable String prefix);
@Nullable String getPrefix();
@NotNull Set<String> getPermissions();
default void clearPermissions() {
for (String perm: getOwnPermissions()) {
for (String perm: getPermissions()) {
removePermission(perm);
}
}
void setInheritance(@Nullable IPermissionHolder holder) throws InheritanceLoopException;
@Nullable IPermissionHolder getInheritance();
default void removeInheritance(@Nullable IPermissionHolder holder) {
if (Objects.equals(getInheritance(), holder)) {
try {
setInheritance(null);
} catch (InheritanceLoopException ignored) {
// should not happen on null. Anyways, we don't care
}
}
}
default void setPrefix(@Nullable String prefix) {
updatePlayerLabels();
};
@Nullable String getPrefix();
default void setSuffix(@Nullable String suffix) {
updatePlayerLabels();
};
@Nullable String getSuffix();
// GLOBAL
@NotNull default Set<String> getAllPermissions() {
return getAllHolders().stream().flatMap(h-> h.getPermissions().stream()).collect(Collectors.toSet());
};
default boolean hasPermission(@Nullable String permission) {
return getAllPermissions().contains(permission);
};
@NotNull default Set<String> getChilds(@Nullable String permission) {
String perm = permission + '.';
return Collections.unmodifiableSet(getAllPermissions().stream().filter(p->p.startsWith(perm)).collect(Collectors.toSet()));
}
@Nullable default String getPrimaryChild(String permission) {
return getAllHolders().stream().flatMap(h-> h.getPermissions().stream()).filter(p->p.startsWith(permission)).findFirst().orElse(null);
}
@NotNull default List<IPermissionHolder> getAllHolders() {
IPermissionHolder inh = getInheritance();
if (inh == null) return Collections.singletonList(this);
List<IPermissionHolder> result = new ArrayList<>();
result.add(this);
result.addAll(inh.getAllHolders());
return result;
}
@NotNull Set<IPermissionHolder> getSubHolders();
default void updatePlayerLabels() {
for (IPermissionHolder sub: getSubHolders()) sub.updatePlayerLabels();
}
@Nullable default String getVisiblePrefix() {
String prefix = getPrefix();
if (prefix != null) return prefix;
IPermissionHolder parent = getInheritance();
return parent == null ? null : parent.getVisiblePrefix();
}
@Nullable default String getVisibleSuffix() {
String suffix = getSuffix();
if (suffix != null) return suffix;
IPermissionHolder parent = getInheritance();
return parent == null ? null : parent.getVisibleSuffix();
}
//INTERNAL
void registerSubHolder(IPermissionHolder holder);
void unregisterSubHolder(IPermissionHolder holder);
void load(Map<String, Object> map);
Map<String, Object> save();
}
\ No newline at end of file
......@@ -8,16 +8,4 @@ import java.util.UUID;
public interface IPlayer extends IPermissionHolder {
@NotNull UUID getId();
@NotNull Set<? extends IGroup> getOwnGroups();
@NotNull Set<IGroup> getAllGroups();
boolean hasGroup(@Nullable IGroup group);
@Nullable IGroup getPrimaryGroup();
void addGroup(@Nullable String groupId);
void removeGroup(@Nullable IGroup group);
default void setGroup(@Nullable IGroup group) {
for (IGroup g: getOwnGroups()) {
removeGroup(g);
}
if (group != null) addGroup(group.getGroupId());
}
}
......@@ -8,7 +8,6 @@ import com.unitedworldminers.permio.api.IPermissionHolder;
import com.unitedworldminers.permio.api.IPlayer;
import com.unitedworldminers.permio.commands.CmdPermission;
import com.unitedworldminers.permio.permissions.Permissions;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import org.jetbrains.annotations.NotNull;
......@@ -42,12 +41,12 @@ public class CmdInfo extends SimpleCommand {
IGroup group = holder instanceof IGroup ? (IGroup) holder : null;
MessageUtils.messageToSender(sender, "commands.info.header", holder.toString());
MessageUtils.messageToSender(sender, "commands.info.permissions", String.join(", ", holder.getOwnPermissions()));
MessageUtils.messageToSender(sender, "commands.info.permissions", String.join(", ", holder.getPermissions()));
MessageUtils.messageToSender(sender, "commands.info.prefix", Utils.orDefault(holder.getPrefix(), "none"));
if (player != null) MessageUtils.messageToSender(sender, "commands.info.groups", player.getOwnGroups().stream().map(Object::toString).collect(Collectors.joining(", ")));
if (player != null) MessageUtils.messageToSender(sender, "commands.info.groups", String.valueOf(player.getInheritance()));
if (group != null) {
MessageUtils.messageToSender(sender, "commands.info.inherits", group.getOwnInheritances().stream().map(Object::toString).collect(Collectors.joining(", ")));
MessageUtils.messageToSender(sender, "commands.info.players", group.getPlayers().stream().map(Object::toString).collect(Collectors.joining(", ")));
MessageUtils.messageToSender(sender, "commands.info.inherits", String.valueOf(group.getInheritance()));
MessageUtils.messageToSender(sender, "commands.info.players", group.getSubHolders().stream().filter(sub -> sub instanceof IPlayer).map(Object::toString).collect(Collectors.joining(", ")));
}
}
......
......@@ -3,10 +3,9 @@ package com.unitedworldminers.permio.commands.group;
import com.unitedworldminers.BaseIO.util.MessageUtils;
import com.unitedworldminers.BaseIO.util.SimpleCommand;
import com.unitedworldminers.permio.api.IGroup;
import com.unitedworldminers.permio.commands.CmdPermission;
import com.unitedworldminers.permio.api.exceptions.InheritanceLoopException;
import com.unitedworldminers.permio.commands.CmdPermission;
import com.unitedworldminers.permio.permissions.Permissions;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import org.jetbrains.annotations.NotNull;
......@@ -39,7 +38,7 @@ public class CmdAddinheritance extends SimpleCommand {
IGroup inherit = CmdPermission.getGroup(sender, args[1]);
if (inherit == null) return;
try {
group.addInheritance(inherit.getGroupId());
group.setInheritance(inherit);
} catch (InheritanceLoopException e) {
MessageUtils.messageToSender(sender, "commands.addinheritance.looperror", inherit.getGroupId(), group.getGroupId());
return;
......
......@@ -4,9 +4,9 @@ import com.unitedworldminers.BaseIO.util.MessageUtils;
import com.unitedworldminers.BaseIO.util.SimpleCommand;
import com.unitedworldminers.permio.api.IGroup;
import com.unitedworldminers.permio.api.IPlayer;
import com.unitedworldminers.permio.api.exceptions.InheritanceLoopException;
import com.unitedworldminers.permio.commands.CmdPermission;
import com.unitedworldminers.permio.permissions.Permissions;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import org.jetbrains.annotations.NotNull;
......@@ -38,7 +38,12 @@ public class CmdAddgroup extends SimpleCommand {
if (player == null) return;
IGroup group = CmdPermission.getGroup(sender, args[1]);
if (group == null) return;
player.addGroup(group.getGroupId());
try {
player.setInheritance(group);
} catch (InheritanceLoopException e) {
MessageUtils.messageToSender(sender, "commands.addgroup.inheritanceloop");
return;
}
MessageUtils.messageToSender(sender, "commands.addgroup.success", group.getGroupId(), args[0]);
}
......
......@@ -4,9 +4,9 @@ import com.unitedworldminers.BaseIO.util.MessageUtils;
import com.unitedworldminers.BaseIO.util.SimpleCommand;
import com.unitedworldminers.permio.api.IGroup;
import com.unitedworldminers.permio.api.IPlayer;
import com.unitedworldminers.permio.api.exceptions.InheritanceLoopException;
import com.unitedworldminers.permio.commands.CmdPermission;
import com.unitedworldminers.permio.permissions.Permissions;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.server.MinecraftServer;
import org.jetbrains.annotations.NotNull;
......@@ -38,7 +38,11 @@ public class CmdRemovegroup extends SimpleCommand {
if (player == null) return;
IGroup group = CmdPermission.getGroup(sender, args[1]);
if (group == null) return;
player.removeGroup(group);
try {
player.setInheritance(Permissions.INSTANCE.defaultGroup);
} catch (InheritanceLoopException ignored) {
// default group
}
MessageUtils.messageToSender(sender, "commands.removegroup.success", group.getGroupId(), args[0]);
}
......
package com.unitedworldminers.permio.permissions;
import com.unitedworldminers.permio.api.IGroup;
import com.unitedworldminers.permio.api.IPlayer;
import com.unitedworldminers.permio.Logger;
import com.unitedworldminers.permio.api.IGroup;
import com.unitedworldminers.permio.api.exceptions.InheritanceLoopException;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.Map;
public class Group extends PermissionHolder implements IGroup {
private static final String KEY_PREFIX = "prefix";
private static final String KEY_PRIORITY = "priority";
private static final String KEY_PERMISSIONS = "permissions";
private static final String KEY_INHERITS = "inherits";
private String groupid;
private String prefix = "";
private final Set<Group> inheritances = new HashSet<>();
public Group(String groupid) {
this.groupid = groupid;
}
@Override
public Set<PermissionNode> getAllPermNodes() {
Set<PermissionNode> result = permissions.values();
inheritances.forEach(g->result.addAll(g.getAllPermNodes()));
return result;
}
@Override
public Map<String, Object> save() {
Map<String, Object> result = new HashMap<>();
result.put(KEY_PREFIX, prefix);
result.put(KEY_PRIORITY, priority);
result.put(KEY_INHERITS, inheritances.stream().map(IGroup::getGroupId).collect(Collectors.toList()));
ArrayList<String> permissionStrings = new ArrayList<>();
permissions.values().forEach((PermissionNode n) -> permissionStrings.add(n.toString()));
result.put(KEY_PERMISSIONS, permissions.values().stream().map(Object::toString).collect(Collectors.toList()));
return result;
}
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
updatePlayerPrefixes();
}
private void updatePlayerPrefixes() {
for (IPlayer pl: getPlayers()) {
if (pl instanceof Player) {
((Player) pl).updateEntityPrefix();
}
}
}
@NotNull
@Override
public String getGroupId() {
......@@ -75,91 +28,40 @@ public class Group extends PermissionHolder implements IGroup {
if (newName != null) groupid = newName.replace(' ', '_');
}
public void addInheritance(Group group) throws InheritanceLoopException {
if (group.equals(this) || group.getAllInheritances().contains(this)) throw new InheritanceLoopException();
inheritances.add(group);
updatePlayerPrefixes();
}
@Override
public void addInheritance(String groupId) throws InheritanceLoopException {
Group group = Permissions.INSTANCE.getGroupInternal(groupId);
if (group != null) addInheritance(group);
}
@Override
public void removeInheritance(IGroup group) {
inheritances.remove(group);
updatePlayerPrefixes();
public String toString() {
return groupid;
}
@NotNull
@Contract(value = "null -> false", pure = true)
@Override
public Set<? extends IGroup> getOwnInheritances() {
return inheritances;
}
@NotNull
public Set<IGroup> getAllInheritances() {
return inheritances.stream().flatMap(g-> Stream.concat(Stream.of(g), g.getAllInheritances().stream())).collect(Collectors.toSet());
public boolean equals(Object obj) {
return obj instanceof Group && groupid.equals(((Group) obj).groupid);
}
@NotNull
@Override
public Set<IPlayer> getPlayers() {
return Permissions.INSTANCE.getPlayers().stream().filter(p -> p.hasGroup(this)).collect(Collectors.toSet());
}
public int getPriority() {
return priority;
}
@SuppressWarnings("unused")
public void setPriority(int priority) {
this.priority = priority;
for (PermissionNode n: permissions.values()) {
n.setBasePriority(priority);
}
updatePlayerPrefixes();
public Map<String, Object> save() {
Map<String, Object> result = super.save();
if (parent != null) result.put(KEY_INHERITS, parent.toString());
return result;
}
@Override
public String toString() {
return groupid;
}
@SuppressWarnings("unchecked")
public void load(Map<String, Object> map) {
Object o = map.get(KEY_PREFIX);
if (o instanceof String) setPrefix((String) o);
o = map.get(KEY_PRIORITY);
if (o instanceof Integer) priority = (int) o;
o = map.get(KEY_PERMISSIONS);
if (o instanceof List) {
((List<String>) o).forEach(this::addPermission);
}
o = map.get(KEY_INHERITS);
if (o instanceof List) {
((List<String>) o).forEach(s -> {
try {
Group g = Permissions.INSTANCE.getGroupInternal(s);
if (g == null) {
Logger.warn("Unknown group inheritance '"+s+"' for group '"+groupid+"'! Ignoring.");
} else {
addInheritance(g.groupid);
}
} catch (InheritanceLoopException e) {
Logger.warn("Group inheritance '"+s+"' for group '"+groupid+"' would cause an inheritance loop! Ignoring.");
super.load(map);
Object o = map.get(KEY_INHERITS);
if (o instanceof String) {
String s = (String) o;
try {
IGroup g = Permissions.INSTANCE.getGroup(s);
if (g == null) {
Logger.warn("Unknown inheritance '" + s + "' for '" + toString() + "'! Ignoring.");
} else {
setInheritance(g);
}
});
} catch (InheritanceLoopException e) {
Logger.warn("Group inheritance '" + s + "' for group '" + toString() + "' would cause an inheritance loop! Ignoring.");
}
}
}
@Override
public boolean equals(Object obj) {
return obj instanceof Group && groupid.equals(((Group) obj).groupid);
}
}
package com.unitedworldminers.permio.permissions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.unitedworldminers.permio.api.IPermissionHolder;
import net.minecraft.util.text.TextComponentString;
import com.unitedworldminers.permio.api.exceptions.InheritanceLoopException;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public abstract class PermissionHolder implements IPermissionHolder {
final BiMap<String, PermissionNode> permissions = HashBiMap.create();
int priority;
public class PermissionHolder implements IPermissionHolder {
private static final String KEY_PREFIX = "prefix";
private static final String KEY_SUFFIX = "suffix";
private static final String KEY_PERMISSIONS = "permissions";
public abstract Set<PermissionNode> getAllPermNodes();
String prefix = null;
String suffix = null;
IPermissionHolder parent = null;
final Map<String, Boolean> permissions = new HashMap<>();
final Set<IPermissionHolder> childs = new HashSet<>();
public PermissionNode getHighestPriorityPermissionMatching(PermissionNode node) {
PermissionNode result = null;
int max = 0;
for (PermissionNode n: getAllPermNodes()) {
if (n.matches(node) && n.getPriority() > max) {
max = n.getPriority();
result = n;
}
@Override
public void addPermission(@Nullable String permission) {
if (permission == null || permission.isEmpty()) return;
boolean inverted = false;
if (permission.charAt(0) == '!') {
inverted = true;
permission = permission.substring(1);
}
return result;
permissions.put(permission, inverted);
}
public abstract Map<String, Object> save();
@Override
public void removePermission(@Nullable String permission) {
if (permission == null || permission.isEmpty()) return;
if (permission.charAt(0) == '!') permission = permission.substring(1);
permissions.remove(permission);
}
@Override
public boolean hasPermission(@Nullable String permission) {
if (permission == null || permission.isEmpty()) return false;
boolean inverted = false;
if (permission.charAt(0) == '!') {
inverted = true;
permission = permission.substring(1);
}
Boolean b = permissions.get(permission);
if (b != null) return inverted == b;
public abstract void load(Map<String, Object> data);
String curr = permission;
while (!(curr = curr.substring(0, or0(curr.lastIndexOf('.')))).isEmpty()) {
b = permissions.get(curr + ".*");
if (b != null) return inverted == b;
}
return false;
}
@Contract(pure = true)
private int or0(int i) {
return i < 0 ? 0 : i;
}
@NotNull
@Override
public void addPermission(String permission) {
PermissionNode node = new PermissionNode(permission);
node.setBasePriority(priority);
permissions.put(node.getNodeString(), node);
public Set<String> getPermissions() {
return permissions.entrySet().stream().map(e-> e.getValue() ? '!' + e.getKey() : e.getKey()).collect(Collectors.toSet());
}
@Override
public void removePermission(String permission) {
permissions.remove(permission);
public void setInheritance(@Nullable IPermissionHolder holder) throws InheritanceLoopException {
if (holder != null) {
if (holder.getAllHolders().contains(this)) throw new InheritanceLoopException();
holder.registerSubHolder(this);
}
if (parent != null) parent.unregisterSubHolder(this);
parent = holder;
}
@NotNull
@Nullable
@Override
public Set<String> getOwnPermissions() {
return permissions.keySet();
public IPermissionHolder getInheritance() {
return parent;
}
@Override
public void setPrefix(@Nullable String prefix) {
IPermissionHolder.super.setPrefix(prefix);
this.prefix = prefix;
}
@Nullable
@Override
public String getPrefix() {
return prefix;
}
@Override
public void setSuffix(@Nullable String suffix) {
IPermissionHolder.super.setSuffix(suffix);
this.suffix = suffix;
}
@Nullable
@Override
public String getSuffix() {
return suffix;
}
@NotNull
@Override
public Set<String> getAllPermissions() {
return getAllPermNodes().stream().map(Object::toString).collect(Collectors.toSet());
public Set<IPermissionHolder> getSubHolders() {
return childs;
}
@Override
public boolean hasPermission(String permission) {
PermissionNode result = getHighestPriorityPermissionMatching(new PermissionNode(permission));
return result != null && !result.isInverted();
public void registerSubHolder(IPermissionHolder holder) {
childs.add(holder);
}
@Override
public String getPrimaryChild(String permission) {
PermissionNode node = new PermissionNode(permission);
PermissionNode result = null;
int max = 0;
for (PermissionNode n: getAllPermNodes()) {
if (n.matchesStart(node) && n.getPriority() > max) {
max = n.getPriority();
result = n;
}
}
return result == null ? null : result.toString();
public void unregisterSubHolder(IPermissionHolder holder) {
childs.remove(holder);
}
@Override
public Map<String, Object> save() {
Map<String, Object> result = new HashMap<>();
if (prefix != null) result.put(KEY_PREFIX, prefix);
if (suffix != null) result.put(KEY_SUFFIX, suffix);
if (!permissions.isEmpty()) result.put(KEY_PERMISSIONS, permissions.values().stream().map(Object::toString).collect(Collectors.toList()));
return result;
}
protected static class PermioPrefix extends TextComponentString {
private static final Pattern cc = Pattern.compile("&([0-9a-fk-or])", Pattern.CASE_INSENSITIVE);
@SuppressWarnings("unchecked")
public void load(Map<String, Object> map) {
Object o = map.get(KEY_PREFIX);
if (o instanceof String) setPrefix((String) o);
o = map.get(KEY_SUFFIX);
if (o instanceof String) setSuffix((String) o);
public PermioPrefix(String prefix) {
super(cc.matcher(prefix).replaceAll("§$1"));
o = map.get(KEY_PERMISSIONS);
if (o instanceof List) {
((List<String>) o).forEach(this::addPermission);
}
}
}
\ No newline at end of file
package com.unitedworldminers.permio.permissions;
public class PermissionNode {
private static final int PRIORITY_BASE_MULTIPLIER = 100;
private static final int PRIORITY_MULTIPLIER = 4;
private static final int PRIORITY_SPECIFIC = +2;