diff --git a/ctools.routing.yml b/ctools.routing.yml
new file mode 100644
index 0000000..4e00ed2
--- /dev/null
+++ b/ctools.routing.yml
@@ -0,0 +1,7 @@
+ctools.user_permission_autocomplete:
+  path: '/ctools/user-permission-autocomplete'
+  defaults:
+    _controller: '\Drupal\ctools\Controller\PermissionAutocomplete::autocompletePermission'
+  requirements:
+    # @todo What permission do we use?
+    _permission: 'administer blocks'
diff --git a/src/Controller/PermissionAutocomplete.php b/src/Controller/PermissionAutocomplete.php
new file mode 100644
index 0000000..393377f
--- /dev/null
+++ b/src/Controller/PermissionAutocomplete.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Controller\PermissionAutocomplete.
+ */
+
+namespace Drupal\ctools\Controller;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\user\PermissionHandlerInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides autocomplete for permissions.
+ */
+class PermissionAutocomplete implements ContainerInjectionInterface {
+
+  /**
+   * @var \Drupal\user\PermissionHandlerInterface
+   */
+  protected $permissionHandler;
+
+  /**
+   * PermissionAutocomplete constructor.
+   *
+   * @param \Drupal\user\PermissionHandlerInterface $permission_handler
+   */
+  public function __construct(PermissionHandlerInterface $permission_handler) {
+    $this->permissionHandler = $permission_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('user.permissions')
+    );
+  }
+
+  /**
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *
+   * @return \Symfony\Component\HttpFoundation\JsonResponse
+   */
+  public function autocompletePermission(Request $request) {
+    $typed_permission = $request->query->get('q');
+    $matches = [];
+    foreach ($this->permissionHandler->getPermissions() as $key => $permission) {
+      if (stripos($key, $typed_permission) !== FALSE || stripos($permission['title'], $typed_permission) !== FALSE) {
+        $matches[] = $permission['title'] . " ($key)";
+      }
+    }
+    return new JsonResponse($matches);
+  }
+
+}
diff --git a/src/Plugin/Condition/Permission.php b/src/Plugin/Condition/Permission.php
new file mode 100644
index 0000000..110be35
--- /dev/null
+++ b/src/Plugin/Condition/Permission.php
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Plugin\Condition\Permission.
+ */
+
+namespace Drupal\ctools\Plugin\Condition;
+
+use Drupal\Core\Condition\ConditionPluginBase;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a 'User Permission' condition.
+ *
+ * @Condition(
+ *   id = "ctools_user_permission",
+ *   label = @Translation("User Permission"),
+ *   context = {
+ *     "user" = @ContextDefinition("entity:user", label = @Translation("User"))
+ *   }
+ * )
+ *
+ */
+class Permission extends ConditionPluginBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * @var \Drupal\user\PermissionHandlerInterface
+   */
+  protected $permissionHandler;
+
+  /**
+   * Permission constructor.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, \Drupal\user\PermissionHandlerInterface $permission_handler) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->permissionHandler = $permission_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('user.permissions')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    $form['permission'] = array(
+      '#type' => 'textfield',
+      '#title' => $this->t('When the user has the following permission'),
+      '#default_value' => $this->getPermissionString($this->configuration['permission']),
+      '#description' => $this->t('If you specify no permission, the condition will evaluate to TRUE for all users.'),
+      '#autocomplete_route_name' => 'ctools.user_permission_autocomplete',
+      '#element_validate' => [[static::class, 'validateAutocomplete']],
+    );
+    return parent::buildConfigurationForm($form, $form_state);
+  }
+
+  /**
+   * Builds an autocomplete-safe permission string from the permission name.
+   *
+   * @param string $permission_name
+   *   The machine name for the permission.
+   *
+   * @return string
+   *   The permission string formatted as "Permission label (permission_name)".
+   */
+  protected function getPermissionString($permission_name) {
+    $permission_string = $permission_name;
+    $permissions = $this->permissionHandler->getPermissions();
+    if (!empty($permissions[$permission_string])) {
+      $permission_string = $permissions[$permission_string]['title'] . ' (' . $permission_string . ')';
+    }
+    return $permission_string;
+  }
+
+  /**
+   * Form element validation handler for the permission textfield.
+   */
+  public static function validateAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form) {
+    if (!empty($element['#value'])) {
+      if (preg_match("/.+\s\(([\w\s]+)\)/", $element['#value'], $matches)) {
+        $form_state->setValueForElement($element, $matches[1]);
+      }
+      else {
+        $form_state->setError($element, 'Invalid permission name.');
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return [
+      'permission' => '',
+    ] + parent::defaultConfiguration();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
+    $this->configuration['permission'] = $form_state->getValue('permission');
+    parent::submitConfigurationForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function summary() {
+    $permission = $this->configuration['permission'];
+    if (!empty($this->configuration['negate'])) {
+      return $this->t('The user does not have the permission "@permission"', array('@permission' => $permission));
+    }
+    else {
+      return $this->t('The user has the permission "@permission"', array('@permission' => $permission));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function evaluate() {
+    if (empty($this->configuration['permission']) && !$this->isNegated()) {
+      return TRUE;
+    }
+    /** @var \Drupal\user\UserInterface $user */
+    $user = $this->getContextValue('user');
+    return $user->hasPermission($this->configuration['permission']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheContexts() {
+    // Optimize cache context, if a user cache context is provided, only use
+    // user.permissions, since that's the only part this condition cares about.
+    $contexts = [];
+    foreach (parent::getCacheContexts() as $context) {
+      $contexts[] = $context == 'user' ? 'user.permissions' : $context;
+    }
+    return $contexts;
+  }
+
+}
diff --git a/src/Tests/PermissionAutocompleteTest.php b/src/Tests/PermissionAutocompleteTest.php
new file mode 100644
index 0000000..f75887c
--- /dev/null
+++ b/src/Tests/PermissionAutocompleteTest.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\ctools\Tests\PermissionAutocompleteTest.
+ */
+
+namespace Drupal\ctools\Tests;
+
+use Drupal\simpletest\BrowserTestBase;
+
+/**
+ * Tests the permission autocomplete.
+ *
+ * @coversDefaultClass \Drupal\ctools\Controller\PermissionAutocomplete
+ *
+ * @group ctools
+ */
+class PermissionAutocompleteTest extends BrowserTestBase {
+
+  public static $modules = ['block', 'ctools'];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function testAutocomplete() {
+    $this->drupalLogin($this->drupalCreateUser(['administer blocks']));
+    $this->drupalGet('ctools/user-permission-autocomplete', ['query' => ['q' => 'administration']]);
+
+    $expected = [
+      'Use the administration pages and help (access administration pages)',
+      'View the administration theme (view the administration theme)',
+    ];
+    $result = json_decode($this->getSession()->getPage()->getContent());
+    $this->assertEquals($expected, $result);
+  }
+
+}
diff --git a/tests/src/Unit/PermissionTest.php b/tests/src/Unit/PermissionTest.php
new file mode 100644
index 0000000..f13e327
--- /dev/null
+++ b/tests/src/Unit/PermissionTest.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\ctools\Unit\PermissionTest.
+ */
+
+namespace Drupal\Tests\ctools\Unit;
+
+use Drupal\Core\Form\FormState;
+use Drupal\ctools\Plugin\Condition\Permission;
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass \Drupal\ctools\Plugin\Condition\Permission
+ * @group CTools
+ */
+class PermissionTest extends UnitTestCase {
+
+  /**
+   * @covers ::validateAutocomplete
+   * @dataProvider providerTestValidateAutocomplete
+   */
+  public function testValidateAutocomplete($value, $expected, $expected_errors) {
+    $form['#parents'] = ['the_parents'];
+    $form['#value'] = $value;
+    $form_state = new FormState();
+    $complete_form = [];
+
+    Permission::validateAutocomplete($form, $form_state, $complete_form);
+    $this->assertSame($expected, $form_state->getValue($form['#parents']));
+    $this->assertSame($expected_errors, $form_state->getErrors());
+  }
+
+  public function providerTestValidateAutocomplete() {
+    $data = [];
+    $data['null'] = [
+      NULL,
+      NULL,
+      [],
+    ];
+    $data['no_match'] = [
+      'some_value',
+      NULL,
+      ['the_parents' => 'Invalid permission name.'],
+    ];
+    $data['match'] = [
+      'Human readable (machine readable)',
+      'machine readable',
+      [],
+    ];
+    return $data;
+  }
+
+}
