Skip to content

Commit

Permalink
Offline permission check events (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
DrexHD committed Oct 10, 2022
1 parent d3c86f0 commit e8eb33e
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 1 deletion.
28 changes: 28 additions & 0 deletions USAGE.md
Expand Up @@ -70,6 +70,25 @@ if (Permissions.check(source, "mymod.permission", true)) {
}
```

#### Checking permissions for a (potentially) offline player
Permission checks for offline players can be made using the players unique id (UUID). The result is returned as a [CompletableFuture](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/CompletableFuture.html).
```java
UUID uuid = ...;
Permissions.check(uuid, "mymod.permission").thenAcceptAsync(result -> {
if (result) {
// Woo!
}
});
```

To simplify checks **not** made on the server thread, you can use `join()`.
```java
UUID uuid = ...;
if (Permissions.check(uuid, "mymod.permission").join()) {
// Woo
};
```

## Usage (getting options)

All the methods you need to get option values are in the `Options` class.
Expand Down Expand Up @@ -119,6 +138,15 @@ PermissionCheckEvent.EVENT.register((source, permission) -> {
});
```

```java
OfflinePermissionCheckEvent.EVENT.register((uuid, permission) -> {
if (isSuperAdmin(uuid)) {
return CompletableFuture.completedFuture(TriState.TRUE);
}
return CompletableFuture.completedFuture(TriState.DEFAULT);
});
```

## Usage (providing options)

Just register a listener for the `OptionRequestEvent`.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
@@ -1,5 +1,5 @@
plugins {
id 'fabric-loom' version '0.12-SNAPSHOT'
id 'fabric-loom' version '1.0-SNAPSHOT'
id 'maven-publish'
}

Expand Down
@@ -0,0 +1,56 @@
/*
* This file is part of fabric-permissions-api, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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 me.lucko.fabric.api.permissions.v0;

import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.util.TriState;
import org.jetbrains.annotations.NotNull;

import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
* Simple permissions check event for (potentially) offline players.
*/
public interface OfflinePermissionCheckEvent {

Event<OfflinePermissionCheckEvent> EVENT = EventFactory.createArrayBacked(OfflinePermissionCheckEvent.class, (callbacks) -> (uuid, permission) -> {
CompletableFuture<TriState> res = CompletableFuture.completedFuture(TriState.DEFAULT);
for (OfflinePermissionCheckEvent callback : callbacks) {
res = res.thenCompose(triState -> {
if (triState != TriState.DEFAULT) {
return CompletableFuture.completedFuture(triState);
}
return callback.onPermissionCheck(uuid, permission);
});
}
return res;
});

@NotNull CompletableFuture<TriState> onPermissionCheck(@NotNull UUID uuid, @NotNull String permission);

}
87 changes: 87 additions & 0 deletions src/main/java/me/lucko/fabric/api/permissions/v0/Permissions.java
Expand Up @@ -25,14 +25,19 @@

package me.lucko.fabric.api.permissions.v0;

import com.mojang.authlib.GameProfile;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.command.CommandSource;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;

import org.jetbrains.annotations.NotNull;

import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;

/**
Expand Down Expand Up @@ -183,4 +188,86 @@ static boolean check(@NotNull Entity entity, @NotNull String permission) {
return check(entity.getCommandSource(), permission);
}

/**
* Gets the {@link TriState state} of a {@code permission} for the given (potentially) offline player.
*
* @param uuid the player uuid
* @param permission the permission
* @return the state of the permission
*/
static @NotNull CompletableFuture<TriState> getPermissionValue(@NotNull UUID uuid, @NotNull String permission) {
Objects.requireNonNull(uuid, "uuid");
Objects.requireNonNull(permission, "permission");
return OfflinePermissionCheckEvent.EVENT.invoker().onPermissionCheck(uuid, permission);
}

/**
* Performs a permission check, falling back to the {@code defaultValue} if the resultant
* state is {@link TriState#DEFAULT}.
*
* @param uuid the player to perform the check for
* @param permission the permission to check
* @param defaultValue the default value to use if nothing has been set
* @return the result of the permission check
*/
static CompletableFuture<Boolean> check(@NotNull UUID uuid, @NotNull String permission, boolean defaultValue) {
return getPermissionValue(uuid, permission).thenApplyAsync(state -> state.orElse(defaultValue));
}

/**
* Performs a permission check, falling back to {@code false} if the resultant state
* is {@link TriState#DEFAULT}.
*
* @param uuid the source to perform the check for
* @param permission the permission to check
* @return the result of the permission check
*/
static CompletableFuture<Boolean> check(@NotNull UUID uuid, @NotNull String permission) {
return getPermissionValue(uuid, permission).thenApplyAsync(state -> state.orElse(false));
}

/**
* Performs a permission check, falling back to {@code false} if the resultant state
* is {@link TriState#DEFAULT}.
*
* @param profile the player profile to perform the check for
* @param permission the permission to check
* @param defaultValue the default value to use if nothing has been set
* @return the result of the permission check
*/
static CompletableFuture<Boolean> check(@NotNull GameProfile profile, @NotNull String permission, boolean defaultValue) {
Objects.requireNonNull(profile, "profile");
return check(profile.getId(), permission, defaultValue);
}

/**
* Performs a permission check, falling back to {@code false} if the resultant state
* is {@link TriState#DEFAULT}.
*
* @param profile the player profile to perform the check for
* @param permission the permission to check
* @return the result of the permission check
*/
static CompletableFuture<Boolean> check(@NotNull GameProfile profile, @NotNull String permission) {
Objects.requireNonNull(profile, "profile");
return check(profile.getId(), permission);
}

/**
* Performs a permission check, falling back to requiring the {@code defaultRequiredLevel}
* if the resultant state is {@link TriState#DEFAULT}.
*
* @param profile the player profile to perform the check for
* @param permission the permission to check
* @param defaultRequiredLevel the required permission level to check for as a fallback
* @param server instance to check permission level
* @return the result of the permission check
*/
static CompletableFuture<Boolean> check(@NotNull GameProfile profile, @NotNull String permission, int defaultRequiredLevel, @NotNull MinecraftServer server) {
Objects.requireNonNull(profile, "profile");
Objects.requireNonNull(server, "server");
BooleanSupplier permissionLevelCheck = () -> server.getPermissionLevel(profile) >= defaultRequiredLevel;
return getPermissionValue(profile.getId(), permission).thenApplyAsync(state -> state.orElseGet(permissionLevelCheck));
}

}

0 comments on commit e8eb33e

Please sign in to comment.