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