Skip to content

Commit

Permalink
Add detailed logging for public certificate service (#1542)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcorilla authored and ple13 committed Aug 16, 2024
1 parent 71fe9af commit 2d7f465
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ kt_jvm_library(
":certificates",
"//src/main/kotlin/org/wfanet/measurement/api/v2alpha:principal_server_interceptor",
"//src/main/kotlin/org/wfanet/measurement/api/v2alpha:resource_key",
"//src/main/kotlin/org/wfanet/measurement/kingdom/service/api/v2alpha:internal_status_conversion",
"//src/main/proto/wfa/measurement/api/v2alpha:certificate_kt_jvm_proto",
"//src/main/proto/wfa/measurement/api/v2alpha:certificates_service_kt_jvm_grpc_proto",
"//src/main/proto/wfa/measurement/api/v2alpha:page_token_kt_jvm_proto",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,11 @@ class CertificatesService(private val internalCertificatesStub: CertificatesCoro
val internalCertificate =
try {
internalCertificatesStub.getCertificate(internalGetCertificateRequest)
} catch (ex: StatusException) {
when (ex.status.code) {
Status.Code.INVALID_ARGUMENT ->
failGrpc(Status.INVALID_ARGUMENT, ex) { "Required field unspecified or invalid." }
else -> failGrpc(Status.UNKNOWN, ex) { "Unknown exception." }
}
} catch (e: StatusException) {
throw when (e.status.code) {
Status.Code.INVALID_ARGUMENT -> Status.INVALID_ARGUMENT
else -> Status.UNKNOWN
}.toExternalStatusRuntimeException(e)
}
return internalCertificate.toCertificate()
}
Expand Down Expand Up @@ -185,11 +184,9 @@ class CertificatesService(private val internalCertificatesStub: CertificatesCoro
internalCertificatesStub.streamCertificates(internalRequest).toList()
} catch (e: StatusException) {
throw when (e.status.code) {
Status.Code.DEADLINE_EXCEEDED -> Status.DEADLINE_EXCEEDED
else -> Status.UNKNOWN
}
.withCause(e)
.asRuntimeException()
Status.Code.DEADLINE_EXCEEDED -> Status.DEADLINE_EXCEEDED
else -> Status.UNKNOWN
}.toExternalStatusRuntimeException(e)
}

if (internalCertificates.isEmpty()) {
Expand Down Expand Up @@ -242,17 +239,13 @@ class CertificatesService(private val internalCertificatesStub: CertificatesCoro
val response =
try {
internalCertificatesStub.createCertificate(internalCertificate)
} catch (ex: StatusException) {
when (ex.status.code) {
Status.Code.NOT_FOUND -> failGrpc(Status.NOT_FOUND, ex) { ex.message ?: "Not found" }
Status.Code.ALREADY_EXISTS ->
failGrpc(Status.ALREADY_EXISTS, ex) {
"Certificate with the subject key identifier (SKID) already exists."
}
Status.Code.INVALID_ARGUMENT ->
failGrpc(Status.INVALID_ARGUMENT, ex) { "Required field unspecified or invalid." }
else -> failGrpc(Status.UNKNOWN, ex) { "Unknown exception." }
}
} catch (e: StatusException) {
throw when (e.status.code) {
Status.Code.NOT_FOUND -> Status.NOT_FOUND
Status.Code.ALREADY_EXISTS -> Status.ALREADY_EXISTS
Status.Code.INVALID_ARGUMENT -> Status.INVALID_ARGUMENT
else -> Status.UNKNOWN
}.toExternalStatusRuntimeException(e)
}
return response.toCertificate()
}
Expand Down Expand Up @@ -294,13 +287,12 @@ class CertificatesService(private val internalCertificatesStub: CertificatesCoro
val internalCertificate =
try {
internalCertificatesStub.revokeCertificate(internalRevokeCertificateRequest)
} catch (ex: StatusException) {
when (ex.status.code) {
Status.Code.NOT_FOUND -> failGrpc(Status.NOT_FOUND, ex) { ex.message ?: "Not found" }
Status.Code.FAILED_PRECONDITION ->
failGrpc(Status.FAILED_PRECONDITION, ex) { "Certificate is in wrong State." }
else -> failGrpc(Status.UNKNOWN, ex) { "Unknown exception." }
}
} catch (e: StatusException) {
throw when (e.status.code) {
Status.Code.NOT_FOUND -> Status.NOT_FOUND
Status.Code.FAILED_PRECONDITION -> Status.FAILED_PRECONDITION
else -> Status.UNKNOWN
}.toExternalStatusRuntimeException(e)
}
return internalCertificate.toCertificate()
}
Expand Down Expand Up @@ -337,13 +329,12 @@ class CertificatesService(private val internalCertificatesStub: CertificatesCoro
val internalCertificate =
try {
internalCertificatesStub.releaseCertificateHold(internalReleaseCertificateHoldRequest)
} catch (ex: StatusException) {
when (ex.status.code) {
Status.Code.NOT_FOUND -> failGrpc(Status.NOT_FOUND, ex) { ex.message ?: "Not found" }
Status.Code.FAILED_PRECONDITION ->
failGrpc(Status.FAILED_PRECONDITION, ex) { "Certificate is in wrong State." }
else -> failGrpc(Status.UNKNOWN, ex) { "Unknown exception." }
}
} catch (e: StatusException) {
throw when (e.status.code) {
Status.Code.NOT_FOUND -> Status.NOT_FOUND
Status.Code.FAILED_PRECONDITION -> Status.FAILED_PRECONDITION
else -> Status.UNKNOWN
}.toExternalStatusRuntimeException(e)
}
return internalCertificate.toCertificate()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import org.wfanet.measurement.api.v2alpha.MeasurementConsumerCertificateKey
import org.wfanet.measurement.api.v2alpha.MeasurementConsumerKey
import org.wfanet.measurement.api.v2alpha.MeasurementKey
import org.wfanet.measurement.api.v2alpha.ModelProviderCertificateKey
import org.wfanet.measurement.api.v2alpha.ModelProviderKey
import org.wfanet.measurement.common.grpc.errorInfo
import org.wfanet.measurement.common.identity.externalIdToApiId
import org.wfanet.measurement.internal.kingdom.ErrorCode
Expand Down Expand Up @@ -175,13 +176,29 @@ fun Status.toExternalStatusRuntimeException(
}
// TODO{@jcorilla}: Populate metadata using subsequent error codes
ErrorCode.MODEL_PROVIDER_NOT_FOUND -> {
errorMessage = "ModelProvider not found."
val modelProviderName =
ModelProviderKey(
externalIdToApiId(
checkNotNull(errorInfo.metadataMap["external_model_provider_id"]).toLong()
)
)
.toName()
put("model_provider", modelProviderName)
errorMessage = "ModelProvider $modelProviderName not found."
}
ErrorCode.CERT_SUBJECT_KEY_ID_ALREADY_EXISTS -> {
errorMessage = "Certificate with the subject key identifier (SKID) already exists."
}
ErrorCode.CERTIFICATE_REVOCATION_STATE_ILLEGAL -> {
errorMessage = "Certificate is in wrong State."
val certificateApiId =
externalIdToApiId(
checkNotNull(errorInfo.metadataMap["external_certificate_id"]).toLong()
)
val certificateRevocationState =
checkNotNull(errorInfo.metadataMap["certificate_revocation_state"]).toString()
put("external_certificate_id", certificateApiId)
put("certification_revocation_state", certificateRevocationState)
errorMessage = "Certificate is in illegal revocation state: $certificateRevocationState."
}
ErrorCode.COMPUTATION_PARTICIPANT_STATE_ILLEGAL -> {
errorMessage = "ComputationParticipant state illegal."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ kt_jvm_test(
test_class = "org.wfanet.measurement.kingdom.service.api.v2alpha.CertificatesServiceTest",
deps = [
"//src/main/kotlin/org/wfanet/measurement/api/v2alpha/testing",
"//src/main/kotlin/org/wfanet/measurement/kingdom/deploy/gcloud/spanner/common",
"@wfa_common_jvm//imports/java/com/google/common/truth",
"@wfa_common_jvm//imports/java/com/google/common/truth/extensions/proto",
"@wfa_common_jvm//imports/java/org/junit",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import org.wfanet.measurement.common.base64UrlEncode
import org.wfanet.measurement.common.crypto.readCertificate
import org.wfanet.measurement.common.crypto.subjectKeyIdentifier
import org.wfanet.measurement.common.crypto.testing.TestData
import org.wfanet.measurement.common.grpc.errorInfo
import org.wfanet.measurement.common.grpc.testing.GrpcTestServerRule
import org.wfanet.measurement.common.grpc.testing.mockService
import org.wfanet.measurement.common.identity.ExternalId
Expand All @@ -89,9 +90,16 @@ import org.wfanet.measurement.internal.kingdom.getCertificateRequest as internal
import org.wfanet.measurement.internal.kingdom.releaseCertificateHoldRequest as internalReleaseCertificateHoldRequest
import org.wfanet.measurement.internal.kingdom.revokeCertificateRequest as internalRevokeCertificateRequest
import org.wfanet.measurement.internal.kingdom.streamCertificatesRequest

private val MODEL_PROVIDER_NAME = makeModelProvider(23456L)
private val MODEL_PROVIDER_NAME_2 = makeModelProvider(23457L)
import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.CertSubjectKeyIdAlreadyExistsException
import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.CertificateNotFoundException
import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.CertificateRevocationStateIllegalException
import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.DuchyNotFoundException
import org.wfanet.measurement.kingdom.deploy.gcloud.spanner.common.ModelProviderNotFoundException

private const val EXTERNAL_MODEL_PROVIDER_ID = 23456L
private const val EXTERNAL_MODEL_PROVIDER_ID_2 = 23457L
private val MODEL_PROVIDER_NAME = makeModelProvider(EXTERNAL_MODEL_PROVIDER_ID)
private val MODEL_PROVIDER_NAME_2 = makeModelProvider(EXTERNAL_MODEL_PROVIDER_ID_2)
private val MODEL_PROVIDER_CERTIFICATE_NAME = "$MODEL_PROVIDER_NAME/certificates/AAAAAAAAAcg"
private const val MEASUREMENT_CONSUMER_NAME = "measurementConsumers/AAAAAAAAAHs"
private const val MEASUREMENT_CONSUMER_NAME_2 = "measurementConsumers/BBBBBBBBBHs"
Expand Down Expand Up @@ -1294,6 +1302,201 @@ class CertificatesServiceTest {
assertThat(exception.status.code).isEqualTo(Status.Code.PERMISSION_DENIED)
}

@Test
fun `getCertificate throws INVALID_ARGUMENT when parent not specified`() {
internalCertificatesMock.stub {
onBlocking { getCertificate(any()) }
.thenThrow(
Status.INVALID_ARGUMENT.withDescription("Parent not specified").asRuntimeException()
)
}
val exception =
assertFailsWith<StatusRuntimeException> {
withPrincipal(
MeasurementConsumerPrincipal(MeasurementConsumerKey.fromName(MEASUREMENT_CONSUMER_NAME)!!)
) {
runBlocking {
service.getCertificate(
getCertificateRequest { name = MEASUREMENT_CONSUMER_CERTIFICATE_NAME }
)
}
}
}
assertThat(exception.status.code).isEqualTo(Status.Code.INVALID_ARGUMENT)
}

@Test
fun `createCertificate throws NOT_FOUND with model provider name when model provider not found`() {
internalCertificatesMock.stub {
onBlocking { createCertificate(any()) }
.thenThrow(
ModelProviderNotFoundException(ExternalId(EXTERNAL_MODEL_PROVIDER_ID))
.asStatusRuntimeException(Status.Code.NOT_FOUND, "Model provider not found")
)
}
val exception =
assertFailsWith<StatusRuntimeException> {
withPrincipal(ModelProviderPrincipal(ModelProviderKey.fromName(MODEL_PROVIDER_NAME)!!)) {
runBlocking {
service.createCertificate(
createCertificateRequest {
parent = MODEL_PROVIDER_NAME
certificate = CERTIFICATE
}
)
}
}
}
assertThat(exception.status.code).isEqualTo(Status.Code.NOT_FOUND)
assertThat(exception.errorInfo?.metadataMap)
.containsEntry("model_provider", MODEL_PROVIDER_NAME)
}

@Test
fun `createCertificate throws ALREADY_EXISTS when certificate with subject key identifier already exists`() {
internalCertificatesMock.stub {
onBlocking { createCertificate(any()) }
.thenThrow(
CertSubjectKeyIdAlreadyExistsException()
.asStatusRuntimeException(
Status.Code.ALREADY_EXISTS,
"Certificate with the subject key identifier (SKID) already exists.",
)
)
}
val exception =
assertFailsWith<StatusRuntimeException> {
withPrincipal(ModelProviderPrincipal(ModelProviderKey.fromName(MODEL_PROVIDER_NAME)!!)) {
runBlocking {
service.createCertificate(
createCertificateRequest {
parent = MODEL_PROVIDER_NAME
certificate = CERTIFICATE
}
)
}
}
}
assertThat(exception.status.code).isEqualTo(Status.Code.ALREADY_EXISTS)
}

@Test
fun `revokeCertificate throws NOT_FOUND when certificate is not found`() {
internalCertificatesMock.stub {
onBlocking { revokeCertificate(any()) }
.thenThrow(
CertificateNotFoundException(EXTERNAL_CERTIFICATE_ID)
.asStatusRuntimeException(Status.Code.NOT_FOUND, "Certificate not found")
)
}
val exception =
assertFailsWith<StatusRuntimeException> {
withPrincipal(ModelProviderPrincipal(ModelProviderKey.fromName(MODEL_PROVIDER_NAME)!!)) {
runBlocking {
service.revokeCertificate(
revokeCertificateRequest {
name = MODEL_PROVIDER_CERTIFICATE_NAME
revocationState = Certificate.RevocationState.REVOKED
}
)
}
}
}
assertThat(exception.status.code).isEqualTo(Status.Code.NOT_FOUND)
}

@Test
fun `revokeCertificate throws FAILED_PRECONDITION with certificate revocation state when certificate state illegal`() {
internalCertificatesMock.stub {
onBlocking { revokeCertificate(any()) }
.thenThrow(
CertificateRevocationStateIllegalException(
EXTERNAL_CERTIFICATE_ID,
InternalCertificate.RevocationState.REVOCATION_STATE_UNSPECIFIED,
)
.asStatusRuntimeException(
Status.Code.FAILED_PRECONDITION,
"Certificate in illegal state",
)
)
}
val exception =
assertFailsWith<StatusRuntimeException> {
withPrincipal(ModelProviderPrincipal(ModelProviderKey.fromName(MODEL_PROVIDER_NAME)!!)) {
runBlocking {
service.revokeCertificate(
revokeCertificateRequest {
name = MODEL_PROVIDER_CERTIFICATE_NAME
revocationState = Certificate.RevocationState.REVOKED
}
)
}
}
}
assertThat(exception.status.code).isEqualTo(Status.Code.FAILED_PRECONDITION)
assertThat(exception.errorInfo?.metadataMap)
.containsEntry(
"certification_revocation_state",
InternalCertificate.RevocationState.REVOCATION_STATE_UNSPECIFIED.toString(),
)
}

@Test
fun `releaseCertificateHold throws NOT_FOUND with duchy api id when duchy not found`() {
internalCertificatesMock.stub {
onBlocking { releaseCertificateHold(any()) }
.thenThrow(
DuchyNotFoundException(DuchyKey.fromName(DUCHY_NAME)!!.duchyId)
.asStatusRuntimeException(Status.Code.NOT_FOUND, "Duchy not found")
)
}
val exception =
assertFailsWith<StatusRuntimeException> {
withPrincipal(DuchyPrincipal(DuchyKey.fromName(DUCHY_NAME)!!)) {
runBlocking {
service.releaseCertificateHold(
releaseCertificateHoldRequest { name = DUCHY_CERTIFICATE_NAME }
)
}
}
}
assertThat(exception.status.code).isEqualTo(Status.Code.NOT_FOUND)
assertThat(exception.errorInfo?.metadataMap).containsEntry("duchy", DUCHY_NAME)
}

@Test
fun `releaseCertificateHold throws FAILED_PRECONDITION with certificate revocation state when certificate state illegal`() {
internalCertificatesMock.stub {
onBlocking { releaseCertificateHold(any()) }
.thenThrow(
CertificateRevocationStateIllegalException(
EXTERNAL_CERTIFICATE_ID,
InternalCertificate.RevocationState.REVOCATION_STATE_UNSPECIFIED,
)
.asStatusRuntimeException(
Status.Code.FAILED_PRECONDITION,
"Certificate in illegal state",
)
)
}
val exception =
assertFailsWith<StatusRuntimeException> {
withPrincipal(DuchyPrincipal(DuchyKey.fromName(DUCHY_NAME)!!)) {
runBlocking {
service.releaseCertificateHold(
releaseCertificateHoldRequest { name = DUCHY_CERTIFICATE_NAME }
)
}
}
}
assertThat(exception.status.code).isEqualTo(Status.Code.FAILED_PRECONDITION)
assertThat(exception.errorInfo?.metadataMap)
.containsEntry(
"certification_revocation_state",
InternalCertificate.RevocationState.REVOCATION_STATE_UNSPECIFIED.toString(),
)
}

companion object {
private val EXTERNAL_DATA_PROVIDER_ID = ExternalId(12345L)
private val DATA_PROVIDER_KEY = DataProviderKey(EXTERNAL_DATA_PROVIDER_ID.apiId.value)
Expand Down

0 comments on commit 2d7f465

Please sign in to comment.