Skip to content
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

Add detailed logging for public certificate service #1542

Merged
merged 3 commits into from
Mar 29, 2024
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 @@ -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
Loading