Skip to content

GH-429 Support permissions "OR" behavior #512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
import dev.rollczi.litecommands.annotations.AnnotationProcessor;
import dev.rollczi.litecommands.meta.Meta;

import dev.rollczi.litecommands.permission.PermissionSet;

public class PermissionAnnotationResolver<SENDER> implements AnnotationProcessor<SENDER> {

@Override
public AnnotationInvoker<SENDER> process(AnnotationInvoker<SENDER> invoker) {
return invoker.on(Permission.class, (annotation, metaHolder) -> {
metaHolder.meta().listEditor(Meta.PERMISSIONS)
.addAll(annotation.value())
.add(new PermissionSet(annotation.value()))
.apply();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import dev.rollczi.litecommands.annotations.AnnotationProcessor;
import dev.rollczi.litecommands.meta.Meta;

import dev.rollczi.litecommands.permission.PermissionSet;

public class PermissionsAnnotationResolver<SENDER> implements AnnotationProcessor<SENDER> {

@Override
Expand All @@ -13,7 +15,7 @@ public AnnotationInvoker<SENDER> process(AnnotationInvoker<SENDER> invoker) {

for (Permission permissionAnnotation : annotation.value()) {
meta.listEditor(Meta.PERMISSIONS)
.addAll(permissionAnnotation.value())
.add(new PermissionSet(permissionAnnotation.value()))
.apply();
}
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package dev.rollczi.litecommands.annotations.permission;

import dev.rollczi.litecommands.annotations.command.Command;
import dev.rollczi.litecommands.annotations.execute.Execute;
import dev.rollczi.litecommands.permission.MissingPermissions;
import dev.rollczi.litecommands.unit.TestPlatformSender;
import dev.rollczi.litecommands.unit.annotations.LiteTestSpec;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

class ParallelPermissionAnnotationTest extends LiteTestSpec {

@Command(name = "test")
@Permission("test.base")
@Permission("test.admin")
static class TestCommand {

@Execute
@Permission("test.permission.execute")
@Permission("test.admin")
void execute() {
}

@Execute(name = "cmd")
@Permission("test.cmd")
void cmd() {
}
}

@Test
void test() {
MissingPermissions permissions = platform.execute("test")
.assertFailedAs(MissingPermissions.class);

List<String> missing = permissions.getPermissions();

assertThat(missing)
.containsExactly("test.permission.execute", "test.admin", "test.base");
}

@Test
void testPermitted() {
platform.execute(TestPlatformSender.permitted("test.admin"), "test")
.assertSuccess();

platform.execute(TestPlatformSender.permitted("test.base", "test.admin"), "test")
.assertSuccess();
platform.execute(TestPlatformSender.permitted("test.base", "test.permission.execute"), "test")
.assertSuccess();

platform.execute(TestPlatformSender.permitted("test.base", "test.cmd"), "test cmd")
.assertSuccess();
platform.execute(TestPlatformSender.permitted("test.admin", "test.cmd"), "test cmd")
.assertSuccess();

assertThat(platform.execute(TestPlatformSender.permitted("test.permission.execute"), "test")
.assertFailedAs(MissingPermissions.class).getPermissions()
).containsExactlyInAnyOrder("test.base", "test.admin");

assertThat(platform.execute(TestPlatformSender.permitted("test.base"), "test")
.assertFailedAs(MissingPermissions.class).getPermissions()
).containsExactlyInAnyOrder("test.permission.execute", "test.admin");

assertThat(platform.execute("test cmd")
.assertFailedAs(MissingPermissions.class).getPermissions()
).containsExactlyInAnyOrder("test.base", "test.admin", "test.cmd");

assertThat(platform.execute(TestPlatformSender.permitted("test.base"), "test cmd")
.assertFailedAs(MissingPermissions.class).getPermissions()
).containsExactlyInAnyOrder("test.cmd");
assertThat(platform.execute(TestPlatformSender.permitted("test.admin"), "test cmd")
.assertFailedAs(MissingPermissions.class).getPermissions()
).containsExactlyInAnyOrder("test.cmd");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import dev.rollczi.litecommands.bind.BindRegistry;
import dev.rollczi.litecommands.context.ContextRegistry;
import dev.rollczi.litecommands.cooldown.CooldownService;
import dev.rollczi.litecommands.event.EventPublisher;
import dev.rollczi.litecommands.handler.result.ResultHandleService;
import dev.rollczi.litecommands.command.builder.CommandBuilderCollector;
import dev.rollczi.litecommands.editor.EditorService;
Expand All @@ -29,6 +30,9 @@ public interface LiteCommandsInternal<SENDER, C extends PlatformSettings> {
@ApiStatus.Internal
Scheduler getScheduler();

@ApiStatus.Internal
EventPublisher getEventPublisher();

@ApiStatus.Internal
SchematicGenerator<SENDER> getSchematicGenerator();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.rollczi.litecommands.argument.ArgumentKey;
import dev.rollczi.litecommands.cooldown.CooldownContext;
import dev.rollczi.litecommands.permission.PermissionSet;
import dev.rollczi.litecommands.priority.PriorityLevel;
import dev.rollczi.litecommands.scheduler.SchedulerPoll;
import dev.rollczi.litecommands.strict.StrictMode;
Expand All @@ -21,7 +22,7 @@
public interface Meta {

MetaKey<List<String>> DESCRIPTION = MetaKey.of("description", MetaType.list(), Collections.emptyList());
MetaKey<List<String>> PERMISSIONS = MetaKey.of("permissions", MetaType.list(), Collections.emptyList());
MetaKey<List<PermissionSet>> PERMISSIONS = MetaKey.of("permissions", MetaType.list(), Collections.emptyList());
MetaKey<PriorityLevel> PRIORITY = MetaKey.of("priority", PriorityLevel.class, PriorityLevel.NORMAL);
MetaKey<Boolean> NATIVE_PERMISSIONS = MetaKey.of("native-permissions", Boolean.class, false);
MetaKey<SchedulerPoll> POLL_TYPE = MetaKey.of("poll-type", SchedulerPoll.class, SchedulerPoll.MAIN);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,77 +1,62 @@
package dev.rollczi.litecommands.permission;

import dev.rollczi.litecommands.meta.Meta;
import dev.rollczi.litecommands.meta.MetaHolder;
import dev.rollczi.litecommands.platform.PlatformSender;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.jetbrains.annotations.ApiStatus;

public class MissingPermissions {
public class MissingPermissions implements PermissionValidationResult {

private final List<String> checkedPermissions;
private final List<String> missingPermissions;
private final List<Verdict> verdicts;

private MissingPermissions(List<String> checkedPermissions, List<String> missingPermissions) {
this.checkedPermissions = checkedPermissions;
this.missingPermissions = missingPermissions;
MissingPermissions(List<Verdict> verdicts) {
this.verdicts = verdicts;
}

@Override
@ApiStatus.Experimental
public List<Verdict> getVerdicts() {
return Collections.unmodifiableList(verdicts);
}

public List<String> getChecked() {
return Collections.unmodifiableList(checkedPermissions);
return verdicts.stream()
.flatMap(result -> result.getChecks().stream())
.flatMap(check -> check.getCheckedPermissions().stream())
.distinct()
.collect(Collectors.toList());
}

public List<String> getPermissions() {
return Collections.unmodifiableList(missingPermissions);
return verdicts.stream()
.flatMap(result -> result.getChecks().stream())
.flatMap(check -> check.getMissingPermissions().stream())
.distinct()
.collect(Collectors.toList());
}

public String asJoinedText() {
return asJoinedText(", ");
}

public String asJoinedText(String separator) {
return String.join(separator, missingPermissions);
return String.join(separator, getPermissions());
}

public boolean isMissing() {
return !missingPermissions.isEmpty();
}

public boolean isPermitted() {
return missingPermissions.isEmpty();
return !isPermitted();
}

public static MissingPermissions check(PlatformSender platformSender, MetaHolder metaHolder) {
List<String> collected = new ArrayList<>();
List<String> missingPermissions = new ArrayList<>();
MetaHolder current = metaHolder;

while (current != null) {
if (!current.meta().has(Meta.PERMISSIONS)) {
current = current.parentMeta();
continue;
}

List<String> permissions = current.meta().get(Meta.PERMISSIONS);

for (String permission : permissions) {
collected.add(permission);

if (!platformSender.hasPermission(permission)) {
missingPermissions.add(permission);
}
}

current = current.parentMeta();
}

return new MissingPermissions(collected, missingPermissions);
return new MissingPermissions(PermissionValidationServiceImpl.check(platformSender, metaHolder));
}

public static MissingPermissions missing(String... permissions) {
return new MissingPermissions(Arrays.asList(permissions), Arrays.asList(permissions));
return new MissingPermissions(Collections.singletonList(new Verdict(MetaHolder.empty(), Collections.singletonList(new Check(Arrays.asList(permissions), Arrays.asList(permissions))))));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.rollczi.litecommands.permission;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

public class PermissionSet {

private final Set<String> permissions;

public PermissionSet(Collection<String> permissions) {
this.permissions = Collections.unmodifiableSet(new LinkedHashSet<>(permissions));
}

public PermissionSet(String... permissions) {
this.permissions = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(permissions)));
}

public Set<String> getPermissions() {
return permissions;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package dev.rollczi.litecommands.permission;

import dev.rollczi.litecommands.meta.MetaHolder;
import dev.rollczi.litecommands.shared.Preconditions;
import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;

/**
* Represents the result of the permission validation.
*/
public interface PermissionValidationResult {

@ApiStatus.Experimental
List<Verdict> getVerdicts();

default boolean isPermitted() {
for (Verdict verdict : getVerdicts()) {
if (!verdict.isPermitted()) {
return false;
}
}

return true;
}

/**
* Represents the cause of the missing permissions.
*/
@ApiStatus.Experimental
final class Verdict {

private final MetaHolder owner;
private final List<Check> checks;

Verdict(MetaHolder owner, List<Check> checks) {
Preconditions.notNull(owner, "owner");
this.owner = owner;
this.checks = checks;
}

public MetaHolder getOwner() {
return owner;
}

public List<Check> getChecks() {
return checks;
}

public boolean isPermitted() {
if (checks.isEmpty()) {
return true;
}

for (Check check : checks) {
if (check.missingPermissions.isEmpty()) {
return true;
}
}

return false;
}

public static Verdict permitted(MetaHolder current) {
return new Verdict(current, Collections.emptyList());
}

}

@ApiStatus.Experimental
class Check {

private final List<String> checkedPermissions;
private final List<String> missingPermissions;

Check(List<String> checkedPermissions, List<String> missingPermissions) {
this.checkedPermissions = checkedPermissions;
this.missingPermissions = missingPermissions;
}

public List<String> getCheckedPermissions() {
return checkedPermissions;
}

public List<String> getMissingPermissions() {
return missingPermissions;
}

}

}
Loading
Loading