diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
index ee55eb9..e6a4e31 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
@@ -66,7 +66,7 @@ function testNodeAccessBasic() {
     $titles = array(); // Titles keyed by nid
     $private_nodes = array(); // Array of nids marked private.
     for ($i = 0; $i < $num_simple_users; $i++) {
-      $simple_users[$i] = $this->drupalCreateUser(array('access content', 'create article content'));
+      $simple_users[$i] = $this->drupalCreateUser(array('access content', 'create article content', 'create terms in tags'));
     }
     foreach ($simple_users as $this->webUser) {
       $this->drupalLogin($this->webUser);
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Access/TaxonomyOverviewAccessCheck.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Access/TaxonomyOverviewAccessCheck.php
new file mode 100644
index 0000000..b07e5aa
--- /dev/null
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Access/TaxonomyOverviewAccessCheck.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\overlay\Access\TaxonomyOverviewAccessCheck
+ */
+
+namespace Drupal\taxonomy\Access;
+
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides an access check for the taxonomy overview page.
+ */
+class TaxonomyOverviewAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return '_access_taxonomy_overview';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    $access = user_access('administer taxonomy') || user_access('access taxonomy overview');
+
+    return $access ? static::ALLOW : static::DENY;
+  }
+
+}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
index d79af40..18dcac8 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Form/OverviewTerms.php
@@ -202,14 +202,22 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
     $destination = drupal_get_destination();
     $row_position = 0;
     // Build the actual form.
+    $access_controller = \Drupal::entityManager()->getAccessController('taxonomy_term');
+    if ($access_controller->createAccess($taxonomy_vocabulary->id())) {
+      $empty = $this->t('No terms available. <a href="@link">Add term</a>.', array('@link' => url('admin/structure/taxonomy/manage/' . $taxonomy_vocabulary->id() . '/add')));
+    }
+    else {
+      $empty = $this->t('No terms available.');
+    }
     $form['terms'] = array(
       '#type' => 'table',
-      '#header' => array($this->t('Name'), $this->t('Weight'), $this->t('Operations')),
-      '#empty' => $this->t('No terms available. <a href="@link">Add term</a>.', array('@link' => url('admin/structure/taxonomy/manage/' . $taxonomy_vocabulary->id() . '/add'))),
+      '#empty' => $empty,
       '#attributes' => array(
         'id' => 'taxonomy',
       ),
     );
+    $change_weight_access = TRUE;
+    $operations_access = TRUE;
     foreach ($current_page as $key => $term) {
       $uri = $term->uri();
       $edit_uri = $term->uri('edit-form');
@@ -255,28 +263,38 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
           ),
         );
       }
-      $form['terms'][$key]['weight'] = array(
-        '#type' => 'weight',
-        '#delta' => $delta,
-        '#title_display' => 'invisible',
-        '#title' => $this->t('Weight for added term'),
-        '#default_value' => $term->weight->value,
-        '#attributes' => array(
-          'class' => array('term-weight'),
-        ),
-      );
-      $operations = array(
-        'edit' => array(
+      $edit_access = $term->access('update');
+      $change_weight_access &= $edit_access;
+
+      if ($edit_access) {
+        $form['terms'][$key]['weight'] = array(
+          '#type' => 'weight',
+          '#delta' => $delta,
+          '#title_display' => 'invisible',
+          '#title' => $this->t('Weight for added term'),
+          '#default_value' => $term->weight->value,
+          '#attributes' => array(
+            'class' => array('term-weight'),
+          ),
+          '#access' => $edit_access,
+        );
+      }
+      $operations = array();
+
+      if ($edit_access) {
+        $operations['edit'] = array(
           'title' => $this->t('edit'),
           'href' => $edit_uri['path'],
           'query' => $destination,
-        ),
-        'delete' => array(
+        );
+      }
+      if ($term->access('delete')) {
+        $operations['delete'] = array(
           'title' => $this->t('delete'),
           'href' => $uri['path'] . '/delete',
           'query' => $destination,
-        ),
-      );
+        );
+      }
       if ($this->moduleHandler->moduleExists('content_translation') && content_translation_translate_access($term)) {
         $operations['translate'] = array(
           'title' => $this->t('translate'),
@@ -284,10 +302,14 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
           'query' => $destination,
         );
       }
-      $form['terms'][$key]['operations'] = array(
-        '#type' => 'operations',
-        '#links' => $operations,
-      );
+
+      $operations_access &= count($operations) > 0;
+      if (count($operations)) {
+        $form['terms'][$key]['operations'] = array(
+          '#type' => 'operations',
+          '#links' => $operations,
+        );
+      }
 
       $form['terms'][$key]['#attributes']['class'] = array();
       if ($parent_fields) {
@@ -317,30 +339,38 @@ public function buildForm(array $form, array &$form_state, VocabularyInterface $
       $row_position++;
     }
 
-    if ($parent_fields) {
-      $form['terms']['#tabledrag'][] = array(
-        'match',
-        'parent',
-        'term-parent',
-        'term-parent',
-        'term-id',
-        FALSE,
-      );
-      $form['terms']['#tabledrag'][] = array(
-        'depth',
-        'group',
-        'term-depth',
-        NULL,
-        NULL,
-        FALSE
-      );
-      $form['terms']['#attached']['library'][] = array('taxonomy', 'drupal.taxonomy');
-      $form['terms']['#attached']['js'][] = array(
-        'data' => array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)),
-        'type' => 'setting',
-      );
+    $form['terms']['#header'] = array($this->t('Name'));
+    if ($change_weight_access) {
+      $form['terms']['#header'][] = $this->t('Weight');
+      if ($parent_fields) {
+        $form['terms']['#tabledrag'][] = array(
+          'match',
+          'parent',
+          'term-parent',
+          'term-parent',
+          'term-id',
+          FALSE,
+        );
+        $form['terms']['#tabledrag'][] = array(
+          'depth',
+          'group',
+          'term-depth',
+          NULL,
+          NULL,
+          FALSE
+        );
+        $form['terms']['#attached']['library'][] = array('taxonomy', 'drupal.taxonomy');
+        $form['terms']['#attached']['js'][] = array(
+          'data' => array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)),
+          'type' => 'setting',
+        );
+      }
+      $form['terms']['#tabledrag'][] = array('order', 'sibling', 'term-weight');
+    }
+
+    if ($operations_access) {
+      $form['terms']['#header'][] = $this->t('Operations');
     }
-    $form['terms']['#tabledrag'][] = array('order', 'sibling', 'term-weight');
 
     if ($taxonomy_vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
       $form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php
index ca98dbb..e644fce 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermAccessController.php
@@ -41,7 +41,7 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A
    * {@inheritdoc}
    */
   protected function checkCreateAccess(AccountInterface $account, array $context, $entity_bundle = NULL) {
-    return $account->hasPermission('administer taxonomy');
+    return $account->hasPermission("create terms in {$entity_bundle}") || $account->hasPermission('administer taxonomy');
   }
 
 }
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
index e127dbb..0491752 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
@@ -21,6 +21,34 @@ public static function getInfo() {
   }
 
   /**
+   * Create, edit and delete a vocabulary via the user interface.
+   */
+  function testVocabularyPermissionsVocabulary() {
+    // VocabularyTest.php already tests for user with "administer taxonomy" permissions.
+
+    // Test as user without proper permissions.
+    $authenticated_user = $this->drupalCreateUser(array());
+    $this->drupalLogin($authenticated_user);
+
+    // Visit the main taxonomy administration page.
+    $this->drupalGet('admin/structure/taxonomy');
+    $this->assertResponse(403, 'Vocabulary overview open failed.');
+
+    // Test as user with "access taxonomy overview" permissions.
+    $proper_user = $this->drupalCreateUser(array('access taxonomy overview'));
+    $this->drupalLogin($proper_user);
+
+    // Visit the main taxonomy administration page.
+    $this->drupalGet('admin/structure/taxonomy');
+    $this->assertResponse(200);
+    $this->assertText(t('Vocabulary name'), 'Vocabulary overview opened successfully.');
+
+    // Create a new vocabulary.
+    $this->clickLink(t('Add vocabulary'));
+    $this->assertResponse(403, 'Add vocabulary form open failed.');
+  }
+
+  /**
    * Create, edit and delete a taxonomy term via the user interface.
    */
   function testVocabularyPermissionsTaxonomyTerm() {
@@ -66,8 +94,35 @@ function testVocabularyPermissionsTaxonomyTerm() {
     $this->drupalPost(NULL, NULL, t('Delete'));
     $this->assertRaw(t('Deleted term %name.', array('%name' => $edit['name'])), 'Term deleted.');
 
+    // Test as user with "create" permissions.
+    $user = $this->drupalCreateUser(array("access taxonomy overview", "create terms in {$vocabulary->id()}"));
+    $this->drupalLogin($user);
+
+    // Visit the main taxonomy administration page.
+    $this->drupalGet('admin/structure/taxonomy/manage/' . $vocabulary->id() . '/add');
+    $this->assertResponse(200);
+    $this->assertField('edit-name', 'Add taxonomy term form opened successfully.');
+
+    // Submit the term.
+    $edit = array();
+    $edit['name'] = $this->randomName();
+
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertRaw(t('Created new term %name.', array('%name' => $edit['name'])), 'Term created successfully.');
+
+    $terms = taxonomy_term_load_multiple_by_name($edit['name']);
+    $term = reset($terms);
+
+    // Edit the term.
+    $this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
+    $this->assertResponse(403, 'Edit taxonomy term form open failed.');
+
+    // Delete the vocabulary.
+    $this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
+    $this->assertResponse(403, 'Delete taxonomy term form open failed.');
+
     // Test as user with "edit" permissions.
-    $user = $this->drupalCreateUser(array("edit terms in {$vocabulary->id()}"));
+    $user = $this->drupalCreateUser(array("access taxonomy overview", "edit terms in {$vocabulary->id()}"));
     $this->drupalLogin($user);
 
     // Visit the main taxonomy administration page.
@@ -91,7 +146,7 @@ function testVocabularyPermissionsTaxonomyTerm() {
     $this->assertResponse(403, 'Delete taxonomy term form open failed.');
 
     // Test as user with "delete" permissions.
-    $user = $this->drupalCreateUser(array("delete terms in {$vocabulary->id()}"));
+    $user = $this->drupalCreateUser(array("access taxonomy overview", "delete terms in {$vocabulary->id()}"));
     $this->drupalLogin($user);
 
     // Visit the main taxonomy administration page.
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
index 56b92ed..55a47eb 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyAccessController.php
@@ -22,7 +22,10 @@ class VocabularyAccessController extends EntityAccessController {
    * {@inheritdoc}
    */
   protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
-    return $account->hasPermission('administer taxonomy');
+    switch ($operation) {
+      case 'view': return $account->hasPermission('administer taxonomy') || $account->hasPermission('access taxonomy overview');
+      default: return $account->hasPermission('administer taxonomy');
+    }
   }
 
   /**
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php
index 3ce091b..d622f04 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyListController.php
@@ -45,12 +45,15 @@ public function getOperations(EntityInterface $entity) {
       'options' => $uri['options'],
       'weight' => 0,
     );
-    $operations['add'] = array(
-      'title' => t('add terms'),
-      'href' => $uri['path'] . '/add',
-      'options' => $uri['options'],
-      'weight' => 10,
-    );
+    $access_controller = \Drupal::entityManager()->getAccessController('taxonomy_term');
+    if ($access_controller->createAccess($entity->bundle())) {
+      $operations['add'] = array(
+        'title' => t('add terms'),
+        'href' => $uri['path'] . '/add',
+        'options' => $uri['options'],
+        'weight' => 10,
+      );
+    }
     unset($operations['delete']);
 
     return $operations;
@@ -84,7 +87,13 @@ public function render() {
       unset($this->weightKey);
     }
     $build = parent::render();
-    $build['#empty'] = t('No vocabularies available. <a href="@link">Add vocabulary</a>.', array('@link' => url('admin/structure/taxonomy/add')));
+
+    if (user_access('administer vocabularies')) {
+      $build['#empty'] = t('No vocabularies available. <a href="@link">Add vocabulary</a>.', array('@link' => url('admin/structure/taxonomy/add')));
+    }
+    else {
+      $build['#empty'] = t('No vocabularies available.');
+    }
     return $build;
   }
 
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 0827bef..e91c474 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -92,9 +92,20 @@ function taxonomy_permission() {
     'administer taxonomy' => array(
       'title' => t('Administer vocabularies and terms'),
     ),
+    'access taxonomy overview' => array(
+      'title' => t('Access the taxonomy overview page'),
+      'description' => t('Get an overview of terms at <a href="@url">admin/structure/taxonomy</a>.', array(
+        '@url' => url('admin/structure/taxonomy'),
+      )),
+    ),
   );
   foreach (entity_load_multiple('taxonomy_vocabulary') as $vocabulary) {
     $permissions += array(
+      'create terms in ' . $vocabulary->id() => array(
+        'title' => t('Create terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
+      ),
+    );
+    $permissions += array(
       'edit terms in ' . $vocabulary->id() => array(
         'title' => t('Edit terms in %vocabulary', array('%vocabulary' => $vocabulary->name)),
       ),
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
index 53f6098..cd27334 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -3,7 +3,7 @@ taxonomy_vocabulary_list:
   defaults:
     _entity_list: 'taxonomy_vocabulary'
   requirements:
-    _permission: 'administer taxonomy'
+    _access_taxonomy_overview: 'TRUE'
 
 taxonomy_term_add:
   pattern: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add'
diff --git a/core/modules/taxonomy/taxonomy.services.yml b/core/modules/taxonomy/taxonomy.services.yml
index 9a7b012..f1b3320 100644
--- a/core/modules/taxonomy/taxonomy.services.yml
+++ b/core/modules/taxonomy/taxonomy.services.yml
@@ -4,3 +4,8 @@ services:
     arguments: ['@entity.manager']
     tags:
       - { name: access_check }
+
+  access_check.taxonomy.taxonomy_overview:
+    class: Drupal\taxonomy\Access\TaxonomyOverviewAccessCheck
+    tags:
+      - { name: access_check }
diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
index c7ec4e0..1e31af2 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/TaggedWithTest.php
@@ -40,6 +40,8 @@ public static function getInfo() {
   }
 
   function setUp() {
+    $this->permissions[] = 'administer taxonomy';
+
     parent::setUp();
 
     // Create two content types. One will have an autocomplete tagging field,
diff --git a/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php b/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php
index 0abb524..ede9897 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Wizard/WizardTestBase.php
@@ -15,6 +15,13 @@
 abstract class WizardTestBase extends ViewTestBase {
 
   /**
+   * Permissions for this user to test with.
+   *
+   * @var array
+   */
+  protected $permissions = array();
+
+  /**
    * Modules to enable.
    *
    * @var array
@@ -24,8 +31,10 @@
   function setUp() {
     parent::setUp();
 
+    $this->permissions = array_merge($this->permissions, array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions'));
+
     // Create and log in a user with administer views permission.
-    $views_admin = $this->drupalCreateUser(array('administer views', 'administer blocks', 'bypass node access', 'access user profiles', 'view all revisions'));
+    $views_admin = $this->drupalCreateUser($this->permissions);
     $this->drupalLogin($views_admin);
   }
 
