From 8eb8c810e87e95afae3a0638ffa2454ef2af3ac7 Mon Sep 17 00:00:00 2001 From: sarahcaseybot Date: Mon, 16 Aug 2021 13:21:00 -0400 Subject: [PATCH] Remove DeleteEntityAction (#1282) --- .../module/tools/ToolsRequestComponent.java | 2 - .../tools/server/DeleteEntityAction.java | 129 ------------------ .../tools/server/DeleteEntityActionTest.java | 100 -------------- .../registry/module/tools/tools_routing.txt | 1 - 4 files changed, 232 deletions(-) delete mode 100644 core/src/main/java/google/registry/tools/server/DeleteEntityAction.java delete mode 100644 core/src/test/java/google/registry/tools/server/DeleteEntityActionTest.java diff --git a/core/src/main/java/google/registry/module/tools/ToolsRequestComponent.java b/core/src/main/java/google/registry/module/tools/ToolsRequestComponent.java index db56afcd0..e8c819f03 100644 --- a/core/src/main/java/google/registry/module/tools/ToolsRequestComponent.java +++ b/core/src/main/java/google/registry/module/tools/ToolsRequestComponent.java @@ -31,7 +31,6 @@ import google.registry.request.RequestModule; import google.registry.request.RequestScope; import google.registry.tools.server.CreateGroupsAction; import google.registry.tools.server.CreatePremiumListAction; -import google.registry.tools.server.DeleteEntityAction; import google.registry.tools.server.GenerateZoneFilesAction; import google.registry.tools.server.KillAllCommitLogsAction; import google.registry.tools.server.KillAllEppResourcesAction; @@ -63,7 +62,6 @@ import google.registry.tools.server.VerifyOteAction; interface ToolsRequestComponent { CreateGroupsAction createGroupsAction(); CreatePremiumListAction createPremiumListAction(); - DeleteEntityAction deleteEntityAction(); EppToolAction eppToolAction(); FlowComponent.Builder flowComponentBuilder(); GenerateZoneFilesAction generateZoneFilesAction(); diff --git a/core/src/main/java/google/registry/tools/server/DeleteEntityAction.java b/core/src/main/java/google/registry/tools/server/DeleteEntityAction.java deleted file mode 100644 index 9f8c6807f..000000000 --- a/core/src/main/java/google/registry/tools/server/DeleteEntityAction.java +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.tools.server; - -import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService; -import static com.googlecode.objectify.Key.create; -import static google.registry.model.ofy.ObjectifyService.auditedOfy; -import static google.registry.persistence.transaction.TransactionManagerFactory.tm; - -import com.google.appengine.api.datastore.Entity; -import com.google.appengine.api.datastore.EntityNotFoundException; -import com.google.appengine.api.datastore.Key; -import com.google.appengine.api.datastore.KeyFactory; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.flogger.FluentLogger; -import com.googlecode.objectify.impl.EntityMetadata; -import google.registry.request.Action; -import google.registry.request.HttpException.BadRequestException; -import google.registry.request.Parameter; -import google.registry.request.Response; -import google.registry.request.auth.Auth; -import java.util.Optional; -import javax.inject.Inject; - -/** - * An action to delete entities in Datastore specified by raw key ids, which can be found in - * Datastore Viewer in the AppEngine console - it's the really long alphanumeric key that is labeled - * "Entity key" on the page for an individual entity. - * - *

rawKeys is the only required parameter. It is a comma-delimited list of Strings. - * - *

WARNING: This servlet can be dangerous if used incorrectly as it can bypass checks on - * deletion (including whether the entity is referenced by other entities) and it does not write - * commit log entries for non-registered types. It should mainly be used for deleting testing or - * malformed data that cannot be properly deleted using existing tools. Generally, if there already - * exists an entity-specific deletion command, then use that one instead. - */ -@Action( - service = Action.Service.TOOLS, - path = DeleteEntityAction.PATH, - auth = Auth.AUTH_INTERNAL_OR_ADMIN) -public class DeleteEntityAction implements Runnable { - - private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - - public static final String PATH = "/_dr/admin/deleteEntity"; - public static final String PARAM_RAW_KEYS = "rawKeys"; - - private final String rawKeys; - private final Response response; - - @Inject - DeleteEntityAction(@Parameter(PARAM_RAW_KEYS) String rawKeys, Response response) { - this.rawKeys = rawKeys; - this.response = response; - } - - @Override - public void run() { - // Get raw key strings from request - ImmutableList.Builder ofyDeletionsBuilder = new ImmutableList.Builder<>(); - ImmutableList.Builder rawDeletionsBuilder = new ImmutableList.Builder<>(); - for (String rawKeyString : Splitter.on(',').split(rawKeys)) { - // Convert raw keys string to real keys. Try to load it from Objectify if it's a registered - // type, and fall back to DatastoreService if its not registered. - Key rawKey = KeyFactory.stringToKey(rawKeyString); - Optional ofyEntity = loadOfyEntity(rawKey); - if (ofyEntity.isPresent()) { - ofyDeletionsBuilder.add(ofyEntity.get()); - continue; - } - Optional rawEntity = loadRawEntity(rawKey); - if (rawEntity.isPresent()) { - rawDeletionsBuilder.add(rawEntity.get().getKey()); - continue; - } - // The entity could not be found by either Objectify or the Datastore service - throw new BadRequestException("Could not find entity with key " + rawKeyString); - } - // Delete raw entities. - ImmutableList rawDeletions = rawDeletionsBuilder.build(); - getDatastoreService().delete(rawDeletions); - // Delete ofy entities. - final ImmutableList ofyDeletions = ofyDeletionsBuilder.build(); - tm().transactNew(() -> auditedOfy().delete().entities(ofyDeletions).now()); - String message = String.format( - "Deleted %d raw entities and %d registered entities", - rawDeletions.size(), - ofyDeletions.size()); - logger.atInfo().log(message); - response.setPayload(message); - } - - private Optional loadOfyEntity(Key rawKey) { - try { - EntityMetadata metadata = auditedOfy().factory().getMetadata(rawKey.getKind()); - return Optional.ofNullable( - metadata == null ? null : auditedOfy().load().key(create(rawKey)).now()); - } catch (Throwable e) { - logger.atWarning().withCause(e).log( - "Could not load entity with key %s using Objectify; falling back to raw Datastore.", - rawKey); - return Optional.empty(); - } - } - - private Optional loadRawEntity(Key rawKey) { - try { - return Optional.ofNullable(getDatastoreService().get(rawKey)); - } catch (EntityNotFoundException e) { - logger.atWarning().withCause(e).log( - "Could not load entity from Datastore service with key %s", rawKey); - return Optional.empty(); - } - } -} diff --git a/core/src/test/java/google/registry/tools/server/DeleteEntityActionTest.java b/core/src/test/java/google/registry/tools/server/DeleteEntityActionTest.java deleted file mode 100644 index 0b8d8f2e1..000000000 --- a/core/src/test/java/google/registry/tools/server/DeleteEntityActionTest.java +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2017 The Nomulus Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package google.registry.tools.server; - -import static com.google.appengine.api.datastore.DatastoreServiceFactory.getDatastoreService; -import static com.google.common.truth.Truth.assertThat; -import static com.googlecode.objectify.Key.create; -import static google.registry.model.ofy.ObjectifyService.auditedOfy; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import com.google.appengine.api.datastore.Entity; -import com.google.appengine.api.datastore.KeyFactory; -import google.registry.model.tld.label.ReservedList; -import google.registry.request.HttpException.BadRequestException; -import google.registry.testing.AppEngineExtension; -import google.registry.testing.FakeResponse; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; - -/** Unit tests for {@link DeleteEntityAction}. */ -class DeleteEntityActionTest { - - @RegisterExtension - final AppEngineExtension appEngine = - AppEngineExtension.builder().withDatastoreAndCloudSql().build(); - - private FakeResponse response = new FakeResponse(); - - @Test - void test_deleteSingleRawEntitySuccessfully() { - Entity entity = new Entity("single", "raw"); - getDatastoreService().put(entity); - new DeleteEntityAction(KeyFactory.keyToString(entity.getKey()), response).run(); - assertThat(response.getPayload()).isEqualTo("Deleted 1 raw entities and 0 registered entities"); - } - - @Test - void test_deleteSingleRegisteredEntitySuccessfully() { - ReservedList ofyEntity = new ReservedList.Builder().setName("foo").build(); - auditedOfy().saveWithoutBackup().entity(ofyEntity).now(); - new DeleteEntityAction(KeyFactory.keyToString(create(ofyEntity).getRaw()), response).run(); - assertThat(response.getPayload()).isEqualTo("Deleted 0 raw entities and 1 registered entities"); - } - - @Test - void test_deletePolymorphicEntity_fallbackSucceedsForUnregisteredType() { - Entity entity = new Entity("single", "raw"); - entity.setIndexedProperty("^d", "UnregType"); - getDatastoreService().put(entity); - new DeleteEntityAction(KeyFactory.keyToString(entity.getKey()), response).run(); - assertThat(response.getPayload()).isEqualTo("Deleted 1 raw entities and 0 registered entities"); - } - - @Test - void test_deleteOneRawEntityAndOneRegisteredEntitySuccessfully() { - Entity entity = new Entity("first", "raw"); - getDatastoreService().put(entity); - String rawKey = KeyFactory.keyToString(entity.getKey()); - ReservedList ofyEntity = new ReservedList.Builder().setName("registered").build(); - auditedOfy().saveWithoutBackup().entity(ofyEntity).now(); - String ofyKey = KeyFactory.keyToString(create(ofyEntity).getRaw()); - new DeleteEntityAction(String.format("%s,%s", rawKey, ofyKey), response).run(); - assertThat(response.getPayload()).isEqualTo("Deleted 1 raw entities and 1 registered entities"); - } - - @Test - void test_deleteNonExistentEntityRepliesWithError() { - Entity entity = new Entity("not", "here"); - String rawKey = KeyFactory.keyToString(entity.getKey()); - BadRequestException thrown = - assertThrows( - BadRequestException.class, () -> new DeleteEntityAction(rawKey, response).run()); - assertThat(thrown).hasMessageThat().contains("Could not find entity with key " + rawKey); - } - - @Test - void test_deleteOneEntityAndNonExistentEntityRepliesWithError() { - ReservedList ofyEntity = new ReservedList.Builder().setName("first_registered").build(); - auditedOfy().saveWithoutBackup().entity(ofyEntity).now(); - String ofyKey = KeyFactory.keyToString(create(ofyEntity).getRaw()); - String rawKey = KeyFactory.keyToString(new Entity("non", "existent").getKey()); - BadRequestException thrown = - assertThrows( - BadRequestException.class, - () -> new DeleteEntityAction(String.format("%s,%s", ofyKey, rawKey), response).run()); - assertThat(thrown).hasMessageThat().contains("Could not find entity with key " + rawKey); - } -} diff --git a/core/src/test/resources/google/registry/module/tools/tools_routing.txt b/core/src/test/resources/google/registry/module/tools/tools_routing.txt index 71701edfd..dfd61aa06 100644 --- a/core/src/test/resources/google/registry/module/tools/tools_routing.txt +++ b/core/src/test/resources/google/registry/module/tools/tools_routing.txt @@ -1,7 +1,6 @@ PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY /_dr/admin/createGroups CreateGroupsAction POST n INTERNAL,API APP ADMIN /_dr/admin/createPremiumList CreatePremiumListAction POST n INTERNAL,API APP ADMIN -/_dr/admin/deleteEntity DeleteEntityAction GET n INTERNAL,API APP ADMIN /_dr/admin/list/domains ListDomainsAction GET,POST n INTERNAL,API APP ADMIN /_dr/admin/list/hosts ListHostsAction GET,POST n INTERNAL,API APP ADMIN /_dr/admin/list/premiumLists ListPremiumListsAction GET,POST n INTERNAL,API APP ADMIN