Add temporary permissions

This commit is contained in:
Luck
2016-07-19 12:54:39 +01:00
Unverified
parent 24ff465679
commit cdf8a4ce18
54 changed files with 1221 additions and 152 deletions
@@ -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);