diff --git a/entity.links.task.yml b/entity.links.task.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8116ae3260ec83c3d5b0eed9df15d359e614ec9d
--- /dev/null
+++ b/entity.links.task.yml
@@ -0,0 +1,3 @@
+entity.revisions_overview:
+  deriver: 'Drupal\entity\Plugin\Derivative\RevisionsOverviewDeriver'
+  weight: 100
diff --git a/src/Access/EntityRevisionRouteAccessChecker.php b/src/Access/EntityRevisionRouteAccessChecker.php
index 8a4184f6c1c76d49da0b2c6c872671ca88e33476..f4f8e1de0ca41a5cf201c18210b32b657df72f73 100644
--- a/src/Access/EntityRevisionRouteAccessChecker.php
+++ b/src/Access/EntityRevisionRouteAccessChecker.php
@@ -11,7 +11,6 @@ use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Entity\RevisionableInterface;
 use Drupal\Core\Routing\Access\AccessInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -48,10 +47,17 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
    * {@inheritdoc}
    */
   public function access(Route $route, AccountInterface $account, Request $request) {
-    $_entity_revision = $request->attributes->get('_entity_revision');
     $operation = $route->getRequirement('_entity_access_revision');
     list(, $operation) = explode('.', $operation, 2);
-    return AccessResult::allowedIf($_entity_revision && $this->checkAccess($_entity_revision, $account, $operation))->cachePerPermissions();
+
+    if ($operation === 'list') {
+      $_entity = $request->attributes->get('_entity', $request->attributes->get($route->getOption('entity_type_id')));
+      return AccessResult::allowedIf($this->checkAccess($_entity, $account, $operation))->cachePerPermissions();
+    }
+    else {
+      $_entity_revision = $request->attributes->get('_entity_revision');
+      return AccessResult::allowedIf($_entity_revision && $this->checkAccess($_entity_revision, $account, $operation))->cachePerPermissions();
+    }
   }
 
   protected function checkAccess(ContentEntityInterface $entity, AccountInterface $account, $operation = 'view') {
@@ -64,12 +70,14 @@ class EntityRevisionRouteAccessChecker implements AccessInterface {
 
     $map = [
       'view' => "view all $entity_type_id revisions",
+      'list' => "view all $entity_type_id revisions",
       'update' => "revert all $entity_type_id revisions",
       'delete' => "delete all $entity_type_id revisions",
     ];
     $bundle = $entity->bundle();
     $type_map = [
       'view' => "view $entity_type_id $bundle revisions",
+      'list' => "view $entity_type_id $bundle revisions",
       'update' => "revert $entity_type_id $bundle revisions",
       'delete' => "delete $entity_type_id $bundle revisions",
     ];
diff --git a/src/Controller/RevisionControllerTrait.php b/src/Controller/RevisionControllerTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..5c3319ccdff5b825bbca976cdb2a77932aa5b0f7
--- /dev/null
+++ b/src/Controller/RevisionControllerTrait.php
@@ -0,0 +1,199 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Controller\RevisionControllerTrait.
+ */
+
+namespace Drupal\entity\Controller;
+
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Defines a trait for common revision UI functionality.
+ */
+trait RevisionControllerTrait {
+
+  /**
+   * Returns the entity type manager.
+   *
+   * @return \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  abstract protected function entityTypeManager();
+
+  /**
+   * Returns the langauge manager.
+   *
+   * @return \Drupal\Core\Language\LanguageManagerInterface
+   */
+  public abstract function languageManager();
+
+  /**
+   * Determines if the user has permission to revert revisions.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to check revert access for.
+   *
+   * @return bool
+   *   TRUE if the user has revert access.
+   */
+  abstract protected function hasRevertRevisionAccess(EntityInterface $entity);
+
+  /**
+   * Determines if the user has permission to delete revisions.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity to check delete revision access for.
+   *
+   * @return bool
+   *   TRUE if the user has delete revision access.
+   */
+  abstract protected function hasDeleteRevisionAccess(EntityInterface $entity);
+
+  /**
+   * Builds a link to revert an entity revision.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity_revision
+   *   The entity to build a revert revision link for.
+   *
+   * @return array
+   *   A link render array.
+   *
+   */
+  abstract protected function buildRevertRevisionLink(EntityInterface $entity_revision);
+
+  /**
+   * Builds a link to delete an entity revision.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity_revision
+   *   The entity to build a delete revision link for.
+   *
+   * @return array
+   *   A link render array.
+   */
+  abstract protected function buildDeleteRevisionLink(EntityInterface $entity_revision);
+
+  /**
+   * Returns a string providing details of the revision.
+   *
+   * E.g. Node describes its revisions using {date} by {username}. For the
+   *   non-current revision, it also provides a link to view that revision.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface $revision
+   *   The entity revision.
+   * @param bool $is_current
+   *   TRUE if the revision is the current revision.
+   *
+   * @return string
+   *   Returns a string to provide the details of the revision.
+   */
+  abstract protected function getRevisionDescription(ContentEntityInterface $revision, $is_current = FALSE);
+
+  /**
+   * Loads all revision IDs of an entity sorted by revision ID descending.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   The entity.
+   *
+   * @return mixed[]
+   */
+  protected function revisionIds(ContentEntityInterface $entity) {
+    $entity_type = $entity->getEntityType();
+    $result = $this->entityTypeManager()->getStorage($entity_type->id())->getQuery()
+      ->allRevisions()
+      ->condition($entity_type->getKey('id'), $entity->id())
+      ->sort($entity_type->getKey('revision'), 'DESC')
+      ->execute();
+    return array_keys($result);
+  }
+
+  /**
+   * Generates an overview table of older revisions of an entity.
+   *
+   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
+   *   An entity object.
+   *
+   * @return array
+   *   A render array.
+   */
+  protected function revisionOverview(ContentEntityInterface $entity) {
+    $langcode = $this->languageManager()
+      ->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
+      ->getId();
+    $entity_storage = $this->entityTypeManager()
+      ->getStorage($entity->getEntityTypeId());
+
+    $header = [$this->t('Revision'), $this->t('Operations')];
+    $rows = [];
+
+    $revision_ids = $this->revisionIds($entity);
+    // @todo Expand the entity storage to load multiple revisions.
+    $entity_revisions = array_combine($revision_ids, array_map(function($vid) use ($entity_storage) {
+      return $entity_storage->loadRevision($vid);
+      }, $revision_ids));
+
+    foreach ($entity_revisions as $revision) {
+      $row = [];
+      /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */
+      if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)
+          ->isRevisionTranslationAffected()
+      ) {
+        $row[] = $this->getRevisionDescription($revision, $revision->isDefaultRevision());
+
+        if ($revision->isDefaultRevision()) {
+          $row[] = [
+            'data' => [
+              '#prefix' => '<em>',
+              '#markup' => $this->t('Current revision'),
+              '#suffix' => '</em>',
+            ],
+          ];
+          foreach ($row as &$current) {
+            $current['class'] = ['revision-current'];
+          }
+        }
+        else {
+          $row[] = $this->getOperationLinks($revision);
+        }
+      }
+
+      $rows[] = $row;
+    }
+
+    $build[$entity->getEntityTypeId() . '_revisions_table'] = [
+      '#theme' => 'table',
+      '#rows' => $rows,
+      '#header' => $header,
+    ];
+
+    // We have no clue about caching yet.
+    $build['#cache']['max-age'] = 0;
+
+    return $build;
+  }
+
+  /**
+   * Get the links of the operations for an entity revision.
+   *
+   * @param \Drupal\Core\Entity\EntityInterface $entity_revision
+   *   The entity to build the revision links for.
+   *
+   * @return array
+   *   The operation links.
+   */
+  protected function getOperationLinks(EntityInterface $entity_revision) {
+    $links = [];
+    if ($this->hasRevertRevisionAccess($entity_revision)) {
+      $links['revert'] = $this->buildRevertRevisionLink($entity_revision);
+    }
+
+    if ($this->hasDeleteRevisionAccess($entity_revision)) {
+      $links['delete'] = $this->buildDeleteRevisionLink($entity_revision);
+    }
+
+    return array_filter($links);
+  }
+
+}
diff --git a/src/Controller/RevisionOverviewController.php b/src/Controller/RevisionOverviewController.php
new file mode 100644
index 0000000000000000000000000000000000000000..5629c5af8e0d8e6a46b330ec64107da63c304abe
--- /dev/null
+++ b/src/Controller/RevisionOverviewController.php
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Controller\RevisionOverviewController.
+ */
+
+namespace Drupal\entity\Controller;
+
+use Drupal\Component\Utility\Xss;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\Datetime\DateFormatterInterface;
+use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\entity\Revision\EntityRevisionLogInterface;
+use Drupal\user\EntityOwnerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a controller which shows the revision history.
+ *
+ * This controller leverages the revision controller trait, which is agnostic to
+ * any entity type, by using the new interface
+ * \Drupal\entity\Revision\EntityRevisionLogInterface.
+ */
+class RevisionOverviewController extends ControllerBase {
+
+  use RevisionControllerTrait;
+
+  /**
+   * The date formatter.
+   *
+   * @var \Drupal\Core\Datetime\DateFormatterInterface
+   */
+  protected $dateFormatter;
+
+  /**
+   * Creates a new RevisionOverviewController instance.
+   *
+   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
+   *   The date formatter.
+   */
+  public function __construct(DateFormatterInterface $date_formatter) {
+    $this->dateFormatter = $date_formatter;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('date.formatter'));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function hasDeleteRevisionAccess(EntityInterface $entity) {
+    return $this->currentUser()->hasPermission("delete all {$entity->id()} revisions");
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function buildRevertRevisionLink(EntityInterface $entity_revision) {
+    if ($entity_revision->hasLinkTemplate('revision-revert')) {
+      return [
+        'title' => t('Revert'),
+        'url' => $entity_revision->toUrl('revision-revert'),
+      ];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function buildDeleteRevisionLink(EntityInterface $entity_revision) {
+    if ($entity_revision->hasLinkTemplate('revision-delete')) {
+      return [
+        'title' => t('Delete'),
+        'url' => $entity_revision->toUrl('revision-delete'),
+      ];
+    }
+  }
+
+  /**
+   * Generates an overview table of older revisions of an entity.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match.
+   *
+   * @return array
+   *   A render array.
+   */
+  public function revisionOverviewController(RouteMatchInterface $route_match) {
+    return $this->revisionOverview($route_match->getParameter($route_match->getRouteObject()->getOption('entity_type_id')));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRevisionDescription(ContentEntityInterface $revision, $is_default = FALSE) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface|\Drupal\user\EntityOwnerInterface|\Drupal\entity\Revision\EntityRevisionLogInterface $revision */
+
+    if ($revision instanceof EntityRevisionLogInterface) {
+      // Use revision link to link to revisions that are not active.
+      $date = $this->dateFormatter->format($revision->getRevisionCreationTime(), 'short');
+      $link = $revision->toLink($date, 'revision');
+
+      $username = [
+        '#theme' => 'username',
+        '#account' => $revision->getRevisionUser(),
+      ];
+    }
+    else {
+      $link = $revision->toLink($revision->label(), 'revision');
+      $username = '';
+
+    }
+
+    $markup = '';
+    if ($revision instanceof EntityRevisionLogInterface) {
+      $markup = $revision->getRevisionLogMessage();
+    }
+
+    if ($username) {
+      $template = '{% trans %}{{ date }} by {{ username }}{% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}';
+    }
+    else {
+      $template = '{% trans %} {{ date }} {% endtrans %}{% if message %}<p class="revision-log">{{ message }}</p>{% endif %}';
+    }
+
+    $column = [
+      'data' => [
+        '#type' => 'inline_template',
+        '#template' => $template,
+        '#context' => [
+          'date' => $link->toString(),
+          'username' => $username,
+          'message' => ['#markup' => $markup, '#allowed_tags' => Xss::getHtmlTagList()],
+        ],
+      ],
+    ];
+    return $column;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function hasRevertRevisionAccess(EntityInterface $entity) {
+    return AccessResult::allowedIfHasPermission($this->currentUser(), "revert all {$entity->getEntityTypeId()} revisions")->orIf(
+      AccessResult::allowedIfHasPermission($this->currentUser(), "revert {$entity->bundle()} {$entity->getEntityTypeId()} revisions")
+    );
+  }
+
+}
diff --git a/src/Plugin/Derivative/RevisionsOverviewDeriver.php b/src/Plugin/Derivative/RevisionsOverviewDeriver.php
new file mode 100644
index 0000000000000000000000000000000000000000..b7e308bfc2f0e17e72709499f9feb2cfe51cd285
--- /dev/null
+++ b/src/Plugin/Derivative/RevisionsOverviewDeriver.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\entity\Plugin\Derivative\RevisionsOverviewDeriver.
+ */
+
+namespace Drupal\entity\Plugin\Derivative;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides local tasks for the revision overview.
+ */
+class RevisionsOverviewDeriver extends DeriverBase implements ContainerDeriverInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * Creates a new RevisionsOverviewDeriver instance.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
+   *   The entity type manager.
+   */
+  public function __construct(\Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager) {
+    $this->entityTypeManager = $entityTypeManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, $base_plugin_id) {
+    return new static(
+      $container->get('entity_type.manager')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDerivativeDefinitions($base_plugin_definition) {
+    $exclude = ['node'];
+
+    $this->derivatives = [];
+    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
+      if (in_array($entity_type_id, $exclude)) {
+        continue;
+      }
+
+      if (!$entity_type->hasLinkTemplate('version-history')) {
+        continue;
+      }
+
+      $this->derivatives[$entity_type_id] = [
+        'route_name' => "entity.$entity_type_id.version_history",
+        'title' => 'Revisions',
+        'base_route' => "entity.$entity_type_id.edit_form",
+        'weight' => 20,
+      ] + $base_plugin_definition;
+    }
+
+    return parent::getDerivativeDefinitions($base_plugin_definition);
+  }
+
+}
diff --git a/src/Routing/RevisionRouteProvider.php b/src/Routing/RevisionRouteProvider.php
index d539c6150b6083964ad09fcb87f688ff1f2b223b..33d2766f9d3af38c54c6abe78705b7b7dad3a9cf 100644
--- a/src/Routing/RevisionRouteProvider.php
+++ b/src/Routing/RevisionRouteProvider.php
@@ -30,6 +30,10 @@ class RevisionRouteProvider implements EntityRouteProviderInterface {
       $collection->add("entity.$entity_type_id.revision_revert_form", $view_route);
     }
 
+    if ($view_route = $this->getRevisionHistoryRoute($entity_type)) {
+      $collection->add("entity.$entity_type_id.version_history", $view_route);
+    }
+
     return $collection;
   }
 
@@ -97,4 +101,32 @@ class RevisionRouteProvider implements EntityRouteProviderInterface {
     }
   }
 
+  /**
+   * Gets the entity revision version history route.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The generated route, if available.
+   */
+  protected function getRevisionHistoryRoute($entity_type) {
+    if ($entity_type->hasLinkTemplate('version-history')) {
+      $entity_type_id = $entity_type->id();
+      $route = new Route($entity_type->getLinkTemplate('version-history'));
+      $route->addDefaults([
+        '_controller' => '\Drupal\entity\Controller\RevisionOverviewController::revisionOverviewController',
+        '_title' => 'Revisions',
+      ]);
+      $route->setRequirement('_entity_access_revision', "$entity_type_id.list");
+      $route->setOption('entity_type_id', $entity_type->id());
+      $route->setOption('parameters', [
+        $entity_type->id() => [
+          'type' => 'entity:' . $entity_type->id(),
+        ],
+      ]);
+      return $route;
+    }
+  }
+
 }
diff --git a/tests/Kernel/RevisionOverviewIntegrationTest.php b/tests/Kernel/RevisionOverviewIntegrationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ab784d8225594e82e9c1379801e9a6cdf67b39b
--- /dev/null
+++ b/tests/Kernel/RevisionOverviewIntegrationTest.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\entity\Kernel\RevisionOverviewIntegrationTest.
+ */
+
+namespace Drupal\Tests\entity\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Tests some integration of the revision overview:
+ *
+ * - Are the routes added properly.
+ * - Are the local tasks added properly.
+ */
+class RevisionOverviewIntegrationTest extends KernelTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node', 'entity_module_test', 'entity', 'user', 'system'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installSchema('system', 'router');
+
+    \Drupal::service('router.builder')->rebuild();
+  }
+
+  public function testIntegration() {
+    /** @var \Drupal\Core\Menu\LocalTaskManagerInterface $local_tasks_manager */
+    $local_tasks_manager = \Drupal::service('plugin.manager.menu.local_task');
+
+    $tasks = $local_tasks_manager->getDefinitions();
+    $this->assertArrayHasKey('entity.revisions_overview:entity_test_enhanced', $tasks);
+    $this->assertArrayNotHasKey('entity.revisions_overview:node', $tasks, 'Node should have been excluded because it provides their own');
+
+    $this->assertEquals('entity.entity_test_enhanced.version_history', $tasks['entity.revisions_overview:entity_test_enhanced']['route_name']);
+    $this->assertEquals('entity.entity_test_enhanced.edit_form', $tasks['entity.revisions_overview:entity_test_enhanced']['base_route']);
+
+    /** @var \Drupal\Core\Routing\RouteProviderInterface $route_provider */
+    $route_provider = \Drupal::service('router.route_provider');
+
+    $route = $route_provider->getRouteByName('entity.entity_test_enhanced.version_history');
+    $this->assertInstanceOf(Route::class, $route);
+    $this->assertEquals('\Drupal\entity\Controller\RevisionOverviewController::revisionOverviewController', $route->getDefault('_controller'));
+  }
+
+}
diff --git a/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php b/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php
index d49e0bdcedfc0ca2842ad47eceb773446b0811c6..53c7ae441dc862b1ad97bd6f13c5dd6c4691954e 100644
--- a/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php
+++ b/tests/modules/entity_module_test/src/Entity/EnhancedEntity.php
@@ -51,8 +51,9 @@ use Drupal\entity\Revision\RevisionableContentEntityBase;
  *     "canonical" = "/entity_test_enhanced/{entity_test_enhanced}",
  *     "revision" = "/entity_test_enhanced/{entity_test_enhanced}/revisions/{entity_test_enhanced_revision}/view",
  *     "revision-revert-form" = "/entity_test_enhanced/{entity_test_enhanced}/revisions/{entity_test_enhanced_revision}/revert",
+ *     "version-history" = "/entity_test_enhanced/{entity_test_enhanced}/revisions",
  *   },
- *   bundle_entity_type = "entity_test_enhanced_bundle"
+ *   bundle_entity_type = "entity_test_enhanced_bundle",
  * )
  */
 class EnhancedEntity extends RevisionableContentEntityBase {
diff --git a/tests/src/Kernel/RevisionBasicUITest.php b/tests/src/Kernel/RevisionBasicUITest.php
index efb6f75ddb43ed663b3bd3f5adf5478354962ce6..5cb764926b49126b012daaaf03e1fadc3bac00be 100644
--- a/tests/src/Kernel/RevisionBasicUITest.php
+++ b/tests/src/Kernel/RevisionBasicUITest.php
@@ -43,6 +43,48 @@ class RevisionBasicUITest extends KernelTestBase {
     \Drupal::service('router.builder')->rebuild();
   }
 
+  /**
+   * Tests the revision history controller.
+   */
+  public function testRevisionHistory() {
+    $entity = EnhancedEntity::create([
+      'name' => 'rev 1',
+      'type' => 'default',
+    ]);
+    $entity->save();
+
+    $revision = clone $entity;
+    $revision->name->value = 'rev 2';
+    $revision->setNewRevision(TRUE);
+    $revision->isDefaultRevision(FALSE);
+    $revision->save();
+
+    /** @var \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel */
+    $http_kernel = \Drupal::service('http_kernel');
+    $request = Request::create($revision->url('version-history'));
+    $response = $http_kernel->handle($request);
+    $this->assertEquals(403, $response->getStatusCode());
+
+    $role = Role::create(['id' => 'test_role']);
+    $role->grantPermission('view all entity_test_enhanced revisions');
+    $role->grantPermission('administer entity_test_enhanced');
+    $role->save();
+
+    $user = User::create([
+      'name' => 'Test user',
+    ]);
+    $user->addRole($role->id());
+    \Drupal::service('account_switcher')->switchTo($user);
+
+    $request = Request::create($revision->url('version-history'));
+    $response = $http_kernel->handle($request);
+    $this->assertEquals(200, $response->getStatusCode());
+
+    // This ensures that the default revision is still the first revision.
+    $this->assertTrue(strpos($response->getContent(), 'entity_test_enhanced/1/revisions/2/view') !== FALSE);
+    $this->assertTrue(strpos($response->getContent(), 'entity_test_enhanced/1') !== FALSE);
+  }
+
   public function testRevisionView() {
     $entity = EnhancedEntity::create([
       'name' => 'rev 1',