Skip to content
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
6 changes: 5 additions & 1 deletion common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
plugins { id("gg.grounds.grpc-conventions") }
plugins {
id("gg.grounds.grpc-conventions")
id("java-test-fixtures")
}

dependencies {
implementation(platform("gg.grounds:grounds-dependencies:0.1.0"))
Expand All @@ -8,5 +11,6 @@ dependencies {
implementation("tools.jackson.module:jackson-module-kotlin:3.1.0")

testImplementation("org.junit.jupiter:junit-jupiter")
testFixturesImplementation(platform("gg.grounds:grounds-dependencies:0.1.0"))
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package gg.grounds.permissions

import com.google.protobuf.TextFormat
import gg.grounds.grpc.permissions.PlayerPermissionSnapshot
import java.time.Instant
import java.util.UUID

object ReleasedPermissionSnapshotFixture {
val playerId: UUID = UUID.fromString("00000000-0000-0000-0000-000000000321")

fun proto(): PlayerPermissionSnapshot {
val fixture =
requireNotNull(javaClass.getResourceAsStream(FIXTURE_PATH)) {
"Released permission snapshot fixture is missing (path=$FIXTURE_PATH)"
}
return fixture.bufferedReader().use { reader ->
PlayerPermissionSnapshot.newBuilder()
.also { TextFormat.getParser().merge(reader, it) }
.build()
}
}

fun expected(): PermissionSnapshot =
PermissionSnapshot(
playerId = playerId,
policyVersion = 73,
issuedAt = Instant.parse("2030-01-01T00:00:00Z"),
refreshAfter = Instant.parse("2030-01-01T00:05:00Z"),
expiresAt = Instant.parse("2030-01-01T01:00:00Z"),
allowPatterns =
listOf(
roleGrant(
pattern = "spawn.use",
origin = PermissionGrantOriginKind.DEFAULT_ROLE,
roleKey = "default",
),
roleGrant(
pattern = "build.use",
origin = PermissionGrantOriginKind.DIRECT_ROLE,
roleKey = "builder",
),
roleGrant(
pattern = "staff.chat",
origin = PermissionGrantOriginKind.GROUP_MAPPING,
roleKey = "staff",
mappingId = MAPPING_ID,
),
roleGrant(
pattern = "home.use",
origin = PermissionGrantOriginKind.GROUP_MAPPING,
roleKey = "member",
mappingId = MAPPING_ID,
inheritedPath = listOf("staff", "member"),
),
),
denyPatterns =
listOf(
PermissionGrant(
effect = PermissionEffect.DENY,
pattern = "operator.use",
scope = PermissionScope.server("survival-1"),
source = PermissionGrantSource.PLAYER,
expiresAt = Instant.parse("2030-01-01T01:00:00Z"),
origin =
PermissionGrantOrigin(
kind = PermissionGrantOriginKind.DIRECT_PERMISSION
),
)
),
roleKeys = setOf("default", "builder", "staff", "member"),
roleMetadata =
listOf(
RoleMetadata("default", "Default", "[D]", "gray", 100),
RoleMetadata("builder", "Builder", "[B]", "green", 75),
RoleMetadata("staff", "Staff", "[S]", "red", 50),
RoleMetadata("member", "Member", null, null, 25),
),
)

private fun roleGrant(
pattern: String,
origin: PermissionGrantOriginKind,
roleKey: String,
mappingId: String? = null,
inheritedPath: List<String> = emptyList(),
): PermissionGrant =
PermissionGrant(
effect = PermissionEffect.ALLOW,
pattern = pattern,
scope = PermissionScope.global(),
source = PermissionGrantSource.ROLE,
origin = PermissionGrantOrigin(origin, roleKey, mappingId, inheritedPath),
)

private const val FIXTURE_PATH =
"/fixtures/service-permissions-v0.7.0/player-permission-snapshot.textproto"
private const val MAPPING_ID = "00000000-0000-0000-0000-000000000322"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
player_id: "00000000-0000-0000-0000-000000000321"
policy_version: 73
issued_at { seconds: 1893456000 }
refresh_after { seconds: 1893456300 }
expires_at { seconds: 1893459600 }
allow_patterns {
effect: PERMISSION_EFFECT_ALLOW
pattern: "spawn.use"
scope { kind: PERMISSION_SCOPE_KIND_GLOBAL }
source: PERMISSION_GRANT_SOURCE_ROLE
origin {
kind: PERMISSION_GRANT_ORIGIN_KIND_DEFAULT_ROLE
role_key: "default"
}
}
allow_patterns {
effect: PERMISSION_EFFECT_ALLOW
pattern: "build.use"
scope { kind: PERMISSION_SCOPE_KIND_GLOBAL }
source: PERMISSION_GRANT_SOURCE_ROLE
origin {
kind: PERMISSION_GRANT_ORIGIN_KIND_DIRECT_ROLE
role_key: "builder"
}
}
allow_patterns {
effect: PERMISSION_EFFECT_ALLOW
pattern: "staff.chat"
scope { kind: PERMISSION_SCOPE_KIND_GLOBAL }
source: PERMISSION_GRANT_SOURCE_ROLE
origin {
kind: PERMISSION_GRANT_ORIGIN_KIND_GROUP_MAPPING
role_key: "staff"
mapping_id: "00000000-0000-0000-0000-000000000322"
}
}
allow_patterns {
effect: PERMISSION_EFFECT_ALLOW
pattern: "home.use"
scope { kind: PERMISSION_SCOPE_KIND_GLOBAL }
source: PERMISSION_GRANT_SOURCE_ROLE
origin {
kind: PERMISSION_GRANT_ORIGIN_KIND_GROUP_MAPPING
role_key: "member"
mapping_id: "00000000-0000-0000-0000-000000000322"
inherited_path: "staff"
inherited_path: "member"
}
}
deny_patterns {
effect: PERMISSION_EFFECT_DENY
pattern: "operator.use"
scope {
kind: PERMISSION_SCOPE_KIND_SERVER
value: "survival-1"
}
source: PERMISSION_GRANT_SOURCE_PLAYER
expires_at { seconds: 1893459600 }
origin { kind: PERMISSION_GRANT_ORIGIN_KIND_DIRECT_PERMISSION }
}
role_keys: "default"
role_keys: "builder"
role_keys: "staff"
role_keys: "member"
role_metadata {
key: "default"
name: "Default"
prefix: "[D]"
color: "gray"
sort_order: 100
}
role_metadata {
key: "builder"
name: "Builder"
prefix: "[B]"
color: "green"
sort_order: 75
}
role_metadata {
key: "staff"
name: "Staff"
prefix: "[S]"
color: "red"
sort_order: 50
}
role_metadata {
key: "member"
name: "Member"
sort_order: 25
}
2 changes: 2 additions & 0 deletions minestom/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,7 @@ dependencies {
implementation("org.slf4j:slf4j-api")

testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation(testFixtures(project(":common")))
testImplementation("io.grpc:grpc-inprocess:1.82.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class GrpcPermissionSnapshotClient private constructor(private val channel: Mana
return GrpcPermissionSnapshotClient(channel)
}

internal fun create(channel: ManagedChannel): GrpcPermissionSnapshotClient =
GrpcPermissionSnapshotClient(channel)

private const val DEFAULT_TIMEOUT_MS = 2000L
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
package gg.grounds.permissions.minestom

import com.google.protobuf.Timestamp
import gg.grounds.grpc.permissions.GetPlayerSnapshotRequest
import gg.grounds.grpc.permissions.PermissionEffect.PERMISSION_EFFECT_ALLOW
import gg.grounds.grpc.permissions.PermissionGrant
import gg.grounds.grpc.permissions.PermissionGrantOrigin
import gg.grounds.grpc.permissions.PermissionGrantOriginKind.PERMISSION_GRANT_ORIGIN_KIND_GROUP_MAPPING
import gg.grounds.grpc.permissions.PermissionGrantSource.PERMISSION_GRANT_SOURCE_ROLE
import gg.grounds.grpc.permissions.PermissionScope
import gg.grounds.grpc.permissions.PermissionScopeKind.PERMISSION_SCOPE_KIND_GLOBAL
import gg.grounds.grpc.permissions.PermissionSnapshotServiceGrpc
import gg.grounds.grpc.permissions.PlayerPermissionSnapshot
import gg.grounds.permissions.ReleasedPermissionSnapshotFixture
import io.grpc.Server
import io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder
import io.grpc.inprocess.InProcessChannelBuilder
import io.grpc.inprocess.InProcessServerBuilder
import io.grpc.stub.StreamObserver
import java.util.UUID
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertInstanceOf
Expand All @@ -29,27 +22,30 @@ class PermissionSnapshotClientTest {
}

@Test
fun `sends only player and server context and maps grant origin`() {
val playerId = UUID.randomUUID()
fun `consumes authoritative backend snapshot without caller groups`() {
val playerId = ReleasedPermissionSnapshotFixture.playerId
var capturedRequest: GetPlayerSnapshotRequest? = null
val serverName = InProcessServerBuilder.generateName()
server =
NettyServerBuilder.forPort(0)
InProcessServerBuilder.forName(serverName)
.directExecutor()
.addService(
object : PermissionSnapshotServiceGrpc.PermissionSnapshotServiceImplBase() {
override fun getPlayerSnapshot(
request: GetPlayerSnapshotRequest,
responseObserver: StreamObserver<PlayerPermissionSnapshot>,
) {
capturedRequest = request
responseObserver.onNext(snapshotResponse(playerId))
responseObserver.onNext(ReleasedPermissionSnapshotFixture.proto())
responseObserver.onCompleted()
}
}
)
.build()
.start()
val channel = InProcessChannelBuilder.forName(serverName).directExecutor().build()

GrpcPermissionSnapshotClient.create("localhost:${server!!.port}").use { client ->
GrpcPermissionSnapshotClient.create(channel).use { client ->
val result =
client.fetchSnapshot(
playerId,
Expand All @@ -63,40 +59,7 @@ class PermissionSnapshotClientTest {
assertEquals(playerId.toString(), request.playerId)
assertEquals("paper", request.serverType)
assertEquals("lobby-1", request.serverId)
assertEquals(
gg.grounds.permissions.PermissionGrantOrigin(
kind = gg.grounds.permissions.PermissionGrantOriginKind.GROUP_MAPPING,
roleKey = "moderator",
mappingId = "mapping-1",
inheritedPath = listOf("member", "moderator"),
),
success.snapshot.allowPatterns.single().origin,
)
assertEquals(ReleasedPermissionSnapshotFixture.expected(), success.snapshot)
}
}

private fun snapshotResponse(playerId: UUID): PlayerPermissionSnapshot {
val timestamp = Timestamp.newBuilder().setSeconds(1_700_000_000).build()
return PlayerPermissionSnapshot.newBuilder()
.setPlayerId(playerId.toString())
.setPolicyVersion(42)
.setIssuedAt(timestamp)
.setRefreshAfter(timestamp)
.setExpiresAt(timestamp)
.addAllowPatterns(
PermissionGrant.newBuilder()
.setEffect(PERMISSION_EFFECT_ALLOW)
.setPattern("grounds.chat")
.setScope(PermissionScope.newBuilder().setKind(PERMISSION_SCOPE_KIND_GLOBAL))
.setSource(PERMISSION_GRANT_SOURCE_ROLE)
.setOrigin(
PermissionGrantOrigin.newBuilder()
.setKind(PERMISSION_GRANT_ORIGIN_KIND_GROUP_MAPPING)
.setRoleKey("moderator")
.setMappingId("mapping-1")
.addAllInheritedPath(listOf("member", "moderator"))
)
)
.build()
}
}
2 changes: 2 additions & 0 deletions velocity/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ dependencies {
implementation("tools.jackson.module:jackson-module-kotlin:3.1.0")

testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation(testFixtures(project(":common")))
testImplementation("io.grpc:grpc-inprocess:1.82.0")
testImplementation("org.slf4j:slf4j-api")
testImplementation("com.velocitypowered:velocity-api")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ class GrpcPermissionSnapshotClient private constructor(private val channel: Mana
return GrpcPermissionSnapshotClient(channel)
}

internal fun create(channel: ManagedChannel): GrpcPermissionSnapshotClient =
GrpcPermissionSnapshotClient(channel)

private const val DEFAULT_TIMEOUT_MS = 2000L
}
}
Expand Down
Loading