Add workaround for plugins adding/removing/modifying permission attachments via reflection (#1024)

who knows why they're doing it, it's not even beneficial for performance...
This commit is contained in:
Luck 2018-05-30 13:17:41 +01:00
parent c66622bd09
commit 644c53a074
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B
11 changed files with 361 additions and 43 deletions

View File

@ -64,6 +64,19 @@ public class DummyPermissibleBase extends PermissibleBase {
}
}
public static void copyFields(PermissibleBase from, PermissibleBase to) {
try {
ATTACHMENTS_FIELD.set(to, ATTACHMENTS_FIELD.get(from));
} catch (Exception e) {
// ignore
}
try {
PERMISSIONS_FIELD.set(to, PERMISSIONS_FIELD.get(from));
} catch (Exception e) {
// ignore
}
}
public static final DummyPermissibleBase INSTANCE = new DummyPermissibleBase();
private DummyPermissibleBase() {

View File

@ -25,11 +25,14 @@
package me.lucko.luckperms.bukkit.model.permissible;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.contexts.ContextsCache;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import org.bukkit.entity.Player;
@ -39,14 +42,18 @@ import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
/**
* PermissibleBase for LuckPerms.
*
@ -63,6 +70,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class LPPermissible extends PermissibleBase {
private static final Field ATTACHMENTS_FIELD;
static {
try {
ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
ATTACHMENTS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new ExceptionInInitializerError(e);
}
}
// the LuckPerms user this permissible references.
private final User user;
@ -83,7 +101,7 @@ public class LPPermissible extends PermissibleBase {
// the attachments hooked onto the permissible.
// this collection is only modified by the attachments themselves
final Set<LPPermissionAttachment> attachments = ConcurrentHashMap.newKeySet();
final Set<LPPermissionAttachment> lpAttachments = ConcurrentHashMap.newKeySet();
public LPPermissible(Player player, User user, LPBukkitPlugin plugin) {
super(player);
@ -91,6 +109,26 @@ public class LPPermissible extends PermissibleBase {
this.player = Objects.requireNonNull(player, "player");
this.plugin = Objects.requireNonNull(plugin, "plugin");
this.contextsCache = plugin.getContextManager().getCacheFor(player);
injectFakeAttachmentsList();
}
/**
* Injects a fake 'attachments' list into the superclass, for dumb plugins
* which for some reason decided to add attachments via reflection.
*
* The fake list proxies (some) calls back to the proper methods on this permissible.
*/
private void injectFakeAttachmentsList() {
FakeAttachmentList fakeList = new FakeAttachmentList();
try {
// the field we need to modify is in the superclass - it has private
// and final modifiers so we have to use reflection to modify it.
ATTACHMENTS_FIELD.set(this, fakeList);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@ -154,7 +192,7 @@ public class LPPermissible extends PermissibleBase {
*
* @param attachments the attachments to add
*/
public void convertAndAddAttachments(Collection<PermissionAttachment> attachments) {
void convertAndAddAttachments(Collection<PermissionAttachment> attachments) {
for (PermissionAttachment attachment : attachments) {
new LPPermissionAttachment(this, attachment).hook();
}
@ -167,14 +205,9 @@ public class LPPermissible extends PermissibleBase {
@Override
public Set<PermissionAttachmentInfo> getEffectivePermissions() {
Set<Map.Entry<String, Boolean>> permissions = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getImmutableBacking().entrySet();
Set<PermissionAttachmentInfo> ret = new HashSet<>(permissions.size());
for (Map.Entry<String, Boolean> entry : permissions) {
ret.add(new PermissionAttachmentInfo(this.player, entry.getKey(), null, entry.getValue()));
}
return ret;
return this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getImmutableBacking().entrySet().stream()
.map(entry -> new PermissionAttachmentInfo(this.player, entry.getKey(), null, entry.getValue()))
.collect(ImmutableCollectors.toSet());
}
@Override
@ -240,11 +273,20 @@ public class LPPermissible extends PermissibleBase {
throw new NullPointerException("attachment");
}
LPPermissionAttachment a;
if (!(attachment instanceof LPPermissionAttachment)) {
throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment.");
// try to find a match
LPPermissionAttachment match = this.lpAttachments.stream().filter(at -> at.getSource() == attachment).findFirst().orElse(null);
if (match != null) {
a = match;
} else {
throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment.");
}
} else {
a = (LPPermissionAttachment) attachment;
}
LPPermissionAttachment a = ((LPPermissionAttachment) attachment);
if (a.getPermissible() != this) {
throw new IllegalArgumentException("Attachment does not belong to this permissible.");
}
@ -259,7 +301,7 @@ public class LPPermissible extends PermissibleBase {
@Override
public void clearPermissions() {
this.attachments.forEach(LPPermissionAttachment::remove);
this.lpAttachments.forEach(LPPermissionAttachment::remove);
}
public User getUser() {
@ -274,15 +316,102 @@ public class LPPermissible extends PermissibleBase {
return this.plugin;
}
public PermissibleBase getOldPermissible() {
PermissibleBase getOldPermissible() {
return this.oldPermissible;
}
public AtomicBoolean getActive() {
AtomicBoolean getActive() {
return this.active;
}
public void setOldPermissible(PermissibleBase oldPermissible) {
void setOldPermissible(PermissibleBase oldPermissible) {
this.oldPermissible = oldPermissible;
}
/**
* A fake list to be injected into the superclass. This implementation simply
* proxies calls back to this permissible instance.
*
* Some (clever/dumb??) plugins attempt to add/remove/query attachments using reflection.
*
* An instance of this map is injected into the super instance so these plugins continue
* to work with LuckPerms.
*/
private final class FakeAttachmentList implements List<PermissionAttachment> {
@Override
public boolean add(PermissionAttachment attachment) {
if (LPPermissible.this.lpAttachments.stream().anyMatch(at -> at.getSource() == attachment)) {
return false;
}
new LPPermissionAttachment(LPPermissible.this, attachment).hook();
return true;
}
@Override
public boolean remove(Object o) {
removeAttachment((PermissionAttachment) o);
return true;
}
@Override
public void clear() {
clearPermissions();
}
@Override
public boolean addAll(@Nonnull Collection<? extends PermissionAttachment> c) {
boolean modified = false;
for (PermissionAttachment e : c) {
if (add(e)) {
modified = true;
}
}
return modified;
}
@Override
public boolean contains(Object o) {
PermissionAttachment attachment = (PermissionAttachment) o;
return LPPermissible.this.lpAttachments.stream().anyMatch(at -> at.getSource() == attachment);
}
@Override
public Iterator<PermissionAttachment> iterator() {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).iterator();
}
@Override
public ListIterator<PermissionAttachment> listIterator() {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).listIterator();
}
@Nonnull
@Override
public Object[] toArray() {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).toArray();
}
@Nonnull
@Override
public <T> T[] toArray(@Nonnull T[] a) {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).toArray(a);
}
@Override public int size() { throw new UnsupportedOperationException(); }
@Override public boolean isEmpty() { throw new UnsupportedOperationException(); }
@Override public boolean containsAll(@Nonnull Collection<?> c) { throw new UnsupportedOperationException(); }
@Override public boolean addAll(int index, @Nonnull Collection<? extends PermissionAttachment> c) { throw new UnsupportedOperationException(); }
@Override public boolean removeAll(@Nonnull Collection<?> c) { throw new UnsupportedOperationException(); }
@Override public boolean retainAll(@Nonnull Collection<?> c) { throw new UnsupportedOperationException(); }
@Override public PermissionAttachment get(int index) { throw new UnsupportedOperationException(); }
@Override public PermissionAttachment set(int index, PermissionAttachment element) { throw new UnsupportedOperationException(); }
@Override public void add(int index, PermissionAttachment element) { throw new UnsupportedOperationException(); }
@Override public PermissionAttachment remove(int index) { throw new UnsupportedOperationException(); }
@Override public int indexOf(Object o) { throw new UnsupportedOperationException(); }
@Override public int lastIndexOf(Object o) { throw new UnsupportedOperationException(); }
@Nonnull @Override public ListIterator<PermissionAttachment> listIterator(int index) { throw new UnsupportedOperationException(); }
@Nonnull @Override public List<PermissionAttachment> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); }
}
}

View File

@ -94,6 +94,11 @@ public class LPPermissionAttachment extends PermissionAttachment {
*/
private PermissionRemovedExecutor removalCallback = null;
/**
* Delegate attachment
*/
private PermissionAttachment source;
public LPPermissionAttachment(LPPermissible permissible, Plugin owner) {
super(DummyPlugin.INSTANCE, null);
this.permissible = permissible;
@ -102,13 +107,14 @@ public class LPPermissionAttachment extends PermissionAttachment {
injectFakeMap();
}
public LPPermissionAttachment(LPPermissible permissible, PermissionAttachment bukkit) {
public LPPermissionAttachment(LPPermissible permissible, PermissionAttachment source) {
super(DummyPlugin.INSTANCE, null);
this.permissible = permissible;
this.owner = null;
// copy
this.perms.putAll(bukkit.getPermissions());
this.perms.putAll(source.getPermissions());
this.source = source;
injectFakeMap();
}
@ -148,12 +154,16 @@ public class LPPermissionAttachment extends PermissionAttachment {
this.removalCallback = removalCallback;
}
PermissionAttachment getSource() {
return this.source;
}
/**
* Hooks this attachment with the parent {@link User} instance.
*/
public void hook() {
this.hooked = true;
this.permissible.attachments.add(this);
this.permissible.lpAttachments.add(this);
for (Map.Entry<String, Boolean> entry : this.perms.entrySet()) {
if (entry.getKey() == null || entry.getKey().isEmpty()) {
continue;
@ -214,7 +224,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
// unhook from the permissible
this.hooked = false;
this.permissible.attachments.remove(this);
this.permissible.lpAttachments.remove(this);
return true;
}

View File

@ -58,7 +58,7 @@ public class MonitoredPermissibleBase extends PermissibleBase {
public MonitoredPermissibleBase(LuckPermsPlugin plugin, PermissibleBase delegate, String name) {
super(null);
DummyPermissibleBase.nullFields(this);
DummyPermissibleBase.copyFields(delegate, this);
this.plugin = plugin;
this.delegate = delegate;

View File

@ -64,6 +64,19 @@ public class DummyPermissibleBase extends PermissibleBase {
}
}
public static void copyFields(PermissibleBase from, PermissibleBase to) {
try {
ATTACHMENTS_FIELD.set(to, ATTACHMENTS_FIELD.get(from));
} catch (Exception e) {
// ignore
}
try {
PERMISSIONS_FIELD.set(to, PERMISSIONS_FIELD.get(from));
} catch (Exception e) {
// ignore
}
}
public static final DummyPermissibleBase INSTANCE = new DummyPermissibleBase();
private DummyPermissibleBase() {

View File

@ -25,10 +25,13 @@
package me.lucko.luckperms.nukkit.model.permissible;
import com.google.common.collect.ImmutableList;
import me.lucko.luckperms.api.Tristate;
import me.lucko.luckperms.common.config.ConfigKeys;
import me.lucko.luckperms.common.contexts.ContextsCache;
import me.lucko.luckperms.common.model.User;
import me.lucko.luckperms.common.utils.ImmutableCollectors;
import me.lucko.luckperms.common.verbose.CheckOrigin;
import me.lucko.luckperms.nukkit.LPNukkitPlugin;
import me.lucko.luckperms.nukkit.model.PermissionDefault;
@ -40,14 +43,19 @@ import cn.nukkit.permission.PermissionAttachment;
import cn.nukkit.permission.PermissionAttachmentInfo;
import cn.nukkit.plugin.Plugin;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nonnull;
/**
* PermissibleBase for LuckPerms.
*
@ -64,6 +72,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class LPPermissible extends PermissibleBase {
private static final Field ATTACHMENTS_FIELD;
static {
try {
ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
ATTACHMENTS_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new ExceptionInInitializerError(e);
}
}
// the LuckPerms user this permissible references.
private final User user;
@ -84,7 +103,7 @@ public class LPPermissible extends PermissibleBase {
// the attachments hooked onto the permissible.
// this collection is only modified by the attachments themselves
final Set<LPPermissionAttachment> attachments = ConcurrentHashMap.newKeySet();
final Set<LPPermissionAttachment> lpAttachments = ConcurrentHashMap.newKeySet();
public LPPermissible(Player player, User user, LPNukkitPlugin plugin) {
super(player);
@ -92,6 +111,26 @@ public class LPPermissible extends PermissibleBase {
this.player = Objects.requireNonNull(player, "player");
this.plugin = Objects.requireNonNull(plugin, "plugin");
this.contextsCache = plugin.getContextManager().getCacheFor(player);
injectFakeAttachmentsList();
}
/**
* Injects a fake 'attachments' list into the superclass, for dumb plugins
* which for some reason decided to add attachments via reflection.
*
* The fake list proxies (some) calls back to the proper methods on this permissible.
*/
private void injectFakeAttachmentsList() {
FakeAttachmentList fakeList = new FakeAttachmentList();
try {
// the field we need to modify is in the superclass - it has private
// and final modifiers so we have to use reflection to modify it.
ATTACHMENTS_FIELD.set(this, fakeList);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@ -157,7 +196,7 @@ public class LPPermissible extends PermissibleBase {
*
* @param attachments the attachments to add
*/
public void convertAndAddAttachments(Collection<PermissionAttachment> attachments) {
void convertAndAddAttachments(Collection<PermissionAttachment> attachments) {
for (PermissionAttachment attachment : attachments) {
new LPPermissionAttachment(this, attachment).hook();
}
@ -170,14 +209,8 @@ public class LPPermissible extends PermissibleBase {
@Override
public Map<String, PermissionAttachmentInfo> getEffectivePermissions() {
Set<Map.Entry<String, Boolean>> permissions = this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getImmutableBacking().entrySet();
Map<String, PermissionAttachmentInfo> ret = new HashMap<>(permissions.size());
for (Map.Entry<String, Boolean> entry : permissions) {
ret.put(entry.getKey(), new PermissionAttachmentInfo(this.player, entry.getKey(), null, entry.getValue()));
}
return ret;
return this.user.getCachedData().getPermissionData(this.contextsCache.getContexts()).getImmutableBacking().entrySet().stream()
.collect(ImmutableCollectors.toMap(Map.Entry::getKey, entry -> new PermissionAttachmentInfo(this.player, entry.getKey(), null, entry.getValue())));
}
@Override
@ -225,11 +258,20 @@ public class LPPermissible extends PermissibleBase {
throw new NullPointerException("attachment");
}
LPPermissionAttachment a;
if (!(attachment instanceof LPPermissionAttachment)) {
throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment.");
// try to find a match
LPPermissionAttachment match = this.lpAttachments.stream().filter(at -> at.getSource() == attachment).findFirst().orElse(null);
if (match != null) {
a = match;
} else {
throw new IllegalArgumentException("Given attachment is not a LPPermissionAttachment.");
}
} else {
a = (LPPermissionAttachment) attachment;
}
LPPermissionAttachment a = ((LPPermissionAttachment) attachment);
if (a.getPermissible() != this) {
throw new IllegalArgumentException("Attachment does not belong to this permissible.");
}
@ -244,7 +286,7 @@ public class LPPermissible extends PermissibleBase {
@Override
public void clearPermissions() {
this.attachments.forEach(LPPermissionAttachment::remove);
this.lpAttachments.forEach(LPPermissionAttachment::remove);
}
public User getUser() {
@ -259,15 +301,102 @@ public class LPPermissible extends PermissibleBase {
return this.plugin;
}
public PermissibleBase getOldPermissible() {
PermissibleBase getOldPermissible() {
return this.oldPermissible;
}
public AtomicBoolean getActive() {
AtomicBoolean getActive() {
return this.active;
}
public void setOldPermissible(PermissibleBase oldPermissible) {
void setOldPermissible(PermissibleBase oldPermissible) {
this.oldPermissible = oldPermissible;
}
/**
* A fake list to be injected into the superclass. This implementation simply
* proxies calls back to this permissible instance.
*
* Some (clever/dumb??) plugins attempt to add/remove/query attachments using reflection.
*
* An instance of this map is injected into the super instance so these plugins continue
* to work with LuckPerms.
*/
private final class FakeAttachmentList implements List<PermissionAttachment> {
@Override
public boolean add(PermissionAttachment attachment) {
if (LPPermissible.this.lpAttachments.stream().anyMatch(at -> at.getSource() == attachment)) {
return false;
}
new LPPermissionAttachment(LPPermissible.this, attachment).hook();
return true;
}
@Override
public boolean remove(Object o) {
removeAttachment((PermissionAttachment) o);
return true;
}
@Override
public void clear() {
clearPermissions();
}
@Override
public boolean addAll(@Nonnull Collection<? extends PermissionAttachment> c) {
boolean modified = false;
for (PermissionAttachment e : c) {
if (add(e)) {
modified = true;
}
}
return modified;
}
@Override
public boolean contains(Object o) {
PermissionAttachment attachment = (PermissionAttachment) o;
return LPPermissible.this.lpAttachments.stream().anyMatch(at -> at.getSource() == attachment);
}
@Override
public Iterator<PermissionAttachment> iterator() {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).iterator();
}
@Override
public ListIterator<PermissionAttachment> listIterator() {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).listIterator();
}
@Nonnull
@Override
public Object[] toArray() {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).toArray();
}
@Nonnull
@Override
public <T> T[] toArray(@Nonnull T[] a) {
return ImmutableList.<PermissionAttachment>copyOf(LPPermissible.this.lpAttachments).toArray(a);
}
@Override public int size() { throw new UnsupportedOperationException(); }
@Override public boolean isEmpty() { throw new UnsupportedOperationException(); }
@Override public boolean containsAll(@Nonnull Collection<?> c) { throw new UnsupportedOperationException(); }
@Override public boolean addAll(int index, @Nonnull Collection<? extends PermissionAttachment> c) { throw new UnsupportedOperationException(); }
@Override public boolean removeAll(@Nonnull Collection<?> c) { throw new UnsupportedOperationException(); }
@Override public boolean retainAll(@Nonnull Collection<?> c) { throw new UnsupportedOperationException(); }
@Override public PermissionAttachment get(int index) { throw new UnsupportedOperationException(); }
@Override public PermissionAttachment set(int index, PermissionAttachment element) { throw new UnsupportedOperationException(); }
@Override public void add(int index, PermissionAttachment element) { throw new UnsupportedOperationException(); }
@Override public PermissionAttachment remove(int index) { throw new UnsupportedOperationException(); }
@Override public int indexOf(Object o) { throw new UnsupportedOperationException(); }
@Override public int lastIndexOf(Object o) { throw new UnsupportedOperationException(); }
@Nonnull @Override public ListIterator<PermissionAttachment> listIterator(int index) { throw new UnsupportedOperationException(); }
@Nonnull @Override public List<PermissionAttachment> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); }
}
}

View File

@ -96,6 +96,11 @@ public class LPPermissionAttachment extends PermissionAttachment {
*/
private PermissionRemovedExecutor removalCallback = null;
/**
* Delegate attachment
*/
private PermissionAttachment source;
public LPPermissionAttachment(LPPermissible permissible, Plugin owner) {
super(DummyPlugin.INSTANCE, null);
this.permissible = permissible;
@ -111,6 +116,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
// copy
this.perms.putAll(nukkit.getPermissions());
this.source = source;
injectFakeMap();
}
@ -149,12 +155,16 @@ public class LPPermissionAttachment extends PermissionAttachment {
this.removalCallback = removalCallback;
}
PermissionAttachment getSource() {
return this.source;
}
/**
* Hooks this attachment with the parent {@link User} instance.
*/
public void hook() {
this.hooked = true;
this.permissible.attachments.add(this);
this.permissible.lpAttachments.add(this);
for (Map.Entry<String, Boolean> entry : this.perms.entrySet()) {
if (entry.getKey() == null || entry.getKey().isEmpty()) {
continue;
@ -215,7 +225,7 @@ public class LPPermissionAttachment extends PermissionAttachment {
// unhook from the permissible
this.hooked = false;
this.permissible.attachments.remove(this);
this.permissible.lpAttachments.remove(this);
}
@Override

View File

@ -58,7 +58,7 @@ public class MonitoredPermissibleBase extends PermissibleBase {
public MonitoredPermissibleBase(LuckPermsPlugin plugin, PermissibleBase delegate, String name) {
super(null);
DummyPermissibleBase.nullFields(this);
DummyPermissibleBase.copyFields(delegate, this);
this.plugin = plugin;
this.delegate = delegate;

View File

@ -173,6 +173,11 @@ public final class SubjectProxy implements Subject, ProxiedSubject {
return CompatibilityUtil.convertContexts(getContextsCache().getContextSet());
}
@Override
public ImmutableContextSet getActiveContextSet() {
return getContextsCache().getContextSet();
}
@Override
public boolean equals(Object o) {
return o == this || o instanceof SubjectProxy && this.ref.equals(((SubjectProxy) o).ref);

View File

@ -174,6 +174,11 @@ public final class SubjectProxy implements Subject, ProxiedSubject {
return CompatibilityUtil.convertContexts(getContextsCache().getContextSet());
}
@Override
public ImmutableContextSet getActiveContextSet() {
return getContextsCache().getContextSet();
}
@Override
public boolean equals(Object o) {
return o == this || o instanceof SubjectProxy && this.ref.equals(((SubjectProxy) o).ref);

View File

@ -25,6 +25,8 @@
package me.lucko.luckperms.sponge.service.model;
import me.lucko.luckperms.api.context.ImmutableContextSet;
import org.spongepowered.api.service.permission.Subject;
import javax.annotation.Nonnull;
@ -37,4 +39,6 @@ public interface ProxiedSubject {
@Nonnull
LPSubjectReference asSubjectReference();
ImmutableContextSet getActiveContextSet();
}