Add temporary permissions
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
package me.lucko.luckperms.utils;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* All credit to Essentials / EssentialsX for this class
|
||||
* https://github.com/drtshock/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java
|
||||
* https://github.com/essentials/Essentials/blob/2.x/Essentials/src/com/earth2me/essentials/utils/DateUtil.java
|
||||
*/
|
||||
public class DateUtil {
|
||||
private static final Pattern TIME_PATTERN = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE);
|
||||
private static final int MAX_YEARS = 100000;
|
||||
|
||||
private DateUtil() {}
|
||||
|
||||
public static boolean shouldExpire(long unixTime) {
|
||||
return unixTime < (System.currentTimeMillis() / 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a time string to a unix timestamp
|
||||
* @param time the time string
|
||||
* @return a unix timestamp
|
||||
* @throws IllegalDateException if the date input was invalid
|
||||
*/
|
||||
public static long parseDateDiff(String time, boolean future) throws IllegalDateException {
|
||||
Matcher m = TIME_PATTERN.matcher(time);
|
||||
int years = 0;
|
||||
int months = 0;
|
||||
int weeks = 0;
|
||||
int days = 0;
|
||||
int hours = 0;
|
||||
int minutes = 0;
|
||||
int seconds = 0;
|
||||
boolean found = false;
|
||||
while (m.find()) {
|
||||
if (m.group() == null || m.group().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < m.groupCount(); i++) {
|
||||
if (m.group(i) != null && !m.group(i).isEmpty()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (m.group(1) != null && !m.group(1).isEmpty()) {
|
||||
years = Integer.parseInt(m.group(1));
|
||||
}
|
||||
if (m.group(2) != null && !m.group(2).isEmpty()) {
|
||||
months = Integer.parseInt(m.group(2));
|
||||
}
|
||||
if (m.group(3) != null && !m.group(3).isEmpty()) {
|
||||
weeks = Integer.parseInt(m.group(3));
|
||||
}
|
||||
if (m.group(4) != null && !m.group(4).isEmpty()) {
|
||||
days = Integer.parseInt(m.group(4));
|
||||
}
|
||||
if (m.group(5) != null && !m.group(5).isEmpty()) {
|
||||
hours = Integer.parseInt(m.group(5));
|
||||
}
|
||||
if (m.group(6) != null && !m.group(6).isEmpty()) {
|
||||
minutes = Integer.parseInt(m.group(6));
|
||||
}
|
||||
if (m.group(7) != null && !m.group(7).isEmpty()) {
|
||||
seconds = Integer.parseInt(m.group(7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new IllegalDateException();
|
||||
}
|
||||
Calendar c = new GregorianCalendar();
|
||||
if (years > 0) {
|
||||
if (years > MAX_YEARS) {
|
||||
years = MAX_YEARS;
|
||||
}
|
||||
c.add(Calendar.YEAR, years * (future ? 1 : -1));
|
||||
}
|
||||
if (months > 0) {
|
||||
c.add(Calendar.MONTH, months * (future ? 1 : -1));
|
||||
}
|
||||
if (weeks > 0) {
|
||||
c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1));
|
||||
}
|
||||
if (days > 0) {
|
||||
c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1));
|
||||
}
|
||||
if (hours > 0) {
|
||||
c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1));
|
||||
}
|
||||
if (minutes > 0) {
|
||||
c.add(Calendar.MINUTE, minutes * (future ? 1 : -1));
|
||||
}
|
||||
if (seconds > 0) {
|
||||
c.add(Calendar.SECOND, seconds * (future ? 1 : -1));
|
||||
}
|
||||
Calendar max = new GregorianCalendar();
|
||||
max.add(Calendar.YEAR, 10);
|
||||
if (c.after(max)) {
|
||||
return (max.getTimeInMillis() / 1000) + 1;
|
||||
}
|
||||
return (c.getTimeInMillis() / 1000) + 1;
|
||||
}
|
||||
|
||||
private static int dateDiff(int type, Calendar fromDate, Calendar toDate, boolean future) {
|
||||
int year = Calendar.YEAR;
|
||||
|
||||
int fromYear = fromDate.get(year);
|
||||
int toYear = toDate.get(year);
|
||||
if (Math.abs(fromYear - toYear) > MAX_YEARS) {
|
||||
toDate.set(year, fromYear +
|
||||
(future ? MAX_YEARS : -MAX_YEARS));
|
||||
}
|
||||
|
||||
int diff = 0;
|
||||
long savedDate = fromDate.getTimeInMillis();
|
||||
while ((future && !fromDate.after(toDate)) || (!future && !fromDate.before(toDate))) {
|
||||
savedDate = fromDate.getTimeInMillis();
|
||||
fromDate.add(type, future ? 1 : -1);
|
||||
diff++;
|
||||
}
|
||||
diff--;
|
||||
fromDate.setTimeInMillis(savedDate);
|
||||
return diff;
|
||||
}
|
||||
|
||||
public static String formatDateDiff(long unixTime) {
|
||||
Calendar c = new GregorianCalendar();
|
||||
c.setTimeInMillis(unixTime * 1000);
|
||||
Calendar now = new GregorianCalendar();
|
||||
return DateUtil.formatDateDiff(now, c);
|
||||
}
|
||||
|
||||
private static String formatDateDiff(Calendar fromDate, Calendar toDate) {
|
||||
boolean future = false;
|
||||
if (toDate.equals(fromDate)) {
|
||||
return "now";
|
||||
}
|
||||
if (toDate.after(fromDate)) {
|
||||
future = true;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int[] types = new int[]{Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND};
|
||||
String[] names = new String[]{"year", "years", "month", "months", "day", "days", "hour", "hours", "minute", "minutes", "second", "seconds"};
|
||||
int accuracy = 0;
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
if (accuracy > 2) {
|
||||
break;
|
||||
}
|
||||
int diff = dateDiff(types[i], fromDate, toDate, future);
|
||||
if (diff > 0) {
|
||||
accuracy++;
|
||||
sb.append(" ").append(diff).append(" ").append(names[i * 2 + (diff > 1 ? 1 : 0)]);
|
||||
}
|
||||
}
|
||||
if (sb.length() == 0) {
|
||||
return "now";
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
public static class IllegalDateException extends Exception {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import java.util.regex.Pattern;
|
||||
public class Patterns {
|
||||
|
||||
public static final Pattern SERVER_SPLIT = Pattern.compile("\\/");
|
||||
public static final Pattern TEMP_SPLIT = Pattern.compile("\\$");
|
||||
public static final Pattern DOT_SPLIT = Pattern.compile("\\.");
|
||||
public static final Pattern GROUP_MATCH = Pattern.compile("group\\..*");
|
||||
public static final Pattern NON_ALPHA_NUMERIC = Pattern.compile("[^A-Za-z0-9]");
|
||||
|
||||
@@ -7,10 +7,8 @@ import me.lucko.luckperms.exceptions.ObjectAlreadyHasException;
|
||||
import me.lucko.luckperms.exceptions.ObjectLacksException;
|
||||
import me.lucko.luckperms.groups.Group;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Represents an object that can hold permissions
|
||||
@@ -39,7 +37,6 @@ public abstract class PermissionObject {
|
||||
/**
|
||||
* The user/group's permissions
|
||||
*/
|
||||
@Setter
|
||||
private Map<String, Boolean> nodes = new HashMap<>();
|
||||
|
||||
protected PermissionObject(LuckPermsPlugin plugin, String objectName) {
|
||||
@@ -48,15 +45,43 @@ public abstract class PermissionObject {
|
||||
this.includeGlobalPermissions = plugin.getConfiguration().getIncludeGlobalPerms();
|
||||
}
|
||||
|
||||
public void setNodes(Map<String, Boolean> nodes) {
|
||||
this.nodes = nodes;
|
||||
auditTemporaryPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for checking if a map has a certain permission. Used by both #hasPermission and #inheritsPermission
|
||||
*/
|
||||
private static boolean hasPermission(Map<String, Boolean> toQuery, String node, boolean b) {
|
||||
// Not temporary
|
||||
if (!node.contains("$")) {
|
||||
return b ? toQuery.containsKey(node) && toQuery.get(node) : toQuery.containsKey(node) && !toQuery.get(node);
|
||||
}
|
||||
|
||||
node = Patterns.TEMP_SPLIT.split(node)[0];
|
||||
|
||||
for (Map.Entry<String, Boolean> e : toQuery.entrySet()) {
|
||||
if (e.getKey().contains("$")) {
|
||||
String[] parts = Patterns.TEMP_SPLIT.split(e.getKey());
|
||||
if (parts[0].equalsIgnoreCase(node)) {
|
||||
return b ? e.getValue() : !e.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the object has a certain permission
|
||||
* @param node The permission node
|
||||
* @param b If the node is true/false(negated)
|
||||
* @return true if the user has the permission
|
||||
*/
|
||||
public boolean hasPermission(String node, Boolean b) {
|
||||
public boolean hasPermission(String node, boolean b) {
|
||||
if (node.startsWith("global/")) node = node.replace("global/", "");
|
||||
return b ? getNodes().containsKey(node) && getNodes().get(node) : getNodes().containsKey(node) && !getNodes().get(node);
|
||||
return hasPermission(getNodes(), node, b);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,17 +91,28 @@ public abstract class PermissionObject {
|
||||
* @param server The server
|
||||
* @return true if the user has the permission
|
||||
*/
|
||||
public boolean hasPermission(String node, Boolean b, String server) {
|
||||
public boolean hasPermission(String node, boolean b, String server) {
|
||||
return hasPermission(server + "/" + node, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see the the object has a permission on a certain server
|
||||
* @param node The permission node
|
||||
* @param b If the node is true/false(negated)
|
||||
* @param temporary if the permission is temporary
|
||||
* @return true if the user has the permission
|
||||
*/
|
||||
public boolean hasPermission(String node, boolean b, boolean temporary) {
|
||||
return hasPermission(node + (temporary ? "$a" : ""), b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the object inherits a certain permission
|
||||
* @param node The permission node
|
||||
* @param b If the node is true/false(negated)
|
||||
* @return true if the user inherits the permission
|
||||
*/
|
||||
public boolean inheritsPermission(String node, Boolean b) {
|
||||
public boolean inheritsPermission(String node, boolean b) {
|
||||
if (node.contains("/")) {
|
||||
// Use other method
|
||||
final String[] parts = Patterns.SERVER_SPLIT.split(node, 2);
|
||||
@@ -93,9 +129,20 @@ public abstract class PermissionObject {
|
||||
* @param server The server
|
||||
* @return true if the user inherits the permission
|
||||
*/
|
||||
public boolean inheritsPermission(String node, Boolean b, String server) {
|
||||
public boolean inheritsPermission(String node, boolean b, String server) {
|
||||
final Map<String, Boolean> local = getLocalPermissions(server, null);
|
||||
return b ? local.containsKey(node) && local.get(node) : local.containsKey(node) && !local.get(node);
|
||||
return hasPermission(local, node, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the object inherits a certain permission
|
||||
* @param node The permission node
|
||||
* @param b If the node is true/false(negated)
|
||||
* @param temporary if the permission is temporary
|
||||
* @return true if the user inherits the permission
|
||||
*/
|
||||
public boolean inheritsPermission(String node, boolean b, boolean temporary) {
|
||||
return inheritsPermission(node + (temporary ? "$a" : ""), b);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,7 +151,7 @@ public abstract class PermissionObject {
|
||||
* @param value What to set the node to - true/false(negated)
|
||||
* @throws ObjectAlreadyHasException if the object already has the permission
|
||||
*/
|
||||
public void setPermission(String node, Boolean value) throws ObjectAlreadyHasException {
|
||||
public void setPermission(String node, boolean value) throws ObjectAlreadyHasException {
|
||||
if (node.startsWith("global/")) node = node.replace("global/", "");
|
||||
if (hasPermission(node, value)) {
|
||||
throw new ObjectAlreadyHasException();
|
||||
@@ -119,21 +166,73 @@ public abstract class PermissionObject {
|
||||
* @param server The server to set the permission on
|
||||
* @throws ObjectAlreadyHasException if the object already has the permission
|
||||
*/
|
||||
public void setPermission(String node, Boolean value, String server) throws ObjectAlreadyHasException {
|
||||
public void setPermission(String node, boolean value, String server) throws ObjectAlreadyHasException {
|
||||
setPermission(server + "/" + node, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a permission for the object
|
||||
* @param node The node to set
|
||||
* @param value What to set the node to - true/false(negated)
|
||||
* @param expireAt The time in unixtime when the permission will expire
|
||||
* @throws ObjectAlreadyHasException if the object already has the permission
|
||||
*/
|
||||
public void setPermission(String node, boolean value, long expireAt) throws ObjectAlreadyHasException {
|
||||
setPermission(node + "$" + expireAt, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a permission for the object
|
||||
* @param node The node to set
|
||||
* @param value What to set the node to - true/false(negated)
|
||||
* @param server The server to set the permission on
|
||||
* @param expireAt The time in unixtime when the permission will expire
|
||||
* @throws ObjectAlreadyHasException if the object already has the permission
|
||||
*/
|
||||
public void setPermission(String node, boolean value, String server, long expireAt) throws ObjectAlreadyHasException {
|
||||
setPermission(node + "$" + expireAt, value, server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a permission for the object
|
||||
* @param node The node to be unset
|
||||
* @param temporary if the permission being removed is temporary
|
||||
* @throws ObjectLacksException if the node wasn't already set
|
||||
*/
|
||||
public void unsetPermission(String node, boolean temporary) throws ObjectLacksException {
|
||||
if (node.startsWith("global/")) node = node.replace("global/", "");
|
||||
String match = null;
|
||||
|
||||
if (!temporary) {
|
||||
if (getNodes().containsKey(node)) {
|
||||
match = node;
|
||||
}
|
||||
} else {
|
||||
for (String n : getNodes().keySet()) {
|
||||
if (n.contains("$")) {
|
||||
String[] parts = Patterns.TEMP_SPLIT.split(n);
|
||||
if (parts[0].equalsIgnoreCase(node)) {
|
||||
match = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match != null) {
|
||||
getNodes().remove(match);
|
||||
} else {
|
||||
throw new ObjectLacksException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a permission for the object
|
||||
* @param node The node to be unset
|
||||
* @throws ObjectLacksException if the node wasn't already set
|
||||
*/
|
||||
public void unsetPermission(String node) throws ObjectLacksException {
|
||||
if (node.startsWith("global/")) node = node.replace("global/", "");
|
||||
if (!getNodes().containsKey(node)) {
|
||||
throw new ObjectLacksException();
|
||||
}
|
||||
getNodes().remove(node);
|
||||
unsetPermission(node, node.contains("$"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +245,17 @@ public abstract class PermissionObject {
|
||||
unsetPermission(server + "/" + node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a permission for the object
|
||||
* @param node The node to be unset
|
||||
* @param server The server to unset the node on
|
||||
* @param temporary if the permission being unset is temporary
|
||||
* @throws ObjectLacksException if the node wasn't already set
|
||||
*/
|
||||
public void unsetPermission(String node, String server, boolean temporary) throws ObjectLacksException {
|
||||
unsetPermission(server + "/" + node, temporary);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the permissions and inherited permissions that apply to a specific server
|
||||
* @param server The server to get nodes for
|
||||
@@ -156,6 +266,83 @@ public abstract class PermissionObject {
|
||||
return getPermissions(server, excludedGroups, includeGlobalPermissions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the objects and returns the temporary ones.
|
||||
* @return a map of temporary nodes
|
||||
*/
|
||||
public Map<Map.Entry<String, Boolean>, Long> getTemporaryNodes() {
|
||||
Map<Map.Entry<String, Boolean>, Long> temps = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Boolean> e : getNodes().entrySet()) {
|
||||
if (!e.getKey().contains("$")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] parts = Patterns.TEMP_SPLIT.split(e.getKey());
|
||||
final long expiry = Long.parseLong(parts[1]);
|
||||
temps.put(new AbstractMap.SimpleEntry<>(parts[0], e.getValue()), expiry);
|
||||
}
|
||||
|
||||
return temps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the objects and returns the non-temporary ones.
|
||||
* @return a map of permanent nodes
|
||||
*/
|
||||
public Map<String, Boolean> getPermanentNodes() {
|
||||
Map<String, Boolean> permas = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Boolean> e : getNodes().entrySet()) {
|
||||
if (e.getKey().contains("$")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
permas.put(e.getKey(), e.getValue());
|
||||
}
|
||||
|
||||
return permas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes temporary permissions that have expired
|
||||
* @return true if permissions had expired and had to be removed
|
||||
*/
|
||||
public boolean auditTemporaryPermissions() {
|
||||
Set<String> toRemove = getNodes().keySet().stream()
|
||||
.filter(s -> s.contains("$"))
|
||||
.filter(s -> DateUtil.shouldExpire(Long.parseLong(Patterns.TEMP_SPLIT.split(s)[1])))
|
||||
.collect(Collectors.toSet());
|
||||
toRemove.forEach(s -> getNodes().remove(s));
|
||||
return !toRemove.isEmpty();
|
||||
}
|
||||
|
||||
private String stripTime(String s) {
|
||||
if (s.contains("$")) {
|
||||
return Patterns.TEMP_SPLIT.split(s)[0];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
protected Map<String, Boolean> convertTemporaryPerms() {
|
||||
auditTemporaryPermissions();
|
||||
|
||||
Map<String, Boolean> nodes = new HashMap<>();
|
||||
Map<String, Boolean> tempNodes = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, Boolean> e : getNodes().entrySet()) {
|
||||
if (e.getKey().contains("$")) {
|
||||
tempNodes.put(e.getKey(), e.getValue());
|
||||
} else {
|
||||
nodes.put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
// temporary permissions override non-temporary permissions
|
||||
tempNodes.entrySet().forEach(e -> nodes.put(stripTime(e.getKey()), e.getValue()));
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private Map<String, Boolean> getPermissions(String server, List<String> excludedGroups, boolean includeGlobal) {
|
||||
if (excludedGroups == null) {
|
||||
excludedGroups = new ArrayList<>();
|
||||
@@ -183,7 +370,7 @@ public abstract class PermissionObject {
|
||||
final Map<String, Boolean> groupNodes = new HashMap<>();
|
||||
|
||||
// Sorts the permissions and puts them into a priority order
|
||||
for (Map.Entry<String, Boolean> node : getNodes().entrySet()) {
|
||||
for (Map.Entry<String, Boolean> node : convertTemporaryPerms().entrySet()) {
|
||||
serverSpecific:
|
||||
if (node.getKey().contains("/")) {
|
||||
String[] parts = Patterns.SERVER_SPLIT.split(node.getKey(), 2);
|
||||
|
||||
Reference in New Issue
Block a user