org.spongepowered.api.service.permission.Subject Maven / Gradle / Ivy
Show all versions of spongeapi Show documentation
/*
* This file is part of SpongeAPI, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered
* Copyright (c) contributors
*
* 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 org.spongepowered.api.service.permission;
import org.spongepowered.api.event.Cause;
import org.spongepowered.api.event.EventContext;
import org.spongepowered.api.event.EventContextKeys;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.context.ContextCalculator;
import org.spongepowered.api.service.context.Contextual;
import org.spongepowered.api.util.Tristate;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* An object which can hold permission data.
*
* Subjects are objects which hold permission data, in the form of
* {@link SubjectData}. They can also be the source of permission requests.
*
* The most common forms of Subject are "users" and "groups", although these
* are not the only forms. Anything can hold permission data, and therefore be a
* subject.
*
* Permission strings
*
* Authorization checks are made using "permission strings."
*
* Permission strings are hierarchical with each level separated by periods
* (full stops). An example of a valid permission string is {@code
* example.function}. Inheritance is implicit; that is, if a subject has been
* granted {@code example}, then the subject should have also be automatically
* granted {@code example.function}, {@code example.another}, {@code
* example.deeper.nesting}, and so on. However, implementations may allow
* administrators to configure "negation" such that {@code example} and all
* child levels would granted but {@code example.access} would denied (for
* example).
*
* It is the responsibility of the {@link PermissionService} implementation
* to provide this behavior, and resolve the implicit permission node
* inheritance explained above when a Subject is queried for permissions. Use
* of a {@link NodeTree} is recommended.
*
* Plugins may opt to implement "dynamic" permissions such as {@code
* example.region.define.} where {@code region} would depend on
* the context of the check. Attention should be made towards the handling of
* periods / full stops in such cases.
*
* Due to the implicit inheritance, it is recommended that commands that
* allow a user to "apply" an effect to other users use
* {@code example.function.self} as the permission for applying this effect
* to one's self. This allows administrators to grant
* {@code example.function.self} to permit usage on one's self and grant
* {@code example.function} to grant usage on other users.
*
* Inheritance
*
* All methods are expected to account for data inherited from parent
* subjects. For a representation of the data that the subject explicitly holds,
* obtain the {@link SubjectData} for the subject.
*
* Additionally, all methods are expected to account for the defaults
* defined in the {@link SubjectCollection} containing this subject, as well
* as the defaults set globally in {@link PermissionService#defaults()}.
*
* Contexts
*
* Permission state is calculated using small pieces of information known as
* contexts. These are derived from a provided {@link Cause} using
* {@link ContextCalculator}s. Subjects have a
* {@linkplain #contextCause() default cause} for context calculation, which is
* based on the subject's active state.
*
* The relevant active state for subject context may be different depending
* on which types of permissions are being checked. Mostly, these will fall into
* either global game state or the subject's own state. While
* the default implementation of {@link #contextCause()} provides the latter,
* some proxy subjects that act as a projection of a subject into a certain
* state (such as {@link org.spongepowered.api.command.CommandCause} may choose
* to default to the former. The relevant cause stack should be carefully
* considered when performing permissions checks.
*
* Use a {@link SubjectCollection} to access instances.
*
* @see PermissionService to manage Subjects.
*/
public interface Subject extends Contextual {
/**
* Returns the subject collection this subject is a member of.
*
* @return The appropriate collection
*/
SubjectCollection containingCollection();
/**
* Gets a SubjectReference representing this subject.
*
* @return A subject reference representing this subject
*/
SubjectReference asSubjectReference();
/**
* Get the game object that may be associated with this subject.
*
* This could be a player, system subject, or something else. The return
* value of this method should not be stored.
*
* @return a potential game object
*/
Optional> associatedObject();
@Override
default Cause contextCause() {
final /* @Nullable */ Object associated = this.associatedObject().orElse(null);
if (associated != null) {
return Cause.of(EventContext.builder().add(EventContextKeys.SUBJECT, this).build(), associated);
} else {
return Contextual.super.contextCause();
}
}
/**
* Returns if this Subject has persistent, non-transient data.
*
* If true, this subject should have two distinct stores of SubjectData,
* and the non-transient form should be saved between sessions.
*
* If false, this subject will have only one store of SubjectData, which
* will not be persisted between sessions.
*
* @return If this Subject has persistent, non-transient data.
*/
boolean isSubjectDataPersisted();
/**
* Returns the primary data backing for this Subject.
*
* If this Subject is not persisted, this data will not be saved
* between sessions.
*
* For subjects which are not persisted, the same store will be returned
* by {@link #transientSubjectData()}.
*
* @return The primary data backing for this Subject
*/
SubjectData subjectData();
/**
* Returns the transient data backing for this Subject.
*
* Transient data is guaranteed to only last for the duration of the
* subject's session, and is not persisted.
*
* For subjects which are not persisted, the same store will be returned
* by {@link #subjectData()}.
*
* @return The transient data backing for this Subject
*/
SubjectData transientSubjectData();
/**
* Test whether the subject is permitted to perform an action given as the
* given permission string.
*
* This must return the same value as {@link #hasPermission(String, Cause)}
* called with the phase tracker's current cause.
*
* @param permission The permission string
* @return True if permission is granted
*/
default boolean hasPermission(final String permission) {
return this.hasPermission(permission, this.contextCause());
}
/**
* Test whether the subject is permitted to perform an action corresponding
* to the given permission string.
*
*
This must return the same boolean equivalent as
* {@link #permissionValue(String, Cause)}.
*
* @param permission The permission string
* @param cause The cause stack to extract context information from
* @return True if permission is granted
*/
default boolean hasPermission(final String permission, final Cause cause) {
return this.permissionValue(permission, cause).asBoolean();
}
/**
* Test whether the subject is permitted to perform an action corresponding
* to the given permission string.
*
* This must return the same boolean equivalent as
* {@link #permissionValue(String, Cause)}.
*
* @param permission The permission string
* @param contexts The contexts to calculate permission status in
* @return True if permission is granted
*/
default boolean hasPermission(final String permission, final Set contexts) {
return this.permissionValue(permission, contexts).asBoolean();
}
/**
* Returns the calculated value set for a given permission.
*
* It is expected that this method will also account for values
* inherited from parent subjects, as well as permission nodes inherited
* implicitly from a more generic level.
*
* Additionally, the defaults defined the {@link SubjectCollection}
* that holds this subject, as well as defaults defined in
* {@link PermissionService#defaults()} should be considered for this
* lookup.
*
* This method is likely to be called frequently, so it is desirable
* that implementations cache the results to method calls.
*
* @param permission The permission to check
* @return The tristate result of the check
*/
default Tristate permissionValue(final String permission) {
return this.permissionValue(permission, this.contextCause());
}
/**
* Returns the calculated value set for a given permission.
*
* It is expected that this method will also account for values
* inherited from parent subjects, as well as permission nodes inherited
* implicitly from a more generic level.
*
* Additionally, the defaults defined the {@link SubjectCollection}
* that holds this subject, as well as defaults defined in
* {@link PermissionService#defaults()} should be considered for this
* lookup.
*
* This method is likely to be called frequently, so it is desirable
* that implementations cache the results to method calls.
*
* @param permission The permission to check
* @param cause The cause to gather context from.
* @return The tristate result of the check
*/
Tristate permissionValue(String permission, Cause cause);
/**
* Returns the calculated value set for a given permission.
*
* It is expected that this method will also account for values
* inherited from parent subjects, as well as permission nodes inherited
* implicitly from a more generic level.
*
* Additionally, the defaults defined the {@link SubjectCollection}
* that holds this subject, as well as defaults defined in
* {@link PermissionService#defaults()} should be considered for this
* lookup.
*
* This method is likely to be called frequently, so it is desirable
* that implementations cache the results to method calls.
*
* @param permission The permission to check
* @param contexts The contexts to query permission value in
* @return The tristate result of the check
*/
Tristate permissionValue(String permission, Set contexts);
/**
* Check if this subject is a child of the given parent in the subject's
* current context, traversing inheritance.
*
* This must return the same value as
* {@link #isChildOf(SubjectReference, Cause)} called with the phase
* tracker's current cause.
*
* @param parent The parent to check for inheritance
* @return Whether this is a child of the given parent
*/
default boolean isChildOf(final SubjectReference parent) {
return this.isChildOf(parent, this.contextCause());
}
/**
* Check if this subject is a child of the given parent in the given context
* combination, traversing inheritance.
*
*
It is expected that this method will also account for data from
* distant parents, inherited from direct parent subjects.
*
*
Additionally, the defaults defined the {@link SubjectCollection}
* that holds this subject, as well as defaults defined in
* {@link PermissionService#defaults()} should be considered for this
* lookup.
*
* @param parent The parent to check for inheritance
* @param cause The cause to gather context from.
* @return Whether this is a child of the given parent
*/
boolean isChildOf(SubjectReference parent, Cause cause);
/**
* Check if this subject is a child of the given parent in the given context
* combination, traversing inheritance.
*
* It is expected that this method will also account for data from
* distant parents, inherited from direct parent subjects.
*
*
Additionally, the defaults defined the {@link SubjectCollection}
* that holds this subject, as well as defaults defined in
* {@link PermissionService#defaults()} should be considered for this
* lookup.
*
* @param parent The parent to check for inheritance
* @param contexts The contexts to query inheritance in
* @return Whether this is a child of the given parent
*/
boolean isChildOf(SubjectReference parent, Set contexts);
/**
* Return all parents that this group has in its current context
* combination.
*
* This must include inherited values if the permissions
* service supports inheritance.
*
* It must also must return the same value as {@link #parents(Cause)}
*
* @return An immutable list of parents
*/
default List extends SubjectReference> parents() {
return this.parents(this.contextCause());
}
/**
* Return all parents that this group has in the given context combination.
*
*
This must include inherited values if the permissions
* service supports inheritance.
*
* @param cause The cause to gather context from.
* @return An immutable list of parents
*/
List extends SubjectReference> parents(Cause cause);
/**
* Return all parents that this group has in the given context combination.
*
* This must include inherited values if the permissions
* service supports inheritance.
*
* @param contexts The cause to gather context from.
* @return An immutable list of parents
*/
List extends SubjectReference> parents(Set contexts);
/**
* Gets the value of a given option in the subject's current context.
*
* This must return the same value as {@link #option(String, Cause)}
* called with the phase tracker's current cause.
*
* @param key The key to get an option by. Case-insensitive.
* @return The value of the option, if any is present
*/
default Optional option(final String key) {
return this.option(key, this.contextCause());
}
/**
* Gets the value of a given option in the given context.
*
* It is expected that this method will account for options
* inherited from parent subjects.
*
*
Additionally, the default options defined by the
* {@link SubjectCollection} that holds this subject, as well as defaults
* defined in {@link PermissionService#defaults()} should be considered
* for this lookup.
*
* @param key The key to get an option by. Case-insensitive.
* @param cause The cause to gather context from.
* @return The value of the option, if any is present
*/
Optional option(String key, Cause cause);
/**
* Gets the value of a given option in the given context.
*
* It is expected that this method will account for options
* inherited from parent subjects.
*
*
Additionally, the default options defined by the
* {@link SubjectCollection} that holds this subject, as well as defaults
* defined in {@link PermissionService#defaults()} should be considered
* for this lookup.
*
* @param key The key to get an option by. Case-insensitive.
* @param contexts The context set to use when calculating causes
* @return The value of the option, if any is present
*/
Optional option(String key, Set contexts);
}