feat: implement permissions
This commit is contained in:
parent
488deea164
commit
7ecb8bfabb
@ -1,170 +1,124 @@
|
|||||||
package me.topchetoeu.jscript.permissions;
|
package me.topchetoeu.jscript.permissions;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
public class Permission {
|
public class Permission {
|
||||||
public final String[][] segments;
|
private static class State {
|
||||||
|
public final int predI, trgI, wildcardI;
|
||||||
|
public final boolean wildcard;
|
||||||
|
|
||||||
private boolean matchSeg(String a, String b) {
|
@Override
|
||||||
if (a.contains("**") || b.contains("**")) throw new IllegalArgumentException("A '**' segment may not contain other characters.");
|
public String toString() {
|
||||||
|
return "State [pr=%s;trg=%s;wildN=%s;wild=%s]".formatted(predI, trgI, wildcardI, wildcard);
|
||||||
var segA = a.split("\\*", -1);
|
|
||||||
var segB = b.split("\\*", -1);
|
|
||||||
|
|
||||||
if (segA.length == 1 || segB.length == 1) {
|
|
||||||
if (segA.length == 1 && segB.length == 1) return a.equals(b);
|
|
||||||
else if (segA.length == 1) return matchSeg(b, a);
|
|
||||||
else {
|
|
||||||
if (!b.startsWith(segA[0]) || !b.endsWith(segA[segA.length - 1])) return false;
|
|
||||||
|
|
||||||
int end = b.length() - segA[segA.length - 1].length();
|
|
||||||
|
|
||||||
for (int i = 1, findI = 1; i < segA.length - 1; i++) {
|
|
||||||
findI = b.indexOf(segA[i], findI);
|
|
||||||
if (findI < 0 || findI + segA[i].length() > end) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String firstA = segA[0], firstB = segB[0];
|
public State(int predicateI, int targetI, int wildcardI, boolean wildcard) {
|
||||||
String lastA = segA[segA.length - 1], lastB = segB[segB.length - 1];
|
this.predI = predicateI;
|
||||||
|
this.trgI = targetI;
|
||||||
if (!firstA.startsWith(firstB) && !firstB.startsWith(firstA)) return false;
|
this.wildcardI = wildcardI;
|
||||||
if (!lastA.endsWith(lastB) && !lastB.endsWith(lastA)) return false;
|
this.wildcard = wildcard;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private boolean matchArrs(String[] a, String[] b, int start, int end) {
|
|
||||||
if (a.length != end - start) return false;
|
|
||||||
if (a.length == 0) return true;
|
|
||||||
if (start >= b.length || end > b.length) return false;
|
|
||||||
if (start < 0 || end <= 0) return false;
|
|
||||||
|
|
||||||
for (var i = start; i < end; i++) {
|
|
||||||
if (!matchSeg(a[i - start], b[i])) return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
private boolean matchFull(String[] a, String[] b) {
|
|
||||||
return matchArrs(a, b, 0, b.length);
|
|
||||||
}
|
|
||||||
private boolean matchStart(String[] a, String[] b) {
|
|
||||||
return matchArrs(a, b, 0, a.length);
|
|
||||||
}
|
|
||||||
private boolean matchEnd(String[] a, String[] b) {
|
|
||||||
return matchArrs(a, b, b.length - a.length, b.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int find(String[] query, String[] target, int start, int end) {
|
public final String namespace;
|
||||||
var findI = 0;
|
public final String value;
|
||||||
|
|
||||||
if (start < 0) start = 0;
|
public boolean match(Permission perm) {
|
||||||
if (query.length == 0) return start;
|
if (!Permission.match(namespace, perm.namespace, '.')) return false;
|
||||||
if (start != 0 && start >= target.length) return -1;
|
if (value == null || perm.value == null) return true;
|
||||||
|
return Permission.match(value, perm.value);
|
||||||
for (var i = start; i < end; i++) {
|
}
|
||||||
if (findI == query.length) return i - findI;
|
public boolean match(Permission perm, char delim) {
|
||||||
else if (matchSeg(query[findI], target[i])) findI++;
|
if (!Permission.match(namespace, perm.namespace, '.')) return false;
|
||||||
else {
|
if (value == null || perm.value == null) return true;
|
||||||
i -= findI;
|
return Permission.match(value, perm.value, delim);
|
||||||
findI = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean match(Permission other) {
|
public boolean match(String perm) {
|
||||||
var an = this.segments.length;
|
return match(new Permission(perm));
|
||||||
var bn = other.segments.length;
|
}
|
||||||
|
public boolean match(String perm, char delim) {
|
||||||
// We must have at least one segment, even if empty
|
return match(new Permission(perm), delim);
|
||||||
if (an == 0 || bn == 0) throw new IllegalArgumentException("Can't have a permission with 0 segments.");
|
|
||||||
|
|
||||||
if (an == 1 || bn == 1) {
|
|
||||||
// If both perms are one segment, we just match the segments themselves
|
|
||||||
if (an == 1 && bn == 1) return matchFull(this.segments[0], other.segments[0]);
|
|
||||||
else if (an == 1) return other.match(this);
|
|
||||||
else {
|
|
||||||
// If just the other perm is one segment, we neet to find all
|
|
||||||
// the segments of this perm sequentially in the other segment.
|
|
||||||
|
|
||||||
var seg = other.segments[0];
|
|
||||||
// Here we check for the prefix and suffix
|
|
||||||
if (!matchStart(this.segments[0], seg)) return false;
|
|
||||||
if (!matchEnd(this.segments[this.segments.length - 1], seg)) return false;
|
|
||||||
|
|
||||||
int end = seg.length - this.segments[this.segments.length - 1].length;
|
|
||||||
|
|
||||||
// Here we go and look for the segments one by one, until one isn't found
|
|
||||||
for (int i = 1, findI = 1; i < this.segments.length - 1; i++) {
|
|
||||||
findI = find(this.segments[i], seg, findI, end);
|
|
||||||
if (findI < 0) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both perms have more than one segment (a.k.a both have **),
|
|
||||||
// we can ignore everything in the middle, as it will always match.
|
|
||||||
// Instead, we check if the prefixes and suffixes match
|
|
||||||
|
|
||||||
var firstA = this.segments[0];
|
|
||||||
var firstB = other.segments[0];
|
|
||||||
|
|
||||||
var lastA = this.segments[this.segments.length - 1];
|
|
||||||
var lastB = other.segments[other.segments.length - 1];
|
|
||||||
|
|
||||||
if (!matchStart(firstA, firstB) && !matchStart(firstB, firstA)) return false;
|
|
||||||
if (!matchEnd(lastA, lastB) && !matchEnd(lastB, lastA)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
var sb = new StringBuilder();
|
if (value != null) return namespace + ":" + value;
|
||||||
var firstSeg = true;
|
else return namespace;
|
||||||
var firstEl = true;
|
|
||||||
|
|
||||||
for (var seg : segments) {
|
|
||||||
if (!firstSeg) {
|
|
||||||
if (!firstEl) sb.append(".");
|
|
||||||
sb.append("**");
|
|
||||||
}
|
|
||||||
firstSeg = false;
|
|
||||||
for (var el : seg) {
|
|
||||||
if (!firstEl) sb.append(".");
|
|
||||||
sb.append(el);
|
|
||||||
firstEl = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Permission(String raw) {
|
public Permission(String raw) {
|
||||||
var segs = raw.split("\\.");
|
var i = raw.indexOf(':');
|
||||||
var curr = new ArrayList<String>();
|
|
||||||
var res = new ArrayList<String[]>();
|
|
||||||
|
|
||||||
for (var seg : segs) {
|
if (i > 0) {
|
||||||
if (seg.equals("**")) {
|
value = raw.substring(i + 1);
|
||||||
res.add(curr.toArray(String[]::new));
|
namespace = raw.substring(0, i);
|
||||||
curr.clear();
|
}
|
||||||
}
|
else {
|
||||||
else curr.add(seg);
|
value = null;
|
||||||
|
namespace = raw;
|
||||||
}
|
}
|
||||||
res.add(curr.toArray(String[]::new));
|
|
||||||
|
|
||||||
segments = res.toArray(String[][]::new);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean match(String a, String b) {
|
public static boolean match(String predicate, String target, char delim) {
|
||||||
return new Permission(a).match(new Permission(b));
|
if (predicate.equals("")) return target.equals("");
|
||||||
|
|
||||||
|
var queue = new LinkedList<State>();
|
||||||
|
queue.push(new State(0, 0, 0, false));
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
var state = queue.poll();
|
||||||
|
var predEnd = state.predI >= predicate.length();
|
||||||
|
|
||||||
|
if (state.trgI >= target.length()) return predEnd;
|
||||||
|
var predC = predEnd ? 0 : predicate.charAt(state.predI);
|
||||||
|
var trgC = target.charAt(state.trgI);
|
||||||
|
|
||||||
|
if (state.wildcard) {
|
||||||
|
if (state.wildcardI == 2 || trgC != delim) {
|
||||||
|
queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true));
|
||||||
|
}
|
||||||
|
queue.add(new State(state.predI, state.trgI, 0, false));
|
||||||
|
}
|
||||||
|
else if (predC == '*') {
|
||||||
|
queue.add(new State(state.predI + 1, state.trgI, state.wildcardI + 1, false));
|
||||||
|
}
|
||||||
|
else if (state.wildcardI > 0) {
|
||||||
|
if (state.wildcardI > 2) throw new IllegalArgumentException("Too many sequential stars.");
|
||||||
|
queue.add(new State(state.predI, state.trgI, state.wildcardI, true));
|
||||||
|
}
|
||||||
|
else if (!predEnd && (predC == '?' || predC == trgC)) {
|
||||||
|
queue.add(new State(state.predI + 1, state.trgI + 1, 0, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public static boolean match(String predicate, String target) {
|
||||||
|
if (predicate.equals("")) return target.equals("");
|
||||||
|
|
||||||
|
var queue = new LinkedList<State>();
|
||||||
|
queue.push(new State(0, 0, 0, false));
|
||||||
|
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
var state = queue.poll();
|
||||||
|
|
||||||
|
if (state.predI >= predicate.length() || state.trgI >= target.length()) {
|
||||||
|
return state.predI >= predicate.length() && state.trgI >= target.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
var predC = predicate.charAt(state.predI);
|
||||||
|
var trgC = target.charAt(state.trgI);
|
||||||
|
|
||||||
|
if (predC == '*') {
|
||||||
|
queue.add(new State(state.predI, state.trgI + 1, state.wildcardI, true));
|
||||||
|
queue.add(new State(state.predI + 1, state.trgI, 0, false));
|
||||||
|
}
|
||||||
|
else if (predC == '?' || predC == trgC) {
|
||||||
|
queue.add(new State(state.predI + 1, state.trgI + 1, 0, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,39 @@
|
|||||||
package me.topchetoeu.jscript.permissions;
|
package me.topchetoeu.jscript.permissions;
|
||||||
|
|
||||||
public interface PermissionsManager {
|
import java.util.ArrayList;
|
||||||
public static final PermissionsManager ALL_PERMS = perm -> true;
|
|
||||||
public static final PermissionsManager NO_PERMS = perm -> false;
|
|
||||||
|
|
||||||
boolean hasPermissions(String perm);
|
public class PermissionsManager {
|
||||||
|
public static final PermissionsManager ALL_PERMS = new PermissionsManager().add(new Permission("**"));
|
||||||
|
|
||||||
|
public final ArrayList<Permission> allowed = new ArrayList<>();
|
||||||
|
public final ArrayList<Permission> denied = new ArrayList<>();
|
||||||
|
|
||||||
|
public PermissionsManager add(Permission perm) {
|
||||||
|
allowed.add(perm);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public PermissionsManager add(String perm) {
|
||||||
|
allowed.add(new Permission(perm));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(Permission perm, char delim) {
|
||||||
|
for (var el : denied) if (el.match(perm, delim)) return false;
|
||||||
|
for (var el : allowed) if (el.match(perm, delim)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public boolean has(Permission perm) {
|
||||||
|
for (var el : denied) if (el.match(perm)) return false;
|
||||||
|
for (var el : allowed) if (el.match(perm)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean has(String perm, char delim) {
|
||||||
|
return has(new Permission(perm), delim);
|
||||||
|
}
|
||||||
|
public boolean has(String perm) {
|
||||||
|
return has(new Permission(perm));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user