Skip to content

feat: bridge permissions into the platforms' native command gates#11

Merged
hbrombeer merged 1 commit into
mainfrom
feat/native-permission-bridge
Jul 14, 2026
Merged

feat: bridge permissions into the platforms' native command gates#11
hbrombeer merged 1 commit into
mainfrom
feat/native-permission-bridge

Conversation

@hbrombeer

Copy link
Copy Markdown
Member

The bug

The permission snapshot was loaded — and then consulted by nobody except this plugin's own /permissions commands.

On Velocity there is no PermissionsSetupEvent subscriber at all, so no PermissionProvider is ever installed. source.hasPermission(...) — Velocity's native API, which every other plugin gates on — falls through to Velocity's default provider, which denies everything for non-console senders.

plugin-agones, AgonesCommand.kt:59:

return source is ConsoleCommandSource || source.hasPermission(PERMISSION)

So a player holding * cannot even see /agones — Velocity hides commands whose hasPermission returns false from tab-completion. plugin-chat (StaffChat, ProxyCommand), plugin-proxy and plugin-social gate the same way. In effect every permission check on the network was dead, and everything fell back to console-only.

Velocity

Installs a SnapshotPermissionProvider via PermissionsSetupEvent, wrapping the provider that was there before as a fallback:

  • non-Player subjects (the console) fall through unchanged, so the console keeps all its permissions
  • a permission the snapshot does not ALLOW also falls through, rather than being modeled as a hard DENY — Velocity's default answers UNDEFINED for a player, so the check is false either way
  • if the console's setup event fires before ProxyInitializeEvent, the subscriber is a no-op and the console keeps the default provider — no NPE, no lockout

Minestom

Minestom has no permission API — verified against the pinned jar (2026.06.20-26.1.2): zero permission classes, CommandSender has no hasPermission, only Player.getPermissionLevel(): Int (the vanilla op tier). There is nothing to hook.

Its one native gate is CommandCondition.canUse, so this ships a single factory:

myCommand.condition = permissions.commandCondition("grounds.lobby.command.foo")

Permissions is already registered in the runtime ServiceRegistry, so no runtime change is needed.

Verification

./gradlew build green. 6 new tests: allow → TRUE, no-grant → falls through to fallback, console → fallback unchanged (Velocity); allows/denies a player, allows console (Minestom).

The snapshot was loaded and then consulted by nobody but this plugin's own
/permissions commands. On Velocity there was no PermissionsSetupEvent
subscriber at all, so source.hasPermission() -- the API every other plugin
gates on -- fell through to Velocity's default provider and denied
everything for non-console senders. A player holding "*" could not even see
/agones in tab-completion, and plugin-chat, plugin-proxy and plugin-social
were gated the same way. Every permission check on the network was dead.

Velocity: install a PermissionProvider backed by the snapshot, wrapping the
previous provider as a fallback so the console keeps its own. A permission
the snapshot does not ALLOW falls through to that fallback rather than being
modeled as a hard DENY -- Velocity's default answers UNDEFINED for a player,
so the check is false either way.

Minestom has no permission API whatsoever (no hasPermission on CommandSender,
only the vanilla op-level int), so there is nothing to hook. Its native gate
is CommandCondition, so ship one factory -- Permissions.commandCondition(node)
-- that a gamemode attaches to a command. Permissions is already in the
runtime ServiceRegistry, so no runtime change is needed.
@hbrombeer hbrombeer merged commit 64429a1 into main Jul 14, 2026
2 checks passed
@hbrombeer hbrombeer deleted the feat/native-permission-bridge branch July 14, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant