권한
경고
These docs were written for SpongeAPI 7 and are likely out of date. If you feel like you can help update them, please submit a PR!
바닐라 서버 명령어에 대한 펄미션을 살펴보고 싶으시다면 이 페이지 페이지 혹은 해당 플러그인에 대한 펄미션 문서 또는 설명을 참고하세요.
권한
펄미션은 대소문자를 구분하는 문자열 키로 Subject가 특정한 행동을 할 수 있는지 없는지를 결정하는데에 쓰입니다. 그 문자열은 온점(.) 으로 구분되고 펄미션의 구조는 이러해야합니다.
<PluginID>.<MainGroup>.<Subgroup1>...
허용되는 문자:
“A” - “Z”
“a” - “z”
“0” - “9”
“_”
“-”
“.”
상속
한 사용자가 ``myplugin.commands``라는 권한을 가지고 있다면 자동적으로 ``myplugin.commands.teleport``와 같은 하위 종속 권한도 명시적으로 그 사용자로부터 제거하지 않은 한 갖게 됩니다.
구조 예시
다음의 예시들은 권한을 구조화하는 것을 보여줄 것입니다. 하지만, 모든 상황에 이를 적용해야 할 필요는 없습니다.
myplugin
플러그인의 **모든 권한**을 부여함
myplugin.commands
플러그인 명령어들에 관한 **모든 권한**을 부여함
myplugin.commands.teleport.execute
해당 명령어를 수행하는 권한만 부여함 - 다른 `teleport`절의 권한이 부여되어 있어도 수행할 수는 없음 (반면 이 권한만 있을 때에는 수행자가 같은 월드의 다른 플레이어들에게 순간이동할 수 있음)
myplugin.commands.teleport.all
한 번에 모든 플레이어를 순간이동할 수 있는 권한만을 부여함
myplugin.commands.teleport.worlds
Only grants the user the permission to teleport to all worlds.
myplugin.commands.teleport.worlds.mynetherworld
Only grants the user the permission to teleport to mynetherworld.
PermissionDescription
The PermissionDescription is an utility that is meant to provide the server owner with details on a certain
permission. PermissionDescription
s are an optional feature a PermissionService can provide. The creation
of a PermissionDescription does not have any impact on whether a permission exists, who has access or what its
default value is.
The description consists of:
the target permission id
a Text description
one or more assigned roles
the owning plugin
If you have a dynamic element such as a World
or ItemType
then you can use <TemplateParts>
.
Usage-Example
import net.kyori.adventure.text.Component;
import org.spongepowered.api.service.permission.PermissionDescription;
import org.spongepowered.api.service.permission.PermissionDescription.Builder;
import org.spongepowered.api.service.permission.PermissionService;
PluginContainer plugin = ...;
Builder builder = permissionService.newDescriptionBuilder(plugin);
builder.id("myplugin.commands.teleport.execute")
.description(Component.text("Allows the user to execute the teleport command."))
.assign(PermissionDescription.ROLE_STAFF, true)
.register();
Simple-Result
myplugin.commands.teleport.execute
Description: Allows the user to execute the teleport command.
Role: user
Owner: MyPlugin v1.2.3
Template-Result
myplugin.commands.teleport.worlds.<World>
Description: Allows the user to teleport to the world <World>.
Role: staff
Owner: MyPlugin v1.2.3
팁
You might skip writing descriptions for some parent permission groups such as myplugin.commands.teleport.worlds
or myplugin.commands
as their meaning can be derived from the permission structure and the defined children
alone.
Subject
A Subject is a holder of assigned permissions. It can be obtained from the PermissionService
via
SubjectCollections.
CommandSources such as Players are Subject
s by default, but there are many other types of
Subject
s. Anything that has permissions is a Subject even if it just delegates the checks to a contained Subject.
Permissions can be granted or denied to a Subject. If a permission is neither granted nor denied its setting will be
inherited. See Inheritance.
Subjects provide methods to check whether they have a certain permission or not.
Plugins that use this method should only query for the specific permission they want to check. It is the
PermissionService’s task to respect the permission and subject inheritance.
Example
The following example could be used to check whether the Player is allowed to execute the teleport command.
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.world.World;
public boolean canTeleport(Player subject, World targetWorld) {
return subject.hasPermission("myplugin.command.teleport.execute")
&& (subject.getWorld() == targetWorld
|| subject.hasPermission("myplugin.command.teleport." + targetWorld.getName()));
}
상속
If a Subject
has a permission assigned, it will use that value.
Otherwise it will be inherited from any parent Subject
. It does not matter what kind of parent (e.g. group or
player) Subject
that might be.
If neither the subject itself nor any parent subjects grant or deny a permission then it will be inherited from the
default Subject
s. Each SubjectCollection
defines its own defaults. The global and weakest default subject can be
obtained from the PermissionService
. Plugins may define their own permissions to the default’s transient
SubjectData during every server start-up. This allows server owners to overwrite the defaults defined by
plugins according to their needs using the default’s persistent SubjectData
. If you would like to provide a
configuration guideline for server owners use PermissionDescription
's role-templates instead.
경고
You should think carefully before granting default permissions to users. By granting the permissions you are assuming that all server owners will want these defaults (at least the first time the plugin runs) and that exceptions will require server owners to explicitly deny the permissions (which can’t even be done without a custom permissions service implementation). This should roughly correspond to a guest on a single player lan world without cheats. For example, a chat plugin would allow sending chat messages by default to imitate vanilla game behavior for features that were changed by the plugin.
참고
The default Subject
s’ persistent SubjectData
s take precedence over the transient ones.
For all other Subject
s the transient SubjectData
s take precedence over the persistent ones.
If neither the Subject, nor any of its parents, nor the defaults assign a value to a permission, then it is automatically denied.
참고
Order of precedence in descending order:
- Subject itself
Transient
Persistent
- Parent Subjects
Transient
Persistent
- SubjectCollection Defaults
Persistent
Transient
- PermissionService Defaults
Persistent
Transient
Deny permission
SubjectCollections
A container for subjects that can be used to obtain a Subject by name. These are the default Subject Collections:
- User
Contains all on-line
Player
s and all off-lineUser
s (at least those with none-default settings).
- Group
Contains all group
Subject
. Groups are a simple way of structuring aSubject
's inheritance tree using namedSubject
s. Groups should be used if a specific subset ofSubject
s have additional permission settings such as a team, faction or role.
- System
Contains other
Subject
s used by the server such as the console and possible remote consoles.
- Command Block
Contains all
Subject
s for command blocks. These are useful if you would like to run aCommandBlock
only with the permissions of the creator.
- Role Template
Contains all role template subjects that are used in
PermissionDescription
s. Useful to lookup all recommended permissions for a user. These should not be used for inheritance.
참고
When SubjectCollection
s are queried for a Subject
they will automatically be created, if they do not already
exist. However they might not necessarily show up in getAllSubjects()
unless none-default values are set.
SubjectData
SubjectData are the actual permission stores connected to the Subject. There are two types of Subject stores:
Transient = Only lasts for the duration of the session, it is never saved
Regular (persistent) = Might be saved somewhere, and therefore be persisted and exist forever. Its recommended for
PermissionService
s to implement a persistent store, however it is not a requirement. It might also depend on the subject type. If there is no persistence then the transient store will be returned in both methods.
Plugin authors should consider whether it is necessary to persist a value when choosing between them.
If it is only for a short time (e.g. during a minigame) then use the transient one.
If it is for a long time or forever (e.g. a promotion to VIP) use the regular (persistent) one.
Please refer to the Inheritance section if want to know more about the inheritance and precedence of the transient
and persistent SubjectData
s.
Example
The following example could be used to give a player a permission with global context and a true value
import org.spongepowered.api.entity.living.player.Player;
public void setPermission(Player player, String permission) {
if(!player.hasPermission(permission)
player.getSubjectData().setPermission(SubjectData.GLOBAL_CONTEXT, permission, Tristate.TRUE);
}
참고
In the above example Tristate.TRUE
was used but you can also use Tristate.FALSE
for a false permission and
Tristate.UNDEFINED
to unset the permission entirely.
Subject Options
Subjects also provide the possibility to store string options. These are basically key value pairs that can be
assigned and inherited. Unlike the permission strings the keys are not hierarchical and don’t provide any inheritance
mechanisms themselves, but the key value pairs itself are inherited from parent Subject
s in the same way permissions
are.
Contexts
If you consider each permission to a privilege or ability to be able to do something, a Context is the circumstances where that privilege is usable.
You might want to give a Subject
permission to do something, but only when the Subject
is in a certain world,
or in a certain region.
Contexts are accumulated by a Subject
, and are then used by the PermissionService
to decide if the Subject
has a privilege or not.
Sponge provides some contexts by default, but it is generally down to other plugins to provide additional contexts to the PermissionService, through a ContextCalculator.
When creating contexts for your own plugin please try to avoid conflicts with other plugins (e.g. by prefixing the context key with your plugin id) unless these contexts are meant to be shared.
참고
Please make sure that your ContextCalculator
responds as fast as possible as it will get called frequently.
경고
ContextCalculator
implementations must be thread safe, because they may be called from outside of the main
thread, or even called in parallel. For to this reason, all non-name or non-uuid based ContextCalculator
s
(such as location-based ones) have to utilize a cache and must be to be updated using event listeners or
synchronized schedulers.
Example
Your ContextCalculator
may look like this:
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.service.context.Context;
import org.spongepowered.api.service.context.ContextCalculator;
import org.spongepowered.api.service.permission.Subject;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class ExampleCalculator implements ContextCalculator<Subject> {
private static final Context IN_ANY_ARENA = new Context("myarenaplugin-inAnyArena", "true");
private static final Context NOT_ANY_ARENA = new Context("myarenaplugin-inAnyArena", "false");
private static final String ARENA_KEY = "myarenaplugin-arena";
private final Map<UUID, String> playerArenas = new ConcurrentHashMap<>();
@Override
public void accumulateContexts(Subject subject, Set<Context> accumulator) {
CommandSource commandSource = subject.getCommandSource().orElse(null);
if (commandSource instanceof Player) {
UUID uuid = ((Player) commandSource).getUniqueId();
String arena = this.playerArenas.get(uuid);
if (arena != null) {
accumulator.add(IN_ANY_ARENA);
accumulator.add(new Context(ARENA_KEY, arena));
} else {
accumulator.add(NOT_ANY_ARENA);
}
}
}
@Override
public boolean matches(Context context, Subject subject) {
CommandSource commandSource = subject.getCommandSource().orElse(null);
if (commandSource instanceof Player) {
UUID uuid = ((Player) commandSource).getUniqueId();
if (context.equals(IN_ANY_ARENA)) {
return this.playerArenas.containsKey(uuid);
} else if (context.equals(NOT_ANY_ARENA)) {
return !this.playerArenas.containsKey(uuid);
} else if (context.getKey().equals(ARENA_KEY)) {
return context.getValue().equals(this.playerArenas.get(uuid));
}
}
return false;
}
}
The ContextCalculator
can be registered via:
permissionService.registerContextCalculator(contextCalculator);
For Forge Mods
If you are the author of a Forge mod and are not using the new Forge PermissionsAPI but are doing OP checks, then you are already on the right path for Sponge to pick up permissions.
The simplest way to create a Sponge permission in a Forge mod without soft-depending on SpongeAPI is to use the method provided by
Vanilla Minecraft code in ICommandSender
, namely ICommandSender.canCommandSenderUseCommand(int permLevel, String commandName)
.
The String passed into that method has no use at all in a Vanilla Forge environment, but when SpongeForge is added it automatically
takes that String and converts it into a working permission.
Example
public class AwesomeBlock extends Block {
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state,
EntityPlayer player, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) {
if (player.canUseCommand(4, "examplemod.awesomeblock.interact")) {
// Do cool stuff
return true;
}
return false;
}
}
As you can see, we simply check for the OP level and pass in an arbitrary String we want to use as a permission when Sponge is used.
When Forge is used by itself the player simply requires the OP level, so passing a value of 0 would allow all users to interact with
the block, but when SpongeForge is added they require the permission node of examplemod.awesomeblock.interact
.
It is recommended to follow the permission structure as described above. The permission inheritance does also apply to these checks.
참고
The SRG name for this method is func_70003_b
.