diff --git a/multiversion.services.yml b/multiversion.services.yml
index 03df8e6..83a7055 100644
--- a/multiversion.services.yml
+++ b/multiversion.services.yml
@@ -28,7 +28,7 @@ services:
       - [setContainer, ['@service_container']]
   workspace.manager:
     class: Drupal\multiversion\Workspace\WorkspaceManager
-    arguments: ['@request_stack', '@entity.manager']
+    arguments: ['@request_stack', '@entity.manager', '@logger.channel.workspace']
     tags:
       - { name: service_collector, tag: workspace_negotiator, call: addNegotiator }
   workspace.conflict_tracker:
@@ -43,6 +43,9 @@ services:
     class: Drupal\multiversion\StreamWrapper\MigrateStream
     tags:
       - { name: stream_wrapper, scheme: migrate }
+  logger.channel.workspace:
+    parent: logger.channel_base
+    arguments: ['cron']
 
   # @todo: {@link https://www.drupal.org/node/2597414 Simplify the container
   # definition for negotiators.}
diff --git a/src/Workspace/WorkspaceAccessException.php b/src/Workspace/WorkspaceAccessException.php
new file mode 100644
index 0000000..19e2565
--- /dev/null
+++ b/src/Workspace/WorkspaceAccessException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Drupal\multiversion\Workspace;
+
+
+use Drupal\Core\Access\AccessException;
+
+/**
+ * Exception thrown when trying to switch to an inaccessible workspace.
+ */
+class WorkspaceAccessException extends AccessException {
+
+}
diff --git a/src/Workspace/WorkspaceManager.php b/src/Workspace/WorkspaceManager.php
index b7fcab3..c522673 100644
--- a/src/Workspace/WorkspaceManager.php
+++ b/src/Workspace/WorkspaceManager.php
@@ -8,10 +8,14 @@
 namespace Drupal\multiversion\Workspace;
 
 use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\multiversion\Entity\WorkspaceInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\NullLogger;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 class WorkspaceManager implements WorkspaceManagerInterface {
+  use StringTranslationTrait;
 
   /**
    * @var \Symfony\Component\HttpFoundation\RequestStack
@@ -40,12 +44,22 @@ class WorkspaceManager implements WorkspaceManagerInterface {
   protected $activeWorkspace;
 
   /**
+   * @var \Psr\Log\LoggerInterface
+   */
+  protected $logger;
+
+  /**
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
+   *   The entity manager. This should get replaced with Entity Type Manager.
+   * @param LoggerInterface $logger
+   *   A logger service.
    */
-  public function __construct(RequestStack $request_stack, EntityManagerInterface $entity_manager) {
+  public function __construct(RequestStack $request_stack, EntityManagerInterface $entity_manager, LoggerInterface $logger = NULL) {
     $this->requestStack = $request_stack;
     $this->entityManager = $entity_manager;
+    $this->logger = $logger ?: new NullLogger();
   }
 
   /**
@@ -84,31 +98,53 @@ class WorkspaceManager implements WorkspaceManagerInterface {
    * @todo {@link https://www.drupal.org/node/2600382 Access check.}
    */
   public function getActiveWorkspace() {
-    // Return the cached value if it is set.
-    if (isset($this->activeWorkspace)) {
-      return $this->activeWorkspace;
+
+    if (!$this->activeWorkspace) {
+      $this->activeWorkspace = $this->deriveActiveWorkspace();
     }
 
-    $this->activeWorkspace = NULL;
+    return $this->activeWorkspace;
+  }
+
+  /**
+   * Determines the active workspace from the available negotiators.
+   *
+   * This method is uncached, and intended to be used by a caching public
+   * method.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|null
+   *   The workspace that should be active, or NULL if there isn't one.
+   */
+  protected function deriveActiveWorkspace() {
+    $active_workspace = NULL;
     $request = $this->requestStack->getCurrentRequest();
     foreach ($this->getSortedNegotiators() as $negotiator) {
       if ($negotiator->applies($request)) {
         if ($workspace_id = $negotiator->getWorkspaceId($request)) {
-          if ($active_workspace = $this->load($workspace_id)) {
-            $this->activeWorkspace = $active_workspace;
+          if ($new_active_workspace = $this->load($workspace_id)) {
+            $active_workspace = $new_active_workspace;
             break;
           }
         }
       }
     }
+    return $active_workspace;
 
-    return $this->activeWorkspace;
   }
 
   /**
    * {@inheritdoc}
    */
   public function setActiveWorkspace(WorkspaceInterface $workspace) {
+
+    // If the current user doesn't have access to view the workspace, they
+    // shouldn't be allowed to switch to it.
+    // @todo Could this be handled better?
+    if (!$workspace->access('view')) {
+      $this->logger->error('Denied access to view workspace {workspace}', ['workspace' => $workspace->label()]);
+      throw new WorkspaceAccessException('The user does not have permission to view that workspace.');
+    }
+
     // Set the workspace on the proper negotiator.
     $request = $this->requestStack->getCurrentRequest();
     foreach ($this->getSortedNegotiators() as $negotiator) {
diff --git a/src/Workspace/WorkspaceManagerInterface.php b/src/Workspace/WorkspaceManagerInterface.php
index f6e9f60..ff13eb2 100644
--- a/src/Workspace/WorkspaceManagerInterface.php
+++ b/src/Workspace/WorkspaceManagerInterface.php
@@ -38,8 +38,14 @@ interface WorkspaceManagerInterface {
   public function getActiveWorkspace();
 
   /**
+   * Sets the active workspace for the site/session.
+   *
    * @param \Drupal\multiversion\Entity\WorkspaceInterface $workspace
+   *   The workspace to set as active.
+   *
    * @return \Drupal\multiversion\Workspace\WorkspaceManagerInterface
+   *
+   * @throws WorkspaceAccessException
    */
   public function setActiveWorkspace(WorkspaceInterface $workspace);
 
