From 44d14001e1bf9dcbad560369dce96abf4d4dd834 Mon Sep 17 00:00:00 2001 From: Luck Date: Thu, 25 Aug 2016 17:23:27 +0100 Subject: [PATCH] Partially implement PermissionService --- .../lucko/luckperms/constants/Patterns.java | 1 + .../luckperms/core/PermissionHolder.java | 1 + .../luckperms/service/LuckPermsService.java | 194 ++++++++++++++++ .../service/collections/GroupCollection.java | 86 +++++++ .../service/collections/UserCollection.java | 104 +++++++++ .../service/simple/SimpleCollection.java | 85 +++++++ .../service/simple/SimpleSubject.java | 125 ++++++++++ .../service/wrapping/LuckPermsSubject.java | 215 ++++++++++++++++++ 8 files changed, 811 insertions(+) create mode 100644 sponge/src/main/java/me/lucko/luckperms/service/LuckPermsService.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/service/collections/GroupCollection.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/service/collections/UserCollection.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleCollection.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleSubject.java create mode 100644 sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java diff --git a/common/src/main/java/me/lucko/luckperms/constants/Patterns.java b/common/src/main/java/me/lucko/luckperms/constants/Patterns.java index 92bb4e91..048e0f3c 100644 --- a/common/src/main/java/me/lucko/luckperms/constants/Patterns.java +++ b/common/src/main/java/me/lucko/luckperms/constants/Patterns.java @@ -44,6 +44,7 @@ public class Patterns { public static final Pattern NON_USERNAME = Pattern.compile("[^A-Za-z0-9_]"); public static final Pattern SHORTHAND_NODE = Pattern.compile("\\.\\([^.]+\\)"); public static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf('ยง') + "[0-9A-FK-OR]"); + public static final Pattern NODE_CONTEXTS = Pattern.compile("\\(.+\\).*"); public static Pattern compile(String regex) throws PatternSyntaxException { if (!CACHE.containsKey(regex)) { diff --git a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java index 6c811d3c..8bcc927b 100644 --- a/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java +++ b/common/src/main/java/me/lucko/luckperms/core/PermissionHolder.java @@ -815,6 +815,7 @@ public abstract class PermissionHolder { return input; } + // TODO Support the "Sponge way" of doing wildcards private static Map applyWildcards(Map input, List possibleNodes) { SortedMap> wildcards = new TreeMap<>(Collections.reverseOrder()); for (Map.Entry e : input.entrySet()) { diff --git a/sponge/src/main/java/me/lucko/luckperms/service/LuckPermsService.java b/sponge/src/main/java/me/lucko/luckperms/service/LuckPermsService.java new file mode 100644 index 00000000..76d6a45a --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/service/LuckPermsService.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.service; + +import com.google.common.collect.ImmutableSet; +import lombok.*; +import me.lucko.luckperms.LPSpongePlugin; +import me.lucko.luckperms.service.collections.GroupCollection; +import me.lucko.luckperms.service.collections.UserCollection; +import me.lucko.luckperms.service.simple.SimpleCollection; +import org.spongepowered.api.plugin.PluginContainer; +import org.spongepowered.api.service.context.ContextCalculator; +import org.spongepowered.api.service.permission.*; +import org.spongepowered.api.text.Text; +import org.spongepowered.api.util.Tristate; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public class LuckPermsService implements PermissionService { + public static final String SERVER_CONTEXT = "server"; + + private final LPSpongePlugin plugin; + + @Getter + private final UserCollection userSubjects; + + @Getter + private final GroupCollection groupSubjects; + + @Getter + private final Set descriptionSet; + + private final Map subjects; + private final Set> contextCalculators; + + public LuckPermsService(LPSpongePlugin plugin) { + this.plugin = plugin; + + userSubjects = new UserCollection(this, plugin.getUserManager()); + groupSubjects = new GroupCollection(this, plugin.getGroupManager()); + + subjects = new ConcurrentHashMap<>(); + subjects.put(PermissionService.SUBJECTS_USER, userSubjects); + subjects.put(PermissionService.SUBJECTS_GROUP, groupSubjects); + + descriptionSet = ConcurrentHashMap.newKeySet(); + contextCalculators = ConcurrentHashMap.newKeySet(); + } + + @Override + public SubjectData getDefaultData() { + return null; // TODO + } + + @Override + public SubjectCollection getSubjects(String s) { + if (!subjects.containsKey(s)) { + subjects.put(s, new SimpleCollection(this, s)); + } + + return subjects.get(s); + } + + @Override + public Map getKnownSubjects() { + return subjects; + } + + @Override + public Optional newDescriptionBuilder(@NonNull Object o) { + Optional container = plugin.getGame().getPluginManager().fromInstance(o); + if (!container.isPresent()) { + throw new IllegalArgumentException("Couldn't find a plugin container for " + o.getClass().getSimpleName()); + } + + return Optional.of(new DescriptionBuilder(this, container.get())); + } + + @Override + public Optional getDescription(String s) { + for (PermissionDescription d : descriptionSet) { + if (d.getId().equals(s)) { + return Optional.of(d); + } + } + + return Optional.empty(); + } + + @Override + public Collection getDescriptions() { + return ImmutableSet.copyOf(descriptionSet); + } + + @Override + public void registerContextCalculator(ContextCalculator contextCalculator) { + contextCalculators.add(contextCalculator); + } + + public List getPossiblePermissions() { + return getDescriptions().stream().map(PermissionDescription::getId).collect(Collectors.toList()); + } + + @RequiredArgsConstructor + @EqualsAndHashCode + @ToString + public static class DescriptionBuilder implements PermissionDescription.Builder { + private final LuckPermsService service; + private final PluginContainer container; + + private String id = null; + private Text description = null; + private final Map roles = new HashMap<>(); + + @Override + public PermissionDescription.Builder id(@NonNull String s) { + id = s; + return this; + } + + @Override + public PermissionDescription.Builder description(@NonNull Text text) { + description = text; + return this; + } + + @Override + public PermissionDescription.Builder assign(@NonNull String s, boolean b) { + roles.put(s, Tristate.fromBoolean(b)); + return this; + } + + @Override + public PermissionDescription register() throws IllegalStateException { + if (id == null) { + throw new IllegalStateException("id cannot be null"); + } + if (description == null) { + throw new IllegalStateException("description cannot be null"); + } + + Description d = new Description(service, container, id, description); + service.getDescriptionSet().add(d); + + // Set role-templates + SubjectCollection subjects = service.getSubjects(PermissionService.SUBJECTS_ROLE_TEMPLATE); + for (Map.Entry assignment : roles.entrySet()) { + Subject subject = subjects.get(assignment.getKey()); + subject.getTransientSubjectData().setPermission(SubjectData.GLOBAL_CONTEXT, id, assignment.getValue()); + } + + return d; + } + } + + @Getter + @AllArgsConstructor + @EqualsAndHashCode + @ToString + public static class Description implements PermissionDescription { + private final LuckPermsService service; + private final PluginContainer owner; + private final String id; + private final Text description; + + @Override + public Map getAssignedSubjects(String id) { + SubjectCollection subjects = service.getSubjects(id); + return subjects.getAllWithPermission(this.id); + } + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/service/collections/GroupCollection.java b/sponge/src/main/java/me/lucko/luckperms/service/collections/GroupCollection.java new file mode 100644 index 00000000..ad34154a --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/service/collections/GroupCollection.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.service.collections; + +import lombok.AllArgsConstructor; +import lombok.NonNull; +import me.lucko.luckperms.groups.GroupManager; +import me.lucko.luckperms.service.LuckPermsService; +import me.lucko.luckperms.service.simple.SimpleSubject; +import me.lucko.luckperms.service.wrapping.LuckPermsSubject; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.PermissionService; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.service.permission.SubjectCollection; +import org.spongepowered.api.service.permission.SubjectData; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class GroupCollection implements SubjectCollection { + private final LuckPermsService service; + private final GroupManager manager; + + @Override + public String getIdentifier() { + return PermissionService.SUBJECTS_GROUP; + } + + @Override + public Subject get(@NonNull String id) { + if (manager.isLoaded(id)) { + return new LuckPermsSubject(manager.get(id), service); + } + + return new SimpleSubject(id, service, this); + } + + @Override + public boolean hasRegistered(@NonNull String id) { + return manager.isLoaded(id); + } + + @Override + public Iterable getAllSubjects() { + return manager.getAll().values().stream() + .map(u -> new LuckPermsSubject(u, service)) + .collect(Collectors.toList()); + } + + @Override + public Map getAllWithPermission(@NonNull String node) { + return getAllWithPermission(SubjectData.GLOBAL_CONTEXT, node); + } + + @Override + public Map getAllWithPermission(@NonNull Set contexts, @NonNull String node) { + return manager.getAll().values().stream() + .map(u -> new LuckPermsSubject(u, service)) + .filter(sub -> sub.hasPermission(contexts, node)) + .map(sub -> new AbstractMap.SimpleEntry(sub, sub.getPermissionValue(contexts, node).asBoolean())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/service/collections/UserCollection.java b/sponge/src/main/java/me/lucko/luckperms/service/collections/UserCollection.java new file mode 100644 index 00000000..5c896c0d --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/service/collections/UserCollection.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.service.collections; + +import lombok.AllArgsConstructor; +import lombok.NonNull; +import me.lucko.luckperms.service.LuckPermsService; +import me.lucko.luckperms.service.simple.SimpleSubject; +import me.lucko.luckperms.service.wrapping.LuckPermsSubject; +import me.lucko.luckperms.users.User; +import me.lucko.luckperms.users.UserManager; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.PermissionService; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.service.permission.SubjectCollection; +import org.spongepowered.api.service.permission.SubjectData; + +import java.util.AbstractMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class UserCollection implements SubjectCollection { + private final LuckPermsService service; + private final UserManager manager; + + @Override + public String getIdentifier() { + return PermissionService.SUBJECTS_USER; + } + + @Override + public Subject get(@NonNull String id) { + try { + UUID u = UUID.fromString(id); + if (manager.isLoaded(u)) { + return new LuckPermsSubject(manager.get(u), service); + } + + } catch (IllegalArgumentException e) { + User user = manager.get(id); + if (user != null) { + return new LuckPermsSubject(user, service); + } + } + + // Wtf am I meant to do here? What if no user is loaded? Load it? Create it? + return new SimpleSubject(id, service, this); + } + + @Override + public boolean hasRegistered(@NonNull String id) { + try { + UUID u = UUID.fromString(id); + return manager.isLoaded(u); + } catch (IllegalArgumentException e) { + User user = manager.get(id); + return user != null; + } + } + + @Override + public Iterable getAllSubjects() { + return manager.getAll().values().stream() + .map(u -> new LuckPermsSubject(u, service)) + .collect(Collectors.toList()); + } + + @Override + public Map getAllWithPermission(@NonNull String id) { + return getAllWithPermission(SubjectData.GLOBAL_CONTEXT, id); + } + + @Override + public Map getAllWithPermission(@NonNull Set contexts, @NonNull String node) { + return manager.getAll().values().stream() + .map(u -> new LuckPermsSubject(u, service)) + .filter(sub -> sub.hasPermission(contexts, node)) + .map(sub -> new AbstractMap.SimpleEntry(sub, sub.getPermissionValue(contexts, node).asBoolean())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleCollection.java b/sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleCollection.java new file mode 100644 index 00000000..e1f2ad0f --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleCollection.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.service.simple; + +import lombok.Getter; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.PermissionService; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.service.permission.SubjectCollection; +import org.spongepowered.api.util.Tristate; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@RequiredArgsConstructor +public class SimpleCollection implements SubjectCollection { + private final PermissionService service; + + @Getter + private final String identifier; + + private final Map subjects = new ConcurrentHashMap<>(); + + @Override + public Subject get(@NonNull String id) { + if (!subjects.containsKey(id)) { + subjects.put(id, new SimpleSubject(id, service, this)); + } + + return subjects.get(id); + } + + @Override + public boolean hasRegistered(@NonNull String id) { + return subjects.containsKey(id); + } + + @Override + public Iterable getAllSubjects() { + return subjects.values(); + } + + @Override + public Map getAllWithPermission(@NonNull String id) { + return getAllWithPermission(Collections.emptySet(), id); + } + + @Override + public Map getAllWithPermission(@NonNull Set contexts, @NonNull String node) { + Map m = new HashMap<>(); + for (Subject subject : subjects.values()) { + Tristate ts = subject.getPermissionValue(contexts, node); + if (ts != Tristate.UNDEFINED) { + m.put(subject, ts.asBoolean()); + } + + } + return m; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleSubject.java b/sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleSubject.java new file mode 100644 index 00000000..3afa2a40 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/service/simple/SimpleSubject.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.service.simple; + +import lombok.Getter; +import lombok.NonNull; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.*; +import org.spongepowered.api.util.Tristate; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Super simple Subject implementation. + */ +@Getter +public class SimpleSubject implements Subject { + private final String identifier; + + private final PermissionService service; + private final SubjectCollection containingCollection; + private final SubjectData subjectData; + + private final Map, Map> perms = new ConcurrentHashMap<>(); + private final Map, Set> parents = new ConcurrentHashMap<>(); + + public SimpleSubject(String identifier, PermissionService service, SubjectCollection containingCollection) { + this.identifier = identifier; + this.service = service; + this.containingCollection = containingCollection; + this.subjectData = new MemorySubjectData(service); + } + + @Override + public Optional getCommandSource() { + return Optional.empty(); + } + + @Override + public SubjectData getTransientSubjectData() { + return getSubjectData(); + } + + @Override + public boolean hasPermission(@NonNull Set contexts, @NonNull String node) { + return getPermissionValue(contexts, node).asBoolean(); + } + + @Override + public boolean hasPermission(@NonNull String permission) { + return getPermissionValue(getActiveContexts(), permission).asBoolean(); + } + + @Override + public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String node) { + if (!perms.containsKey(contexts)) { + return Tristate.UNDEFINED; + } + + Map context = perms.get(contexts); + if (context.containsKey(node)) { + return context.get(node); + } + + for (Subject parent : getParents(contexts)) { + Tristate ts = parent.getPermissionValue(contexts, node); + if (ts != Tristate.UNDEFINED) { + return ts; + } + } + + return Tristate.UNDEFINED; + } + + @Override + public boolean isChildOf(@NonNull Subject parent) { + return isChildOf(getActiveContexts(), parent); + } + + @Override + public boolean isChildOf(@NonNull Set contexts, @NonNull Subject subject) { + return parents.containsKey(contexts) && parents.get(contexts).contains(subject); + } + + @Override + public List getParents() { + return getParents(getActiveContexts()); + } + + @Override + public List getParents(@NonNull Set contexts) { + if (!parents.containsKey(contexts)) { + return Collections.emptyList(); + } + + return new ArrayList<>(parents.get(contexts)); + } + + @Override + public Set getActiveContexts() { + return SubjectData.GLOBAL_CONTEXT; + } +} diff --git a/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java b/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java new file mode 100644 index 00000000..a5663004 --- /dev/null +++ b/sponge/src/main/java/me/lucko/luckperms/service/wrapping/LuckPermsSubject.java @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.service.wrapping; + +import lombok.AllArgsConstructor; +import lombok.NonNull; +import me.lucko.luckperms.constants.Patterns; +import me.lucko.luckperms.core.PermissionHolder; +import me.lucko.luckperms.groups.Group; +import me.lucko.luckperms.service.LuckPermsService; +import me.lucko.luckperms.users.User; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.CommandSource; +import org.spongepowered.api.entity.living.player.Player; +import org.spongepowered.api.service.context.Context; +import org.spongepowered.api.service.permission.Subject; +import org.spongepowered.api.service.permission.SubjectCollection; +import org.spongepowered.api.service.permission.SubjectData; +import org.spongepowered.api.util.Tristate; + +import java.util.*; +import java.util.stream.Collectors; + +@AllArgsConstructor +public class LuckPermsSubject implements Subject { + private final PermissionHolder holder; + private final LuckPermsService service; + + @Override + public String getIdentifier() { + return holder.getObjectName(); + } + + @Override + public Optional getCommandSource() { + if (holder instanceof User) { + final UUID uuid = ((User) holder).getUuid(); + + Optional p = Sponge.getServer().getPlayer(uuid); + if (p.isPresent()) { + return Optional.of(p.get()); + } + } + + return Optional.empty(); + } + + @Override + public SubjectCollection getContainingCollection() { + if (holder instanceof Group) { + return service.getGroupSubjects(); + } else { + return service.getUserSubjects(); + } + } + + @Override + public SubjectData getSubjectData() { + return null; // TODO + } + + @Override + public SubjectData getTransientSubjectData() { + return null; // TODO + } + + @Override + public boolean hasPermission(@NonNull Set contexts, @NonNull String node) { + return getPermissionValue(contexts, node).asBoolean(); + } + + @Override + public boolean hasPermission(String permission) { + return getPermissionValue(getActiveContexts(), permission).asBoolean(); + } + + private Map applyContexts(@NonNull Set set) { + final Map map = new HashMap<>(); + + String world = null; + String server = null; + + Map contexts = new HashMap<>(); + + for (Context context : set) { + if (context.getType().equals(Context.WORLD_KEY)) { + world = context.getName(); + continue; + } + + if (context.getType().equals(LuckPermsService.SERVER_CONTEXT)) { + server = context.getName(); + continue; + } + + contexts.put(context.getType(), context.getName()); + } + + Map local = holder.getLocalPermissions(server, world, null, service.getPossiblePermissions()); + perms: + for (Map.Entry e : local.entrySet()) { + if (!contexts.isEmpty()) { + if (!Patterns.NODE_CONTEXTS.matcher(e.getKey()).matches()) { + continue; + } + + String[] parts = e.getKey().substring(1).split("\\)", 2); + // 0 = context, 1 = node + + // Parse the context values from this node + Map contextValues = new HashMap<>(); + for (String s : parts[0].split("\\,")) { + if (!s.contains("=")) { + // Not valid + continue; + } + + // contextKey=value + String[] con = s.split("\\=", 2); + contextValues.put(con[0], con[1]); + } + + // Check that all of the requested contexts are met + for (Map.Entry req : contexts.entrySet()) { + if (!contextValues.containsKey(e.getKey())) { + continue; + } + + if (!contextValues.get(req.getKey()).equalsIgnoreCase(req.getValue())) { + // Not valid within the current contexts + continue perms; + } + } + + // Passed all da tests. + map.put(parts[1], e.getValue()); + } else { + map.put(e.getKey(), e.getValue()); + } + + } + + return map; + } + + @Override + public Tristate getPermissionValue(@NonNull Set contexts, @NonNull String node) { + final Map nodes = applyContexts(contexts); + + if (nodes.containsKey(node)) { + return Tristate.fromBoolean(nodes.get(node)); + } else { + return Tristate.UNDEFINED; + } + } + + @Override + public boolean isChildOf(@NonNull Subject parent) { + return isChildOf(getActiveContexts(), parent); + } + + @Override + public boolean isChildOf(@NonNull Set contexts, @NonNull Subject parent) { + return parent instanceof PermissionHolder && getPermissionValue(contexts, "group." + parent.getIdentifier()).asBoolean(); + } + + @Override + public List getParents() { + return getParents(getActiveContexts()); + } + + @Override + public List getParents(@NonNull Set contexts) { + final Set parents = new HashSet<>(); + final Map nodes = applyContexts(contexts); + + for (Map.Entry e : nodes.entrySet()) { + if (!e.getValue()) { + continue; + } + + if (Patterns.GROUP_MATCH.matcher(e.getKey()).matches()) { + final String groupName = e.getKey().substring("group.".length()); + parents.add(groupName); + } + } + + return parents.stream().map(s -> service.getGroupSubjects().get(s)).collect(Collectors.toList()); + } + + @Override + public Set getActiveContexts() { + return SubjectData.GLOBAL_CONTEXT; + } +}