From 0777be3d6c474ff62ea1faed91995b91cae44bba Mon Sep 17 00:00:00 2001 From: gbrodman Date: Fri, 5 Dec 2025 15:22:15 -0500 Subject: [PATCH] Allow superuser ext to override client/server transfer prohibited (#2890) The superuser can remove/add those statuses anyway, so there's not really any point. This also saves us trouble if we need to do a BTAPPA transfer. --- .../domain/DomainTransferRequestFlow.java | 10 ++-- .../domain/DomainTransferRequestFlowTest.java | 50 +++++++++++++++++++ 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java index 071d590d2..bd92e541b 100644 --- a/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java +++ b/core/src/main/java/google/registry/flows/domain/DomainTransferRequestFlow.java @@ -133,10 +133,9 @@ import org.joda.time.DateTime; @ReportingSpec(ActivityReportField.DOMAIN_TRANSFER_REQUEST) public final class DomainTransferRequestFlow implements MutatingFlow { - private static final ImmutableSet DISALLOWED_STATUSES = ImmutableSet.of( - StatusValue.CLIENT_TRANSFER_PROHIBITED, - StatusValue.PENDING_DELETE, - StatusValue.SERVER_TRANSFER_PROHIBITED); + private static final ImmutableSet NON_SUPERUSER_DISALLOWED_STATUSES = + ImmutableSet.of( + StatusValue.CLIENT_TRANSFER_PROHIBITED, StatusValue.SERVER_TRANSFER_PROHIBITED); @Inject ResourceCommand resourceCommand; @Inject ExtensionManager extensionManager; @@ -299,8 +298,9 @@ public final class DomainTransferRequestFlow implements MutatingFlow { DateTime now, Optional superuserExtension) throws EppException { - verifyNoDisallowedStatuses(existingDomain, DISALLOWED_STATUSES); + verifyNoDisallowedStatuses(existingDomain, ImmutableSet.of(StatusValue.PENDING_DELETE)); if (!isSuperuser) { + verifyNoDisallowedStatuses(existingDomain, NON_SUPERUSER_DISALLOWED_STATUSES); verifyAuthInfoPresentForResourceTransfer(authInfo); verifyAuthInfo(authInfo.get(), existingDomain); } diff --git a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java index d649d555d..f0f33b871 100644 --- a/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java +++ b/core/src/test/java/google/registry/flows/domain/DomainTransferRequestFlowTest.java @@ -1004,6 +1004,40 @@ class DomainTransferRequestFlowTest ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "5"))); } + @Test + void testSuccess_superuserExtension_clientTransferProhibited() throws Exception { + setupDomain("example", "tld"); + eppRequestSource = EppRequestSource.TOOL; + domain = + persistResource( + domain.asBuilder().addStatusValue(StatusValue.CLIENT_TRANSFER_PROHIBITED).build()); + doSuccessfulSuperuserExtensionTest( + "domain_transfer_request_superuser_extension.xml", + "domain_transfer_request_response_su_ext_zero_period_zero_transfer_length.xml", + domain.getRegistrationExpirationTime().plusYears(0), + ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"), + Optional.empty(), + Period.create(0, Unit.YEARS), + Duration.ZERO); + } + + @Test + void testSuccess_superuserExtension_serverTransferProhibited() throws Exception { + setupDomain("example", "tld"); + eppRequestSource = EppRequestSource.TOOL; + domain = + persistResource( + domain.asBuilder().addStatusValue(StatusValue.SERVER_TRANSFER_PROHIBITED).build()); + doSuccessfulSuperuserExtensionTest( + "domain_transfer_request_superuser_extension.xml", + "domain_transfer_request_response_su_ext_zero_period_zero_transfer_length.xml", + domain.getRegistrationExpirationTime().plusYears(0), + ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"), + Optional.empty(), + Period.create(0, Unit.YEARS), + Duration.ZERO); + } + @Test void testSuccess_cappedExpiration() throws Exception { setupDomain("example", "tld"); @@ -1809,6 +1843,22 @@ class DomainTransferRequestFlowTest assertThat(thrown).hasMessageThat().contains("pendingDelete"); } + @Test + void testFailure_pendingDelete_evenWhenSuperuser() { + setupDomain("example", "tld"); + eppRequestSource = EppRequestSource.TOOL; + domain = persistResource(domain.asBuilder().addStatusValue(StatusValue.PENDING_DELETE).build()); + ResourceStatusProhibitsOperationException thrown = + assertThrows( + ResourceStatusProhibitsOperationException.class, + () -> + runTest( + "domain_transfer_request_superuser_extension.xml", + UserPrivileges.SUPERUSER, + ImmutableMap.of("PERIOD", "0", "AUTOMATIC_TRANSFER_LENGTH", "0"))); + assertThat(thrown).hasMessageThat().contains("pendingDelete"); + } + @Test void testIcannActivityReportField_getsLogged() throws Exception { setupDomain("example", "tld");