From bacee09e57c4ec8a9d740a65bfb540e946593264 Mon Sep 17 00:00:00 2001 From: Dimos Raptis Date: Wed, 27 Sep 2023 09:16:46 +0100 Subject: [PATCH] CORE-12411 - Add declination reason (#4696) This change adjusts the MGM code, so that it communicates the reason for declined requests back to the user and the appropriate handler, so that this information is stored in the DB and surfaced to the user. In some cases, we don't want to reveal potentially sensitive information to a member (especially in cases where the request hasn't gone fully through authentication yet). In these cases, a general message is communicated instructing the user to communicate with the network operator for more details. --- .../impl/client/MGMResourceClientImpl.kt | 6 +- .../impl/client/MGMResourceClientTest.kt | 5 +- .../impl/p2p/MembershipP2PMarkersProcessor.kt | 3 +- .../SetOwnRegistrationStatusHandler.kt | 2 +- .../SetOwnRegistrationStatusHandlerTest.kt | 11 +- .../PersistMemberRegistrationStateHandler.kt | 1 + .../handler/mgm/ApproveRegistrationHandler.kt | 8 +- .../handler/mgm/DeclineRegistrationHandler.kt | 1 + ...rocessMemberVerificationResponseHandler.kt | 5 +- .../handler/mgm/QueueRegistrationHandler.kt | 1 + .../handler/mgm/StartRegistrationHandler.kt | 165 ++++++++++-------- .../handler/mgm/VerifyMemberHandler.kt | 3 +- .../dynamic/mgm/ExpirationProcessorImpl.kt | 4 +- ...rsistMemberRegistrationStateHandlerTest.kt | 7 +- .../mgm/ApproveRegistrationHandlerTest.kt | 5 +- .../mgm/DeclineRegistrationHandlerTest.kt | 3 +- ...ssMemberVerificationResponseHandlerTest.kt | 2 +- .../mgm/QueueRegistrationHandlerTest.kt | 1 + .../mgm/StartRegistrationHandlerTest.kt | 2 +- gradle.properties | 2 +- .../membership/lib/VersionedMessageBuilder.kt | 4 +- .../RegistrationRequestConstants.kt | 26 ++- .../lib/VersionedMessageBuilderTest.kt | 8 +- 23 files changed, 172 insertions(+), 103 deletions(-) diff --git a/components/membership/membership-client-impl/src/main/kotlin/net/corda/membership/impl/client/MGMResourceClientImpl.kt b/components/membership/membership-client-impl/src/main/kotlin/net/corda/membership/impl/client/MGMResourceClientImpl.kt index eb1ce633d9d..c5d4dee2a79 100644 --- a/components/membership/membership-client-impl/src/main/kotlin/net/corda/membership/impl/client/MGMResourceClientImpl.kt +++ b/components/membership/membership-client-impl/src/main/kotlin/net/corda/membership/impl/client/MGMResourceClientImpl.kt @@ -51,6 +51,7 @@ import net.corda.membership.lib.MemberInfoExtension.Companion.isMgm import net.corda.membership.lib.MemberInfoFactory import net.corda.membership.lib.approval.ApprovalRuleParams import net.corda.membership.lib.deserializeContext +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_GENERAL_MANUAL_DECLINED import net.corda.membership.lib.toPersistentGroupParameters import net.corda.membership.persistence.client.MembershipPersistenceClient import net.corda.membership.persistence.client.MembershipQueryClient @@ -597,7 +598,8 @@ class MGMResourceClientImpl @Activate constructor( if (approve) { publishRegistrationCommand(ApproveRegistration(), memberName, mgm.groupId) } else { - publishRegistrationCommand(DeclineRegistration(reason ?: ""), memberName, mgm.groupId) + publishRegistrationCommand(DeclineRegistration(reason ?: "", + DECLINED_REASON_FOR_USER_GENERAL_MANUAL_DECLINED), memberName, mgm.groupId) } } @@ -615,7 +617,7 @@ class MGMResourceClientImpl @Activate constructor( "declined. Refer to the docs on Member Suspension to suspend approved members." } publishRegistrationCommand( - DeclineRegistration(FORCE_DECLINE_MESSAGE), + DeclineRegistration(FORCE_DECLINE_MESSAGE, DECLINED_REASON_FOR_USER_GENERAL_MANUAL_DECLINED), findMemberName(requestStatus.memberProvidedContext), mgm.groupId ) diff --git a/components/membership/membership-client-impl/src/test/kotlin/net/corda/membership/impl/client/MGMResourceClientTest.kt b/components/membership/membership-client-impl/src/test/kotlin/net/corda/membership/impl/client/MGMResourceClientTest.kt index 8f0a35631c4..89622ea842a 100644 --- a/components/membership/membership-client-impl/src/test/kotlin/net/corda/membership/impl/client/MGMResourceClientTest.kt +++ b/components/membership/membership-client-impl/src/test/kotlin/net/corda/membership/impl/client/MGMResourceClientTest.kt @@ -59,6 +59,7 @@ import net.corda.membership.lib.approval.ApprovalRuleParams import net.corda.membership.lib.impl.MemberInfoFactoryImpl import net.corda.membership.lib.impl.converter.EndpointInfoConverter import net.corda.membership.lib.impl.converter.MemberNotaryDetailsConverter +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_GENERAL_MANUAL_DECLINED import net.corda.membership.persistence.client.MembershipPersistenceClient import net.corda.membership.persistence.client.MembershipPersistenceOperation import net.corda.membership.persistence.client.MembershipPersistenceResult @@ -912,7 +913,7 @@ class MGMResourceClientTest { Record( Schemas.Membership.REGISTRATION_COMMAND_TOPIC, "$memberName-$DEFAULT_MEMBER_GROUP_ID", - RegistrationCommand(DeclineRegistration(reason)) + RegistrationCommand(DeclineRegistration(reason, DECLINED_REASON_FOR_USER_GENERAL_MANUAL_DECLINED)) ) ) ) @@ -1086,7 +1087,7 @@ class MGMResourceClientTest { Record( Schemas.Membership.REGISTRATION_COMMAND_TOPIC, "$memberName-$DEFAULT_MEMBER_GROUP_ID", - RegistrationCommand(DeclineRegistration("Force declined by MGM")) + RegistrationCommand(DeclineRegistration("Force declined by MGM", DECLINED_REASON_FOR_USER_GENERAL_MANUAL_DECLINED)) ) ) ) diff --git a/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/MembershipP2PMarkersProcessor.kt b/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/MembershipP2PMarkersProcessor.kt index 7766aa51fc6..09786f87eb5 100644 --- a/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/MembershipP2PMarkersProcessor.kt +++ b/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/MembershipP2PMarkersProcessor.kt @@ -3,6 +3,7 @@ package net.corda.membership.impl.p2p import net.corda.data.membership.command.registration.RegistrationCommand import net.corda.data.membership.command.registration.mgm.DeclineRegistration import net.corda.data.p2p.markers.AppMessageMarker +import net.corda.membership.lib.registration.DECLINED_REASON_COMMS_ISSUE import net.corda.membership.p2p.helpers.TtlIdsFactory import net.corda.messaging.api.processor.DurableProcessor import net.corda.messaging.api.records.Record @@ -26,7 +27,7 @@ internal class MembershipP2PMarkersProcessor( Schemas.Membership.REGISTRATION_COMMAND_TOPIC, key, RegistrationCommand( - DeclineRegistration("Could not send message to member.") + DeclineRegistration(DECLINED_REASON_COMMS_ISSUE, DECLINED_REASON_COMMS_ISSUE) ) ) ) diff --git a/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandler.kt b/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandler.kt index 887feaa48b7..06d89cd5286 100644 --- a/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandler.kt +++ b/components/membership/membership-p2p-impl/src/main/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandler.kt @@ -52,6 +52,6 @@ internal class SetOwnRegistrationStatusHandler( RegistrationStatus.FAILED -> RegistrationStatusV2.FAILED else -> throw IllegalArgumentException("Unknown status '${newStatus.name}' received.") } - return SetOwnRegistrationStatusV2(registrationId, status) + return SetOwnRegistrationStatusV2(registrationId, status, null) } } diff --git a/components/membership/membership-p2p-impl/src/test/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandlerTest.kt b/components/membership/membership-p2p-impl/src/test/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandlerTest.kt index de92737c59e..695efda8d48 100644 --- a/components/membership/membership-p2p-impl/src/test/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandlerTest.kt +++ b/components/membership/membership-p2p-impl/src/test/kotlin/net/corda/membership/impl/p2p/handler/SetOwnRegistrationStatusHandlerTest.kt @@ -25,9 +25,11 @@ class SetOwnRegistrationStatusHandlerTest { RegistrationStatus.DECLINED ) private val payloadV2 = ByteBuffer.wrap(byteArrayOf(4, 5, 6)) + private val reason = "some reason" private val statusV2 = SetOwnRegistrationStatusV2( "id", - RegistrationStatusV2.DECLINED + RegistrationStatusV2.DECLINED, + reason ) private val avroSchemaRegistry: AvroSchemaRegistry = mock { on { getClassType(payloadV1) } doReturn SetOwnRegistrationStatus::class.java @@ -44,6 +46,11 @@ class SetOwnRegistrationStatusHandlerTest { @Test fun `invokeAuthenticatedMessage returns PersistMemberRegistrationState command - V1 version converted to V2 successfully`() { val record = handler.invokeAuthenticatedMessage(header, payloadV1) + val statusV2WithoutReason = SetOwnRegistrationStatusV2( + "id", + RegistrationStatusV2.DECLINED, + null + ) assertSoftly { softly -> softly.assertThat(record.topic).isEqualTo(REGISTRATION_COMMAND_TOPIC) @@ -52,7 +59,7 @@ class SetOwnRegistrationStatusHandlerTest { RegistrationCommand( PersistMemberRegistrationState( identity, - statusV2 + statusV2WithoutReason ) ) ) diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandler.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandler.kt index 5746875478a..c6d92b889ff 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandler.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandler.kt @@ -21,6 +21,7 @@ internal class PersistMemberRegistrationStateHandler( member, command.setStatusRequest.registrationId, command.setStatusRequest.newStatus, + command.setStatusRequest.reason ).createAsyncCommands() return RegistrationHandlerResult( null, diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandler.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandler.kt index b00b2ab447a..285abae1447 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandler.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandler.kt @@ -20,6 +20,7 @@ import net.corda.membership.lib.MemberInfoExtension.Companion.notaryDetails import net.corda.membership.lib.MemberInfoFactory import net.corda.membership.lib.exceptions.MembershipPersistenceException import net.corda.membership.lib.VersionedMessageBuilder.retrieveRegistrationStatusMessage +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_INTERNAL_ERROR import net.corda.membership.p2p.helpers.P2pRecordsFactory import net.corda.membership.persistence.client.MembershipPersistenceClient import net.corda.membership.persistence.client.MembershipPersistenceResult @@ -130,7 +131,8 @@ internal class ApproveRegistrationHandler( val statusUpdateMessage = retrieveRegistrationStatusMessage( memberInfo.platformVersion, registrationId, - RegistrationStatus.APPROVED.name + RegistrationStatus.APPROVED.name, + null ) val persistApproveMessage = if (statusUpdateMessage != null) { p2pRecordsFactory.createAuthenticatedMessageRecord( @@ -152,7 +154,9 @@ internal class ApproveRegistrationHandler( logger.warn("Could not approve registration request: '$registrationId'", e) return RegistrationHandlerResult( state, - listOf(Record(REGISTRATION_COMMAND_TOPIC, key, RegistrationCommand(DeclineRegistration(e.message)))) + listOf(Record(REGISTRATION_COMMAND_TOPIC, key, + RegistrationCommand(DeclineRegistration(e.message, DECLINED_REASON_FOR_USER_INTERNAL_ERROR))) + ) ) } diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandler.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandler.kt index a12d610ad50..4ff66ebfd0d 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandler.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandler.kt @@ -69,6 +69,7 @@ internal class DeclineRegistrationHandler( pendingMemberInfo.platformVersion, registrationId, RegistrationStatus.DECLINED.name, + command.reasonForUser ) if (statusUpdateMessage != null) { p2pRecordsFactory.createAuthenticatedMessageRecord( diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandler.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandler.kt index 28f9ba0e15b..85cc4fd1f5a 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandler.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandler.kt @@ -25,6 +25,7 @@ import net.corda.membership.lib.approval.RegistrationRulesEngine import net.corda.membership.lib.registration.PRE_AUTH_TOKEN import net.corda.membership.lib.VersionedMessageBuilder.retrieveRegistrationStatusMessage import net.corda.membership.lib.deserializeContext +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_INTERNAL_ERROR import net.corda.membership.lib.toMap import net.corda.membership.p2p.helpers.P2pRecordsFactory import net.corda.membership.p2p.helpers.P2pRecordsFactory.Companion.getTtlMinutes @@ -112,7 +113,7 @@ internal class ProcessMemberVerificationResponseHandler( status ).createAsyncCommands() val statusUpdateMessage = retrieveRegistrationStatusMessage( - pendingInfo.platformVersion, registrationId, status.name + pendingInfo.platformVersion, registrationId, status.name, null ) val persistStatusMessage = if (statusUpdateMessage != null) { p2pRecordsFactory.createAuthenticatedMessageRecord( @@ -142,7 +143,7 @@ internal class ProcessMemberVerificationResponseHandler( REGISTRATION_COMMAND_TOPIC, key, RegistrationCommand( - DeclineRegistration(e.message) + DeclineRegistration(e.message, DECLINED_REASON_FOR_USER_INTERNAL_ERROR) ) ), ) diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandler.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandler.kt index 1c2c8aca635..0b4d0dfcd08 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandler.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandler.kt @@ -124,6 +124,7 @@ internal class QueueRegistrationHandler( platformVersion, command.memberRegistrationRequest.registrationId, RegistrationStatus.RECEIVED_BY_MGM.name, + null ) // if we are unable to create the status message, then we won't send anything val statusUpdateRecord = statusUpdateMessage?.let { diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandler.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandler.kt index 1a951d8e7a3..2be534fe837 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandler.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandler.kt @@ -36,6 +36,18 @@ import net.corda.membership.lib.MemberInfoExtension.Companion.status import net.corda.membership.lib.MemberInfoFactory import net.corda.membership.lib.SelfSignedMemberInfo import net.corda.membership.lib.deserializeContext +import net.corda.membership.lib.registration.DECLINED_REASON_EMPTY_REGISTRATION_CONTEXT +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_INTERNAL_ERROR +import net.corda.membership.lib.registration.DECLINED_REASON_GROUP_ID_IN_REQUEST_NOT_MATCHING_TARGET +import net.corda.membership.lib.registration.DECLINED_REASON_INVALID_NOTARY_SERVICE_PLUGIN_TYPE +import net.corda.membership.lib.registration.DECLINED_REASON_NAME_IN_REQUEST_NOT_MATCHING_NAME_IN_P2P_MSG +import net.corda.membership.lib.registration.DECLINED_REASON_NOTARY_MISSING_NOTARY_DETAILS +import net.corda.membership.lib.registration.DECLINED_REASON_NOT_MGM_IDENTITY +import net.corda.membership.lib.registration.DECLINED_REASON_NO_ENDPOINTS_SPECIFIED +import net.corda.membership.lib.registration.DECLINED_REASON_RESISTRANT_IS_MGM +import net.corda.membership.lib.registration.DECLINED_REASON_SERIAL_NEGATIVE +import net.corda.membership.lib.registration.DECLINED_REASON_SERIAL_NULL import net.corda.membership.lib.registration.RegistrationRequestHelpers.getPreAuthToken import net.corda.membership.lib.toMap import net.corda.membership.persistence.client.MembershipPersistenceClient @@ -106,17 +118,15 @@ internal class StartRegistrationHandler( val mgmMemberInfo = getMGMMemberInfo(mgmHoldingId) val registrationRequest = membershipQueryClient.queryRegistrationRequest(mgmHoldingId, registrationId) .getOrThrow() - validateRegistrationRequest(registrationRequest != null) { - "Could not find registration request with ID `$registrationId`." - } + validateRegistrationRequest(registrationRequest != null, + "Could not find registration request with ID `$registrationId`.", DECLINED_REASON_FOR_USER_INTERNAL_ERROR) logger.info("Registering $pendingMemberHoldingId with MGM for holding identity: $mgmHoldingId") val pendingMemberInfo = buildPendingMemberInfo(registrationRequest!!) // Parse the registration request and verify contents // The MemberX500Name matches the source MemberX500Name from the P2P messaging - validateRegistrationRequest( - pendingMemberInfo.name == pendingMemberHoldingId.x500Name - ) { "MemberX500Name in registration request does not match member sending request over P2P." } + validateRegistrationRequest(pendingMemberInfo.name == pendingMemberHoldingId.x500Name, + DECLINED_REASON_NAME_IN_REQUEST_NOT_MATCHING_NAME_IN_P2P_MSG, DECLINED_REASON_NAME_IN_REQUEST_NOT_MATCHING_NAME_IN_P2P_MSG) val persistentMemberInfo = memberInfoFactory.createPersistentMemberInfo( mgmMemberInfo.holdingIdentity.toAvro(), @@ -151,15 +161,12 @@ internal class StartRegistrationHandler( } } - validateRegistrationRequest(registrationRequest.serial != null) { - "Serial on the registration request should not be null." - } - validateRegistrationRequest(registrationRequest.serial!! >= 0) { - "Serial cannot be negative on the registration request." - } - validateRegistrationRequest(!memberTypeChecker.isMgm(pendingMemberHoldingId)) { - "Registration request is registering an MGM holding identity." - } + validateRegistrationRequest(registrationRequest.serial != null, + DECLINED_REASON_SERIAL_NULL, DECLINED_REASON_SERIAL_NULL) + validateRegistrationRequest(registrationRequest.serial!! >= 0, + DECLINED_REASON_SERIAL_NEGATIVE, DECLINED_REASON_SERIAL_NEGATIVE) + validateRegistrationRequest(!memberTypeChecker.isMgm(pendingMemberHoldingId), + DECLINED_REASON_RESISTRANT_IS_MGM, DECLINED_REASON_RESISTRANT_IS_MGM) val activeOrSuspendedInfo = membershipQueryClient.queryMemberInfo( mgmHoldingId, @@ -168,18 +175,19 @@ internal class StartRegistrationHandler( it.status == MEMBER_STATUS_ACTIVE || it.status == MEMBER_STATUS_SUSPENDED } if (registrationRequest.serial!! > 0) { //re-registration - validateRegistrationRequest(activeOrSuspendedInfo != null) { + val serialShouldBeZero = "Member has not registered previously so serial number should be 0, but it was ${registrationRequest.serial}." - } - validateRegistrationRequest(activeOrSuspendedInfo!!.serial <= registrationRequest.serial!!) { + validateRegistrationRequest(activeOrSuspendedInfo != null, serialShouldBeZero, serialShouldBeZero) + + val serialNotUpToDate = "Registration request was submitted for an older version of member info. " + - "The submitted serial was ${registrationRequest.serial}, but the latest serial is ${activeOrSuspendedInfo.serial}. " + + "The submitted serial was ${registrationRequest.serial}, but the latest serial is ${activeOrSuspendedInfo!!.serial}. " + "Please submit a new request with an up-to-date serial number." - } + validateRegistrationRequest(activeOrSuspendedInfo.serial <= registrationRequest.serial!!, + serialNotUpToDate, serialNotUpToDate) } else if (registrationRequest.serial!! == 0L) { // initial registration - validateRegistrationRequest(activeOrSuspendedInfo == null) { - "Member already exists with the same X500 name." - } + validateRegistrationRequest(activeOrSuspendedInfo == null, + DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON, DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON) } validatePreAuthTokenUsage(mgmHoldingId, pendingMemberInfo, registrationRequest) @@ -194,20 +202,17 @@ internal class StartRegistrationHandler( it.key.startsWith(ROLES_PREFIX) || it.key.startsWith("corda.notary") } - validateRegistrationRequest( - diff.isEmpty() - ) { "Fields ${diff.map { it.key }} cannot be added, removed or updated during re-registration." } + val diffInvalidMsgFn = { "Fields ${diff.map { it.key }} cannot be added, removed or updated during re-registration." } + validateRegistrationRequest(diff.isEmpty(), diffInvalidMsgFn, diffInvalidMsgFn) } // The group ID matches the group ID of the MGM - validateRegistrationRequest( - pendingMemberInfo.groupId == mgmMemberInfo.groupId - ) { "Group ID in registration request does not match the group ID of the target MGM." } + validateRegistrationRequest(pendingMemberInfo.groupId == mgmMemberInfo.groupId, + DECLINED_REASON_GROUP_ID_IN_REQUEST_NOT_MATCHING_TARGET, DECLINED_REASON_GROUP_ID_IN_REQUEST_NOT_MATCHING_TARGET) // There is at least one endpoint specified - validateRegistrationRequest( - pendingMemberInfo.endpoints.isNotEmpty() - ) { "Registering member has not specified any endpoints" } + validateRegistrationRequest(pendingMemberInfo.endpoints.isNotEmpty(), + DECLINED_REASON_NO_ENDPOINTS_SPECIFIED, DECLINED_REASON_NO_ENDPOINTS_SPECIFIED) // Validate role-specific information if any role is set validateRoleInformation(mgmHoldingId, pendingMemberInfo) @@ -216,10 +221,10 @@ internal class StartRegistrationHandler( VerifyMember() } catch (ex: InvalidRegistrationRequestException) { logger.warn("Declined registration. ${ex.originalMessage}") - DeclineRegistration(ex.originalMessage) + DeclineRegistration(ex.originalMessage, ex.reasonForUser) } catch (ex: Exception) { logger.warn("Declined registration. ${ex.message}") - DeclineRegistration("Failed to verify registration request due to: [${ex.message}]") + DeclineRegistration("Failed to verify registration request due to: [${ex.message}]", DECLINED_REASON_FOR_USER_INTERNAL_ERROR) } outputRecords.add(Record(REGISTRATION_COMMAND_TOPIC, key, RegistrationCommand(outputCommand))) @@ -234,27 +239,32 @@ internal class StartRegistrationHandler( command: StartRegistration ): net.corda.data.identity.HoldingIdentity? = state?.registeringMember - private class InvalidRegistrationRequestException(reason: String) : CordaRuntimeException(reason) + private class InvalidRegistrationRequestException(reason: String, val reasonForUser: String?) : CordaRuntimeException(reason) - private fun validateRegistrationRequest(condition: Boolean, errorMsg: () -> String) { + private fun validateRegistrationRequest(condition: Boolean, errorMsg: String, reasonForUser: String?) { if (!condition) { - with(errorMsg.invoke()) { - logger.info(this) - throw InvalidRegistrationRequestException(this) - } + logger.info(errorMsg) + throw InvalidRegistrationRequestException(errorMsg, reasonForUser) + } + } + + private fun validateRegistrationRequest(condition: Boolean, errorMsgFn: () -> String, reasonForUserFn: () -> String?) { + if (!condition) { + val errorMsg = errorMsgFn() + val reasonForUser = reasonForUserFn() + logger.info(errorMsg) + throw InvalidRegistrationRequestException(errorMsg, reasonForUser) } } private fun buildPendingMemberInfo(registrationRequest: RegistrationRequestDetails): SelfSignedMemberInfo { val memberContext = registrationRequest.memberProvidedContext.data.array().deserializeContext(keyValuePairListDeserializer) - validateRegistrationRequest(memberContext.isNotEmpty()) { - "Empty member context in the registration request." - } + validateRegistrationRequest(memberContext.isNotEmpty(), + DECLINED_REASON_EMPTY_REGISTRATION_CONTEXT, DECLINED_REASON_EMPTY_REGISTRATION_CONTEXT) val customFieldsValid = registrationContextCustomFieldsVerifier.verify(memberContext) - validateRegistrationRequest(customFieldsValid !is RegistrationContextCustomFieldsVerifier.Result.Failure) { - (customFieldsValid as RegistrationContextCustomFieldsVerifier.Result.Failure).reason - } + val errorMsgFn = { (customFieldsValid as RegistrationContextCustomFieldsVerifier.Result.Failure).reason } + validateRegistrationRequest(customFieldsValid !is RegistrationContextCustomFieldsVerifier.Result.Failure, errorMsgFn, errorMsgFn) val now = clock.instant().toString() val mgmContext = sortedMapOf( @@ -276,9 +286,7 @@ internal class StartRegistrationHandler( private fun getMGMMemberInfo(mgm: HoldingIdentity): MemberInfo { return memberTypeChecker.getMgmMemberInfo(mgm).apply { - validateRegistrationRequest(this != null) { - "Registration request is targeted at non-MGM holding identity." - } + validateRegistrationRequest(this != null, DECLINED_REASON_NOT_MGM_IDENTITY, DECLINED_REASON_NOT_MGM_IDENTITY) }!! } @@ -288,36 +296,39 @@ internal class StartRegistrationHandler( ?: throw MembershipRegistrationException("Could not read group parameters of the membership group '${member.groupId}'.") // If role is set to notary, notary details are specified member.notaryDetails?.let { notary -> - validateRegistrationRequest( - notary.keys.isNotEmpty() - ) { "Registering member has role set to 'notary', but has missing notary key details." } + validateRegistrationRequest(notary.keys.isNotEmpty(), + DECLINED_REASON_NOTARY_MISSING_NOTARY_DETAILS, DECLINED_REASON_NOTARY_MISSING_NOTARY_DETAILS) + notary.serviceProtocol?.let { - validateRegistrationRequest( - it.isNotBlank() - ) { "Registering member has specified an invalid notary service plugin type." } + validateRegistrationRequest(it.isNotBlank(), + DECLINED_REASON_INVALID_NOTARY_SERVICE_PLUGIN_TYPE, DECLINED_REASON_INVALID_NOTARY_SERVICE_PLUGIN_TYPE) } - // The notary service x500 name is different from the notary virtual node being registered. - validateRegistrationRequest( - member.name != notary.serviceName - ) { "The virtual node `${member.name}` and the notary service `${notary.serviceName}`" + + + val differentNotaryServiceVnodeNameFn = { "The virtual node `${member.name}` and the notary service `${notary.serviceName}`" + " name cannot be the same." } + // The notary service x500 name is different from the notary virtual node being registered. + validateRegistrationRequest(member.name != notary.serviceName, + differentNotaryServiceVnodeNameFn, differentNotaryServiceVnodeNameFn) + + val serviceNameExistsForOtherVnodeFn = + { "There is a virtual node having the same name as the notary service ${notary.serviceName}." } // The notary service x500 name is different from any existing virtual node x500 name (notary or otherwise). validateRegistrationRequest( membershipQueryClient.queryMemberInfo( mgmHoldingId, listOf(HoldingIdentity(notary.serviceName, member.groupId)) - ).getOrThrow().firstOrNull() == null - ) { "There is a virtual node having the same name as the notary service ${notary.serviceName}." } + ).getOrThrow().firstOrNull() == null, serviceNameExistsForOtherVnodeFn, { DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON }) + + val notaryServiceExistsFn = { "Notary service '${notary.serviceName}' already exists." } validateRegistrationRequest( groupReader.lookup().none { it.notaryDetails?.serviceName == notary.serviceName && it.name != member.name - } - ) { "Notary service '${notary.serviceName}' already exists." } + }, notaryServiceExistsFn, { DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON }) } - validateRegistrationRequest(groupParameters.notaries.none { it.name == member.name }) { + validateRegistrationRequest(groupParameters.notaries.none { it.name == member.name }, { "Registering member's name '${member.name}' is already in use as a notary service name." - } + }, { DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON }) } /** @@ -337,18 +348,18 @@ internal class StartRegistrationHandler( preAuthTokenId = it, viewInactive = false ).getOrThrow() - validateRegistrationRequest(result.isNotEmpty()) { + validateRegistrationRequest(result.isNotEmpty(), { logger.warn( "'${pendingMemberInfo.name}' in group '${pendingMemberInfo.groupId}' attempted to " + "register with invalid pre-auth token '$it'." ) "Registration attempted to use a pre-auth token which is " + "not currently active for this member." - } + }, { DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON }) result.first().ttl?.let { - validateRegistrationRequest(it >= clock.instant()) { - "Registration attempted to use a pre-auth token which has expired." - } + validateRegistrationRequest(it >= clock.instant(), + "Registration attempted to use a pre-auth token which has expired.", + DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON) } logger.info( "'${pendingMemberInfo.name}' in group '${pendingMemberInfo.groupId}' has provided " + @@ -356,22 +367,26 @@ internal class StartRegistrationHandler( ) } } catch (e: IllegalArgumentException) { + val reason = "Registration failed due to invalid format for the provided pre-auth token." e.mapToInvalidRegistrationRequestException( - "Registration failed due to invalid format for the provided pre-auth token." + reason, + reason ) } catch (e: MembershipQueryResult.QueryException) { e.mapToInvalidRegistrationRequestException( - "Registration failed due to failure to query configured pre-auth tokens." + "Registration failed due to failure to query configured pre-auth tokens.", + DECLINED_REASON_FOR_USER_INTERNAL_ERROR ) } catch (e: ContextDeserializationException) { e.mapToInvalidRegistrationRequestException( - "Registration failed due to failure when deserializing registration context." + "Registration failed due to failure when deserializing registration context.", + DECLINED_REASON_FOR_USER_INTERNAL_ERROR ) } } - private fun Exception.mapToInvalidRegistrationRequestException(message: String) { + private fun Exception.mapToInvalidRegistrationRequestException(message: String, reasonForUser: String?) { logger.info(message, this) - throw InvalidRegistrationRequestException(message) + throw InvalidRegistrationRequestException(message, reasonForUser) } } \ No newline at end of file diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/VerifyMemberHandler.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/VerifyMemberHandler.kt index b459ddba3a9..badfeb6ff4f 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/VerifyMemberHandler.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/VerifyMemberHandler.kt @@ -15,6 +15,7 @@ import net.corda.membership.impl.registration.dynamic.handler.MemberTypeChecker import net.corda.membership.impl.registration.dynamic.handler.MissingRegistrationStateException import net.corda.membership.impl.registration.dynamic.handler.RegistrationHandler import net.corda.membership.impl.registration.dynamic.handler.RegistrationHandlerResult +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_INTERNAL_ERROR import net.corda.membership.p2p.helpers.P2pRecordsFactory import net.corda.membership.p2p.helpers.P2pRecordsFactory.Companion.getTtlMinutes import net.corda.membership.p2p.helpers.TtlIdsFactory @@ -84,7 +85,7 @@ internal class VerifyMemberHandler( Schemas.Membership.REGISTRATION_COMMAND_TOPIC, key, RegistrationCommand( - DeclineRegistration(e.message) + DeclineRegistration(e.message, DECLINED_REASON_FOR_USER_INTERNAL_ERROR) ) ), ) diff --git a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/mgm/ExpirationProcessorImpl.kt b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/mgm/ExpirationProcessorImpl.kt index 56c05e8a7aa..a2cdcd44555 100644 --- a/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/mgm/ExpirationProcessorImpl.kt +++ b/components/membership/registration-impl/src/main/kotlin/net/corda/membership/impl/registration/dynamic/mgm/ExpirationProcessorImpl.kt @@ -19,6 +19,7 @@ import net.corda.lifecycle.StartEvent import net.corda.lifecycle.StopEvent import net.corda.lifecycle.TimerEvent import net.corda.membership.lib.MemberInfoExtension.Companion.isMgm +import net.corda.membership.lib.registration.DECLINED_REASON_FOR_USER_INTERNAL_ERROR import net.corda.membership.persistence.client.MembershipQueryClient import net.corda.membership.read.MembershipGroupReaderProvider import net.corda.membership.registration.ExpirationProcessor @@ -285,7 +286,8 @@ internal class ExpirationProcessorImpl internal constructor( key = "${id.x500Name}-${id.groupId}", value = RegistrationCommand( DeclineRegistration( - "Registration request stuck and expired." + "Registration request stuck and expired.", + DECLINED_REASON_FOR_USER_INTERNAL_ERROR ) ) ) diff --git a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandlerTest.kt b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandlerTest.kt index c335e1c57a8..f7acb9236d3 100644 --- a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandlerTest.kt +++ b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/member/PersistMemberRegistrationStateHandlerTest.kt @@ -37,11 +37,13 @@ class PersistMemberRegistrationStateHandlerTest { ) } doReturn operation } + private val reason = "some reason" val command = PersistMemberRegistrationState( HoldingIdentity("O=Alice, L=London, C=GB", "GroupId"), SetOwnRegistrationStatus( UUID(1,2).toString(), - RegistrationStatus.DECLINED + RegistrationStatus.DECLINED, + reason ) ) @@ -65,7 +67,8 @@ class PersistMemberRegistrationStateHandlerTest { verify(membershipPersistenceClient).setRegistrationRequestStatus( command.member.toCorda(), command.setStatusRequest.registrationId, - command.setStatusRequest.newStatus + command.setStatusRequest.newStatus, + reason ) } diff --git a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandlerTest.kt b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandlerTest.kt index 45939994ccd..a1b8197c292 100644 --- a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandlerTest.kt +++ b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ApproveRegistrationHandlerTest.kt @@ -171,7 +171,8 @@ class ApproveRegistrationHandlerTest { eq( SetOwnRegistrationStatus( registrationId, - RegistrationStatus.APPROVED + RegistrationStatus.APPROVED, + null ) ), anyOrNull(), @@ -257,7 +258,7 @@ class ApproveRegistrationHandlerTest { fun `invoke does not send registration status update message when status cannot be retrieved`() { val mockedBuilder = Mockito.mockStatic(VersionedMessageBuilder::class.java).also { it.`when` { - VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any()) + VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any(), any()) } doReturn null } diff --git a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandlerTest.kt b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandlerTest.kt index 64410c1f570..79bbbb89887 100644 --- a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandlerTest.kt +++ b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/DeclineRegistrationHandlerTest.kt @@ -96,6 +96,7 @@ class DeclineRegistrationHandlerTest { SetOwnRegistrationStatus( REGISTRATION_ID, RegistrationStatus.DECLINED, + null ) ), any(), @@ -154,7 +155,7 @@ class DeclineRegistrationHandlerTest { fun `handler does not send registration status update message when status cannot be retrieved`() { val mockedBuilder = Mockito.mockStatic(VersionedMessageBuilder::class.java).also { it.`when` { - VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any()) + VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any(), any()) } doReturn null } diff --git a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandlerTest.kt b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandlerTest.kt index 89d575efc67..ce3547b1ad3 100644 --- a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandlerTest.kt +++ b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/ProcessMemberVerificationResponseHandlerTest.kt @@ -484,7 +484,7 @@ class ProcessMemberVerificationResponseHandlerTest { val mockedBuilder = Mockito.mockStatic(VersionedMessageBuilder::class.java).also { it.`when` { - VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any()) + VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any(), any()) } doReturn null } diff --git a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandlerTest.kt b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandlerTest.kt index 8dd332fe089..5dabb18ddd6 100644 --- a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandlerTest.kt +++ b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/QueueRegistrationHandlerTest.kt @@ -174,6 +174,7 @@ class QueueRegistrationHandlerTest { SetOwnRegistrationStatus( registrationId, RegistrationStatus.RECEIVED_BY_MGM, + null ) ), eq(5), diff --git a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandlerTest.kt b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandlerTest.kt index 3d5ea8d70d2..211c9f2150b 100644 --- a/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandlerTest.kt +++ b/components/membership/registration-impl/src/test/kotlin/net/corda/membership/impl/registration/dynamic/handler/mgm/StartRegistrationHandlerTest.kt @@ -336,7 +336,7 @@ class StartRegistrationHandlerTest { fun `invoke does not send registration status update message when status cannot be retrieved`() { val mockedBuilder = Mockito.mockStatic(VersionedMessageBuilder::class.java).also { it.`when` { - VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any()) + VersionedMessageBuilder.retrieveRegistrationStatusMessage(any(), any(), any(), any()) } doReturn null } diff --git a/gradle.properties b/gradle.properties index 6f6487c371d..84f6f74a16b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -46,7 +46,7 @@ commonsTextVersion = 1.10.0 bouncycastleVersion=1.73 # Corda API libs revision (change in 4th digit indicates a breaking change) # Change to 5.0.0.xx-SNAPSHOT to pick up maven local published copy -cordaApiVersion=5.1.0.23-beta+ +cordaApiVersion=5.1.0.24-beta+ disruptorVersion=3.4.4 felixConfigAdminVersion=1.9.26 diff --git a/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/VersionedMessageBuilder.kt b/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/VersionedMessageBuilder.kt index 68ba6ac5699..4e0d45139df 100644 --- a/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/VersionedMessageBuilder.kt +++ b/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/VersionedMessageBuilder.kt @@ -8,12 +8,12 @@ object VersionedMessageBuilder { private val logger = LoggerFactory.getLogger("net.corda.membership.lib.VersionedMessageBuilder.kt") @JvmStatic - fun retrieveRegistrationStatusMessage(platformVersion: Int, registrationId: String, status: String) = + fun retrieveRegistrationStatusMessage(platformVersion: Int, registrationId: String, status: String, reason: String?) = try { if (platformVersion < 50100) { SetOwnRegistrationStatus(registrationId, RegistrationStatus.valueOf(status)) } else { - SetOwnRegistrationStatusV2(registrationId, RegistrationStatusV2.valueOf(status)) + SetOwnRegistrationStatusV2(registrationId, RegistrationStatusV2.valueOf(status), reason) } } catch (e: IllegalArgumentException) { logger.warn("Could not retrieve status '$status', returning null.") diff --git a/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/registration/RegistrationRequestConstants.kt b/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/registration/RegistrationRequestConstants.kt index 3e2fcde8219..9658134187c 100644 --- a/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/registration/RegistrationRequestConstants.kt +++ b/libs/membership/membership-common/src/main/kotlin/net/corda/membership/lib/registration/RegistrationRequestConstants.kt @@ -6,4 +6,28 @@ const val REGISTRATION_SIGNATURE = "corda.registration.request.signature" const val REGISTRATION_CONTEXT = "corda.registration.request.context" /** Key name for pre-auth token property. */ -const val PRE_AUTH_TOKEN = "corda.auth.token" \ No newline at end of file +const val PRE_AUTH_TOKEN = "corda.auth.token" + +/** + * Reasons for declination of a registration request. + */ +const val DECLINED_REASON_FOR_USER_INTERNAL_ERROR = "Internal error on the MGM side. " + + "Please reach out to the network operator to find out the reason the request was declined." +// The below should be used in scenarios where we don't want to leak sensitive information. +const val DECLINED_REASON_FOR_USER_GENERAL_INVALID_REASON = "Invalid request. " + + "Please reach out to the network operator to find out the reason the request was declined." +const val DECLINED_REASON_FOR_USER_GENERAL_MANUAL_DECLINED = "The request was manually declined by the network operator. " + + "Please reach out to them to find out the reason the request was declined." +const val DECLINED_REASON_EMPTY_REGISTRATION_CONTEXT = "Empty member context in the registration request." +const val DECLINED_REASON_NOT_MGM_IDENTITY = "Registration request is targeted at non-MGM holding identity." +const val DECLINED_REASON_NAME_IN_REQUEST_NOT_MATCHING_NAME_IN_P2P_MSG = + "MemberX500Name in registration request does not match member sending request over P2P." +const val DECLINED_REASON_SERIAL_NULL = "Serial on the registration request should not be null." +const val DECLINED_REASON_SERIAL_NEGATIVE = "Serial cannot be negative on the registration request." +const val DECLINED_REASON_RESISTRANT_IS_MGM = "Registration request is registering an MGM holding identity." +const val DECLINED_REASON_GROUP_ID_IN_REQUEST_NOT_MATCHING_TARGET = + "Group ID in registration request does not match the group ID of the target MGM." +const val DECLINED_REASON_NO_ENDPOINTS_SPECIFIED = "Registering member has not specified any endpoints" +const val DECLINED_REASON_NOTARY_MISSING_NOTARY_DETAILS = "Registering member has role set to 'notary', but has missing notary key details." +const val DECLINED_REASON_INVALID_NOTARY_SERVICE_PLUGIN_TYPE = "Registering member has specified an invalid notary service plugin type." +const val DECLINED_REASON_COMMS_ISSUE = "MGM could not establish communication back to registering member." \ No newline at end of file diff --git a/libs/membership/membership-common/src/test/kotlin/net/corda/membership/lib/VersionedMessageBuilderTest.kt b/libs/membership/membership-common/src/test/kotlin/net/corda/membership/lib/VersionedMessageBuilderTest.kt index 1b6fea71267..42c05c84179 100644 --- a/libs/membership/membership-common/src/test/kotlin/net/corda/membership/lib/VersionedMessageBuilderTest.kt +++ b/libs/membership/membership-common/src/test/kotlin/net/corda/membership/lib/VersionedMessageBuilderTest.kt @@ -15,7 +15,7 @@ class VersionedMessageBuilderTest { @Test fun `SetOwnRegistrationStatus version 1 messages are built as expected`() { RegistrationStatus.values().forEach { status -> - with(retrieveRegistrationStatusMessage(50001, registrationId, status.name)) { + with(retrieveRegistrationStatusMessage(50001, registrationId, status.name, "some reason")) { assertThat(this).isInstanceOf(SetOwnRegistrationStatus::class.java) val message = this as SetOwnRegistrationStatus assertThat(message.newStatus).isInstanceOf(RegistrationStatus::class.java) @@ -27,17 +27,19 @@ class VersionedMessageBuilderTest { @Test fun `SetOwnRegistrationStatus version 2 messages are built as expected`() { RegistrationStatusV2.values().forEach { status -> - with(retrieveRegistrationStatusMessage(50101, registrationId, status.name)) { + val reason = "some reason" + with(retrieveRegistrationStatusMessage(50101, registrationId, status.name, "some reason")) { assertThat(this).isInstanceOf(SetOwnRegistrationStatusV2::class.java) val message = this as SetOwnRegistrationStatusV2 assertThat(message.newStatus).isInstanceOf(RegistrationStatusV2::class.java) assertThat(message.newStatus.name).isEqualTo(status.name) + assertThat(message.reason).isEqualTo(reason) } } } @Test fun `null is returned when not expected status needs to be distributed`() { - assertThat(retrieveRegistrationStatusMessage(50101, registrationId, "dummyStatus")).isNull() + assertThat(retrieveRegistrationStatusMessage(50101, registrationId, "dummyStatus", "some reason")).isNull() } } \ No newline at end of file