Skip to content

Commit

Permalink
Setup for reporting v2alpha (#942)
Browse files Browse the repository at this point in the history
* Adding building blocks for reporting v2 service implementations. Most
files are inherited from v1.
* Fix metric spec config.
  • Loading branch information
riemanli authored Apr 14, 2023
1 parent 8095eea commit 3af5669
Show file tree
Hide file tree
Showing 12 changed files with 577 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2022 The Cross-Media Measurement Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wfanet.measurement.reporting.service.api.v2alpha

import com.google.protobuf.ByteString
import java.io.File
import org.wfanet.measurement.api.v2alpha.MeasurementConsumerKey
import org.wfanet.measurement.common.api.AkidConfigPrincipalLookup
import org.wfanet.measurement.common.api.AkidConfigResourceNameLookup
import org.wfanet.measurement.common.api.PrincipalLookup
import org.wfanet.measurement.common.api.ResourceKey
import org.wfanet.measurement.common.api.toResourceKeyLookup
import org.wfanet.measurement.common.parseTextProto
import org.wfanet.measurement.config.AuthorityKeyToPrincipalMap
import org.wfanet.measurement.config.reporting.MeasurementConsumerConfig
import org.wfanet.measurement.config.reporting.MeasurementConsumerConfigs

/** [PrincipalLookup] of [ReportingPrincipal] by authority key identifier (AKID). */
class AkidPrincipalLookup(
akidConfig: AuthorityKeyToPrincipalMap,
measurementConsumerConfigs: MeasurementConsumerConfigs
) : PrincipalLookup<ReportingPrincipal, ByteString> {

/**
* Constructs [AkidConfigPrincipalLookup] from a file.
*
* @param akidConfig a [File] containing an [AuthorityKeyToPrincipalMap] message in text format
* @param measurementConsumerConfigs a [File] containing a [MeasurementConsumerConfigs] message in
* text format
*/
constructor(
akidConfig: File,
measurementConsumerConfigs: File
) : this(
parseTextProto(akidConfig, AuthorityKeyToPrincipalMap.getDefaultInstance()),
parseTextProto(measurementConsumerConfigs, MeasurementConsumerConfigs.getDefaultInstance())
)

private val measurementConsumerConfigs: Map<String, MeasurementConsumerConfig> =
measurementConsumerConfigs.configsMap

private val resourceKeyLookup =
AkidConfigResourceNameLookup(akidConfig).toResourceKeyLookup(MeasurementConsumerKey.FACTORY)

override suspend fun getPrincipal(lookupKey: ByteString): ReportingPrincipal? {
val resourceKey: ResourceKey = resourceKeyLookup.getResourceKey(lookupKey) ?: return null
return when (resourceKey) {
is MeasurementConsumerKey -> {
val resourceName: String = resourceKey.toName()
val config =
measurementConsumerConfigs[resourceName]
?: error("Missing MeasurementConsumerConfig for $resourceName")
MeasurementConsumerPrincipal(resourceKey, config)
}
else -> null
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")

package(default_visibility = ["//visibility:public"])

kt_jvm_library(
name = "resource_key",
srcs = glob(["*Key.kt"]) + ["IdVariable.kt"],
deps = [
"//src/main/kotlin/org/wfanet/measurement/common/api:resource_key",
"@wfa_common_jvm//src/main/kotlin/org/wfanet/measurement/common",
],
)

kt_jvm_library(
name = "akid_principal_lookup",
srcs = ["AkidPrincipalLookup.kt"],
deps = [
"//src/main/kotlin/org/wfanet/measurement/api/v2alpha:resource_key",
"//src/main/kotlin/org/wfanet/measurement/common/api:akid_config_lookup",
"//src/main/kotlin/org/wfanet/measurement/reporting/service/api/v2alpha:reporting_principal",
"//src/main/proto/wfa/measurement/config/reporting:measurement_consumer_config_kt_jvm_proto",
],
)

kt_jvm_library(
name = "context_keys",
srcs = ["ContextKeys.kt"],
deps = [
"//src/main/kotlin/org/wfanet/measurement/common/api:principal",
"//src/main/kotlin/org/wfanet/measurement/reporting/service/api/v2alpha:reporting_principal",
"@wfa_common_jvm//imports/java/io/grpc:api",
"@wfa_common_jvm//imports/java/io/grpc:context",
],
)

kt_jvm_library(
name = "reporting_principal",
srcs = ["ReportingPrincipal.kt"],
deps = [
"//src/main/kotlin/org/wfanet/measurement/api/v2alpha:resource_key",
"//src/main/kotlin/org/wfanet/measurement/common/api:principal",
"//src/main/proto/wfa/measurement/config/reporting:measurement_consumer_config_kt_jvm_proto",
],
)

kt_jvm_library(
name = "principal_server_interceptor",
srcs = ["PrincipalServerInterceptor.kt"],
deps = [
"context_keys",
":reporting_principal",
"//src/main/kotlin/org/wfanet/measurement/api/v2alpha:resource_key",
"//src/main/kotlin/org/wfanet/measurement/common/api/grpc",
"//src/main/kotlin/org/wfanet/measurement/common/identity",
"@wfa_common_jvm//imports/java/com/google/protobuf",
"@wfa_common_jvm//imports/java/io/grpc:api",
"@wfa_common_jvm//imports/java/io/grpc:context",
"@wfa_common_jvm//src/main/kotlin/org/wfanet/measurement/common/grpc",
"@wfa_common_jvm//src/main/kotlin/org/wfanet/measurement/common/identity",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2022 The Cross-Media Measurement Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wfanet.measurement.reporting.service.api.v2alpha

import io.grpc.Context

object ContextKeys {
/** This is the context key for the authenticated [ReportingPrincipal]. */
val PRINCIPAL_CONTEXT_KEY: Context.Key<ReportingPrincipal> = Context.key("principal")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2022 The Cross-Media Measurement Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wfanet.measurement.reporting.service.api.v2alpha

import org.wfanet.measurement.common.ResourceNameParser
import org.wfanet.measurement.common.api.ResourceKey

private val parser =
ResourceNameParser(
"measurementConsumers/{measurement_consumer}" +
"/dataProviders/{data_provider}/eventGroups/{event_group}"
)

/** [ResourceKey] of an EventGroup. */
class EventGroupKey(
val cmmsMeasurementConsumerId: String,
val cmmsDataProviderId: String,
val cmmsEventGroupId: String
) : ResourceKey {
override fun toName(): String {
return parser.assembleName(
mapOf(
IdVariable.MEASUREMENT_CONSUMER to cmmsMeasurementConsumerId,
IdVariable.DATA_PROVIDER to cmmsDataProviderId,
IdVariable.EVENT_GROUP to cmmsEventGroupId
)
)
}

companion object FACTORY : ResourceKey.Factory<EventGroupKey> {
val defaultValue = EventGroupKey("", "", "")

override fun fromName(resourceName: String): EventGroupKey? {
return parser.parseIdVars(resourceName)?.let {
EventGroupKey(
it.getValue(IdVariable.MEASUREMENT_CONSUMER),
it.getValue(IdVariable.DATA_PROVIDER),
it.getValue(IdVariable.EVENT_GROUP)
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2022 The Cross-Media Measurement Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wfanet.measurement.reporting.service.api.v2alpha

import java.util.Locale
import org.wfanet.measurement.common.ResourceNameParser

internal enum class IdVariable {
DATA_PROVIDER,
EVENT_GROUP,
MEASUREMENT_CONSUMER,
REPORTING_SET,
METRIC,
REPORT,
}

internal fun ResourceNameParser.assembleName(idMap: Map<IdVariable, String>): String {
return assembleName(idMap.mapKeys { it.key.name.lowercase(Locale.getDefault()) })
}

internal fun ResourceNameParser.parseIdVars(resourceName: String): Map<IdVariable, String>? {
return parseIdSegments(resourceName)?.mapKeys {
IdVariable.valueOf(it.key.uppercase(Locale.getDefault()))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2022 The Cross-Media Measurement Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wfanet.measurement.reporting.service.api.v2alpha

import org.wfanet.measurement.common.ResourceNameParser
import org.wfanet.measurement.common.api.ResourceKey

private val parser =
ResourceNameParser("measurementConsumers/{measurement_consumer}/metrics/{metric}")

/** [ResourceKey] of a Report. */
data class MetricKey(
val cmmsMeasurementConsumerId: String,
val metricId: String,
) : ResourceKey {
override fun toName(): String {
return parser.assembleName(
mapOf(
IdVariable.MEASUREMENT_CONSUMER to cmmsMeasurementConsumerId,
IdVariable.METRIC to metricId,
)
)
}

companion object FACTORY : ResourceKey.Factory<MetricKey> {
val defaultValue = MetricKey("", "")

override fun fromName(resourceName: String): MetricKey? {
return parser.parseIdVars(resourceName)?.let {
MetricKey(it.getValue(IdVariable.MEASUREMENT_CONSUMER), it.getValue(IdVariable.METRIC))
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2022 The Cross-Media Measurement Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.wfanet.measurement.reporting.service.api.v2alpha

import com.google.protobuf.ByteString
import io.grpc.BindableService
import io.grpc.Context
import io.grpc.ServerInterceptors
import io.grpc.ServerServiceDefinition
import io.grpc.Status
import org.wfanet.measurement.api.v2alpha.MeasurementConsumerKey
import org.wfanet.measurement.common.api.PrincipalLookup
import org.wfanet.measurement.common.api.grpc.AkidPrincipalServerInterceptor
import org.wfanet.measurement.common.grpc.failGrpc
import org.wfanet.measurement.common.identity.AuthorityKeyServerInterceptor
import org.wfanet.measurement.config.reporting.MeasurementConsumerConfig

/**
* Returns a [ReportingPrincipal] in the current gRPC context. Requires [PrincipalServerInterceptor]
* to be installed.
*
* Callers can trust that the [ReportingPrincipal] is authenticated (but not necessarily
* authorized).
*/
val principalFromCurrentContext: ReportingPrincipal
get() =
ContextKeys.PRINCIPAL_CONTEXT_KEY.get()
?: failGrpc(Status.UNAUTHENTICATED) { "No ReportingPrincipal found" }

/**
* Executes [block] with [principal] installed in a new [Context].
*
* The caller of [withPrincipal] is responsible for guaranteeing that [block] can act as [principal]
* -- in other words, [principal] is treated as already authenticated.
*/
fun <T> withPrincipal(principal: ReportingPrincipal, block: () -> T): T {
return Context.current().withPrincipal(principal).call(block)
}

/** Executes [block] with a [MeasurementConsumerPrincipal] installed in a new [Context]. */
fun <T> withMeasurementConsumerPrincipal(
measurementConsumerName: String,
config: MeasurementConsumerConfig,
block: () -> T
): T {
return Context.current()
.withPrincipal(
MeasurementConsumerPrincipal(
MeasurementConsumerKey.fromName(measurementConsumerName)!!,
config
)
)
.call(block)
}

/** Adds [principal] to the receiver and returns the new [Context]. */
fun Context.withPrincipal(principal: ReportingPrincipal): Context {
return withValue(ContextKeys.PRINCIPAL_CONTEXT_KEY, principal)
}

/** Convenience helper for [AkidPrincipalServerInterceptor]. */
fun BindableService.withPrincipalsFromX509AuthorityKeyIdentifiers(
akidPrincipalLookup: PrincipalLookup<ReportingPrincipal, ByteString>
): ServerServiceDefinition {
return ServerInterceptors.interceptForward(
this,
AuthorityKeyServerInterceptor(),
AkidPrincipalServerInterceptor(
ContextKeys.PRINCIPAL_CONTEXT_KEY,
AuthorityKeyServerInterceptor.AUTHORITY_KEY_IDENTIFIERS_CONTEXT_KEY,
akidPrincipalLookup
)
)
}
Loading

0 comments on commit 3af5669

Please sign in to comment.