diff --git a/core/modules/user/lib/Drupal/user/Entity/User.php b/core/modules/user/lib/Drupal/user/Entity/User.php
index 44bce77..2219207 100644
--- a/core/modules/user/lib/Drupal/user/Entity/User.php
+++ b/core/modules/user/lib/Drupal/user/Entity/User.php
@@ -436,15 +436,18 @@ public static function baseFieldDefinitions($entity_type) {
 
     $fields['langcode'] = FieldDefinition::create('language')
       ->setLabel(t('Language code'))
-      ->setDescription(t('The user language code.'));
+      ->setDescription(t('The user language code.'))
+      ->setClass('\Drupal\user\FieldType\UserUnrestrictedFieldItemList');
 
     $fields['preferred_langcode'] = FieldDefinition::create('language')
       ->setLabel(t('Preferred admin language code'))
-      ->setDescription(t("The user's preferred language code for receiving emails and viewing the site."));
+      ->setDescription(t("The user's preferred language code for receiving emails and viewing the site."))
+      ->setClass('\Drupal\user\FieldType\UserUnrestrictedFieldItemList');
 
     $fields['preferred_admin_langcode'] = FieldDefinition::create('language')
       ->setLabel(t('Preferred language code'))
-      ->setDescription(t("The user's preferred language code for viewing administration pages."));
+      ->setDescription(t("The user's preferred language code for viewing administration pages."))
+      ->setClass('\Drupal\user\FieldType\UserUnrestrictedFieldItemList');
 
     // The name should not vary per language. The username is the visual
     // identifier for a user and needs to be consistent in all languages.
@@ -457,7 +460,8 @@ public static function baseFieldDefinitions($entity_type) {
         // that.
         'UserName' => array(),
         'UserNameUnique' => array(),
-      ));
+      ))
+      ->setClass('\Drupal\user\FieldType\UserNameFieldItemList');
 
     $fields['pass'] = FieldDefinition::create('string')
       ->setLabel(t('Password'))
@@ -472,10 +476,12 @@ public static function baseFieldDefinitions($entity_type) {
     // @todo Convert to a text field in https://drupal.org/node/1548204.
     $fields['signature'] = FieldDefinition::create('string')
       ->setLabel(t('Signature'))
-      ->setDescription(t('The signature of this user.'));
+      ->setDescription(t('The signature of this user.'))
+      ->setClass('\Drupal\user\FieldType\UserUnrestrictedFieldItemList');
     $fields['signature_format'] = FieldDefinition::create('string')
       ->setLabel(t('Signature format'))
-      ->setDescription(t('The signature format of this user.'));
+      ->setDescription(t('The signature format of this user.'))
+      ->setClass('\Drupal\user\FieldType\UserUnrestrictedFieldItemList');
 
     $fields['timezone'] = FieldDefinition::create('string')
       ->setLabel(t('Timezone'))
diff --git a/core/modules/user/lib/Drupal/user/FieldType/UserFieldItemListBase.php b/core/modules/user/lib/Drupal/user/FieldType/UserFieldItemListBase.php
new file mode 100644
index 0000000..587d54a
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/FieldType/UserFieldItemListBase.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\user\FieldType\UserFieldItemListBase.
+ */
+
+namespace Drupal\user\FieldType;
+
+use Drupal\Core\Field\FieldItemList;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Helper parent class for user base fields.
+ */
+abstract class UserFieldItemListBase extends FieldItemList {
+
+  /**
+   * Factored administrative permission check.
+   *
+   * @todo Consider moving this method to AccountInterface / User.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The account object.
+   *
+   * @return bool
+   *   TRUE if the account has permission otherwise FALSE.
+   */
+  public static function hasAdministrativePermission(AccountInterface $account) {
+    return $account->hasPermission('administer users');
+  }
+
+  /**
+   * Is the passed account the same as the user entity bearing the field.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The account object.
+   *
+   * @return bool
+   *   TRUE if the passed account is the same user holding the field.
+   */
+  public function isMe(AccountInterface $account) {
+    return $this->getEntity()->id() === $account->id();
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/FieldType/UserNameFieldItemList.php b/core/modules/user/lib/Drupal/user/FieldType/UserNameFieldItemList.php
new file mode 100644
index 0000000..7dcce9d
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/FieldType/UserNameFieldItemList.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\user\FieldType\UserNameFieldItemList.
+ */
+
+namespace Drupal\user\FieldType;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Represents a user name entity field items list.
+ *
+ * Unlike basic strings, it has custom access rules.
+ */
+class UserNameFieldItemList extends UserFieldItemListBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultAccess($operation = 'view', AccountInterface $account = NULL) {
+    // Because the user name is the label of the entity, its visibility is that
+    // of the entity.
+    if ($operation === 'view') {
+      return TRUE;
+    }
+
+    // User administrators have all permissions on user fields.
+    if (self::hasAdministrativePermission($account)) {
+      return TRUE;
+    }
+
+    $result = $this->isMe($account) && $account->hasPermission('change own username');
+
+    return $result;
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/FieldType/UserRestrictedFieldItemList.php b/core/modules/user/lib/Drupal/user/FieldType/UserRestrictedFieldItemList.php
new file mode 100644
index 0000000..f2787a7
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/FieldType/UserRestrictedFieldItemList.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @file
+ * Contains Drupal\user\FieldType\UserRestrictedFieldItemList.
+ */
+
+namespace Drupal\user\FieldType;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Class for restricted user fields.
+ *
+ * Such fields are accessible to the user itself and users having 'access user
+ * profiles' for view, but are only modifiable by user administrators.
+ */
+class UserRestrictedFieldItemList extends UserFieldItemListBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultAccess($operation = 'view', AccountInterface $account = NULL) {
+    if (self::hasAdministrativePermission($account)) {
+      return TRUE;
+    }
+
+    if ($operation === 'view') {
+      return $account->hasPermission('access user profiles') || $this->isMe($account);
+    }
+
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/FieldType/UserUnrestrictedFieldItemList.php b/core/modules/user/lib/Drupal/user/FieldType/UserUnrestrictedFieldItemList.php
new file mode 100644
index 0000000..ac46d42
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/FieldType/UserUnrestrictedFieldItemList.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @file
+ * Contains Drupal\user\FieldType\UserUnrestrictedFieldItemList.
+ */
+
+namespace Drupal\user\FieldType;
+
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Class for Unrestricted user fields.
+ *
+ * Such fields are accessible to the user itself and users having 'access user
+ * profiles' for view, and are modifiable by the user itself.
+ */
+class UserUnrestrictedFieldItemList extends UserFieldItemListBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultAccess($operation = 'view', AccountInterface $account = NULL) {
+    if (self::hasAdministrativePermission($account) || $this->isMe($account)) {
+      return TRUE;
+    }
+
+    if ($operation === 'view') {
+      return $account->hasPermission('access user profiles');
+    }
+
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserNameAccessTest.php b/core/modules/user/lib/Drupal/user/Tests/UserNameAccessTest.php
new file mode 100644
index 0000000..d7f65aa
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Tests/UserNameAccessTest.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\user\Tests\UserNameAccessTest.
+ */
+
+namespace Drupal\user\Tests;
+
+use Drupal\Core\Session\AccountInterface;
+use Drupal\system\Tests\Entity\EntityUnitTestBase;
+use Drupal\user\Entity\User;
+
+/**
+ * Tests the entity access controller.
+ */
+class UserNameAccessTest extends EntityUnitTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'User name access',
+      'description' => 'Tests user name access.',
+      'group' => 'User',
+    );
+  }
+
+  /**
+   * Asserts user name access correctly grants or denies access.
+   */
+  public function assertUserNameAccess($ops, User $observed, AccountInterface $account, $info) {
+    foreach ($ops as $op => $result) {
+      $message = format_string("User name access returns @result with operation '@op'. @info.", array(
+        '@result' => !isset($result) ? 'null' : ($result ? 'true' : 'false'),
+        '@op' => $op,
+        '@info' => $info,
+      ));
+
+      $this->assertEqual($result, $observed->name->access($op, $account), $message);
+    }
+  }
+
+  /**
+   * Ensures user name access is working properly.
+   */
+  public function testUserNameAccess() {
+    // Set up a non-admin user that is allowed to view user profiles.
+    $user_viewer = $this->createUser(array('uid' => 2), array('access user profiles'));
+
+    // Set up a non-admin user that is allowed to change its own user name.
+    $user_changer = $this->createUser(array('uid' => 3), array('change own username'));
+
+    // Set up a non-admin user with administer users privileges, but not more.
+    $user_admin = $this->createUser(array('uid' => 4), array('administer users'));
+
+    // The viewer user is allowed to see user names on all accounts.
+    $this->assertUserNameAccess(array(
+      'update' => FALSE,
+      'view' => TRUE,
+    ), $user_viewer, $user_viewer, "Own account");
+    $this->assertUserNameAccess(array(
+      'update' => FALSE,
+      'view' => TRUE,
+    ), $user_changer, $user_viewer, "Third-party account");
+
+    // The changer user is only allowed to change its own user name.
+    $this->assertUserNameAccess(array(
+      'update' => FALSE,
+      'view' => TRUE,
+    ), $user_viewer, $user_changer, "Own account");
+    $this->assertUserNameAccess(array(
+      'update' => TRUE,
+      'view' => TRUE,
+    ), $user_changer, $user_changer, "Third-party account");
+
+    // The users-administrator user has all accesses.
+    $this->assertUserNameAccess(array(
+      'update' => TRUE,
+      'view' => TRUE,
+    ), $user_admin, $user_admin, "Own account");
+    $this->assertUserNameAccess(array(
+      'update' => TRUE,
+      'view' => TRUE,
+    ), $user_viewer, $user_admin, "Third-party account");
+  }
+
+}
