Index: src/wp-admin/includes/schema.php
===================================================================
--- src/wp-admin/includes/schema.php	(revision 38935)
+++ src/wp-admin/includes/schema.php	(working copy)
@@ -621,6 +621,7 @@
 	populate_roles_270();
 	populate_roles_280();
 	populate_roles_300();
+	populate_roles_470();
 }
 
 /**
@@ -861,7 +862,45 @@
 	}
 }
 
+
 /**
+ * Create and modify WordPress roles for WordPress 4.7.
+ *
+ * @since 4.7.0
+ */
+function populate_roles_470(){
+  
+  $administrator = get_role( 'administrator' );
+  if ( !empty( $administrator ) ) {
+    $administrator->add_cap( 'edit_user_meta' );
+    $administrator->add_cap( 'add_user_meta' );
+    $administrator->add_cap( 'delete_user_meta' );
+    $administrator->add_cap( 'edit_term_meta' );
+    $administrator->add_cap( 'add_term_meta' );
+    $administrator->add_cap( 'delete_term_meta' );
+    $administrator->add_cap( 'edit_post_meta' );
+    $administrator->add_cap( 'add_post_meta' );
+    $administrator->add_cap( 'delete_post_meta' );
+    $administrator->add_cap( 'edit_comment_meta' );
+    $administrator->add_cap( 'add_comment_meta' );
+    $administrator->add_cap( 'delete_comment_meta' );
+  }
+
+  $editor = get_role( 'editor' );
+  if ( !empty( $editor ) ) {
+    $editor->add_cap( 'edit_term_meta' );
+    $editor->add_cap( 'add_term_meta' );
+    $editor->add_cap( 'delete_term_meta' );
+    $editor->add_cap( 'edit_post_meta' );
+    $editor->add_cap( 'add_post_meta' );
+    $editor->add_cap( 'delete_post_meta' );
+    $editor->add_cap( 'edit_comment_meta' );
+    $editor->add_cap( 'add_comment_meta' );
+    $editor->add_cap( 'delete_comment_meta' );
+  }
+}
+
+/**
  * Install Network.
  *
  * @since 3.0.0
Index: src/wp-includes/capabilities.php
===================================================================
--- src/wp-includes/capabilities.php	(revision 38935)
+++ src/wp-includes/capabilities.php	(working copy)
@@ -29,7 +29,6 @@
 function map_meta_cap( $cap, $user_id ) {
 	$args = array_slice( func_get_args(), 2 );
 	$caps = array();
-
 	switch ( $cap ) {
 	case 'remove_user':
 		$caps[] = 'remove_users';
@@ -51,6 +50,50 @@
 			$caps[] = 'edit_users'; // edit_user maps to edit_users.
 		}
 		break;
+	case 'edit_user_meta':
+	case 'delete_user_meta':
+	case 'add_user_meta':
+		// There must be a user specified to edit their meta.
+		$user = ( ! empty( $args ) || ! empty( $args[0] ) ) ?  get_user_by( 'id', $args[0] ) : false;
+
+		if( ! $user ){
+			$caps[] = 'do_not_allow';
+			break;
+		}
+
+		$meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
+
+		if ( $meta_key && ( has_filter( "auth_user_meta_{$meta_key}" ) ) ) {
+			/**
+			 * Filters whether the user is allowed to add user meta to a user.
+			 *
+			 * The dynamic portion of the hook name, `$meta_key`, refers to the
+			 * meta key passed to map_meta_cap().
+			 *
+			 * @since 4.7.0
+			 *
+			 * @param bool   $allowed  Whether the user can add the user meta. Default false.
+			 * @param string $meta_key The meta key.
+			 * @param int    $user->ID The user being edited.
+			 * @param int    $user_id  User ID.
+			 * @param string $cap      Capability name.
+			 * @param array  $caps     User capabilities.
+			 */
+			$allowed = apply_filters( "auth_user_meta_{$meta_key}", false, $meta_key, $user->ID, $user_id, $cap, $caps );
+	
+			if ( ! $allowed ){
+				$caps[] = 'do_not_allow';
+			}
+		} elseif ( $user->ID === $user_id ) {
+			// If you can edit yourself, you can edit your meta. 
+			$caps = map_meta_cap( 'edit_user', $user_id, $user->ID );
+		}
+		else {
+			// The default is requiring the capability itself.
+			$caps[] = $cap;
+		}
+		break;
+
 	case 'delete_post':
 	case 'delete_page':
 		$post = get_post( $args[0] );
@@ -242,16 +285,13 @@
 	case 'edit_post_meta':
 	case 'delete_post_meta':
 	case 'add_post_meta':
-		$post = get_post( $args[0] );
+		$post = ( ! empty( $args ) && ! empty( $args[0] ) ) ? get_post( $args[0] ) : false;
 		if ( ! $post ) {
 			$caps[] = 'do_not_allow';
 			break;
 		}
-
 		$post_type = get_post_type( $post );
 
-		$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
-
 		$meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
 
 		if ( $meta_key && ( has_filter( "auth_post_meta_{$meta_key}" ) || has_filter( "auth_post_{$post_type}_meta_{$meta_key}" ) ) ) {
@@ -289,14 +329,22 @@
 			 */
 			$allowed = apply_filters( "auth_post_{$post_type}_meta_{$meta_key}", $allowed, $meta_key, $post->ID, $user_id, $cap, $caps );
 
-			if ( ! $allowed )
-				$caps[] = $cap;
+			if ( ! $allowed ){
+				$caps[] = 'do_not_allow';
+			}
 		} elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) {
+			$caps[] = 'do_not_allow';
+		} elseif ( (int)$post->post_author === (int)$user_id ) {
+			// If you're the author, you can edit your own post if you're allowed
+			$caps = map_meta_cap( 'edit_post', $user_id, $post->ID );	
+		} else {
+			// The default is requiring the capability itself.
 			$caps[] = $cap;
 		}
 		break;
 	case 'edit_comment':
 		$comment = get_comment( $args[0] );
+
 		if ( ! $comment ) {
 			$caps[] = 'do_not_allow';
 			break;
@@ -314,6 +362,47 @@
 			$caps = map_meta_cap( 'edit_posts', $user_id );
 		}
 		break;
+	case 'edit_comment_meta':
+	case 'delete_comment_meta':
+	case 'add_comment_meta':
+		$comment = ( ! empty( $args ) && ! empty( $args[0] ) ) ? get_comment( $args[0] ) : false;
+		if ( ! $comment ) {
+			$caps[] = 'do_not_allow';
+			break;
+		}
+		
+		$meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
+
+		if ( $meta_key && ( has_filter( "auth_comment_meta_{$meta_key}" ) ) ) {
+			/**
+			 * Filters whether the user is allowed to add comment meta to a comment.
+			 *
+			 * The dynamic portion of the hook name, `$meta_key`, refers to the
+			 * meta key passed to map_meta_cap().
+			 *
+			 * @since 4.7.0
+			 *
+			 * @param bool   $allowed  Whether the user can add the comment meta. Default false.
+			 * @param string $meta_key The meta key.
+			 * @param int    $comment_id  Comment ID.
+			 * @param int    $user_id  User ID.
+			 * @param string $cap      Capability name.
+			 * @param array  $caps     User capabilities.
+			 */
+			$allowed = apply_filters( "auth_comment_meta_{$meta_key}", false, $meta_key, $comment->comment_post_ID, $user_id, $cap, $caps );
+	
+			if ( ! $allowed ){
+				$caps[] = 'do_not_allow';
+			}
+		} elseif( !empty( $comment->user_id ) && ( $comment->user_id === $user_id ) ){
+			// If there is no auth_callback, you can only edit the meta for comments you can edit.
+			$caps = map_meta_cap( 'edit_comment', $user_id, $comment->comment_ID );
+		}
+		else {
+			// The default is requiring the capability itself.
+			$caps[] = $cap; 
+		}
+		break;
 	case 'unfiltered_upload':
 		if ( defined('ALLOW_UNFILTERED_UPLOADS') && ALLOW_UNFILTERED_UPLOADS && ( !is_multisite() || is_super_admin( $user_id ) )  )
 			$caps[] = $cap;
@@ -431,6 +520,43 @@
 		$caps = map_meta_cap( $tax->cap->$taxo_cap, $user_id, $term_id );
 
 		break;
+	case 'edit_term_meta':
+	case 'delete_term_meta':
+	case 'add_term_meta':
+		$term_id = ( ! empty( $args ) && ! empty( $args[0] ) )  ? (int)$args[0] : false;
+		$term = ( ! empty( $term_id ) ) ? get_term( $term_id ) : false;
+		if ( ! $term || is_wp_error( $term ) ) {
+			$caps[] = 'do_not_allow';
+			break;
+		}
+
+		$meta_key = isset( $args[ 1 ] ) ? $args[ 1 ] : false;
+
+		if ( $meta_key && ( has_filter( "auth_term_meta_{$meta_key}" ) ) ) {
+			/**
+			 * Filters whether the user is allowed to add term meta to a term.
+			 *
+			 * The dynamic portion of the hook name, `$meta_key`, refers to the
+			 * meta key passed to map_meta_cap().
+			 *
+			 * @since 4.7.0
+			 *
+			 * @param bool   $allowed  Whether the user can add the term meta. Default false.
+			 * @param string $meta_key The meta key.
+			 * @param int    $term_id  Term ID.
+			 * @param int    $user_id  User ID.
+			 * @param string $cap      Capability name.
+			 * @param array  $caps     User capabilities.
+			 */
+			$allowed = apply_filters( "auth_term_meta_{$meta_key}", false, $meta_key, $term_id, $user_id, $cap, $caps );
+			if ( ! $allowed ){
+				$caps[] = 'do_not_allow';
+			}
+		} else {
+			// The default is requiring the capability itself.
+			$caps[] = $cap; 
+		}
+		break;
 	case 'manage_post_tags':
 	case 'edit_categories':
 	case 'edit_post_tags':
Index: src/wp-includes/meta.php
===================================================================
--- src/wp-includes/meta.php	(revision 38935)
+++ src/wp-includes/meta.php	(working copy)
@@ -1038,9 +1038,7 @@
 	if ( empty( $args['auth_callback'] ) ) {
 		if ( is_protected_meta( $meta_key, $object_type ) ) {
 			$args['auth_callback'] = '__return_false';
-		} else {
-			$args['auth_callback'] = '__return_true';
-		}
+		} 
 	}
 
 	// Back-compat: old sanitize and auth callbacks are applied to all of an object type.
Index: tests/phpunit/tests/meta/registerMeta.php
===================================================================
--- tests/phpunit/tests/meta/registerMeta.php	(revision 38935)
+++ tests/phpunit/tests/meta/registerMeta.php	(working copy)
@@ -74,7 +74,7 @@
 					'description' => '',
 					'single' => false,
 					'sanitize_callback' => null,
-					'auth_callback' => '__return_true',
+					'auth_callback' => null,
 					'show_in_rest' => false,
 				),
 			),
@@ -96,7 +96,7 @@
 					'description' => '',
 					'single' => false,
 					'sanitize_callback' => null,
-					'auth_callback' => '__return_true',
+					'auth_callback' => null,
 					'show_in_rest' => false,
 				),
 			),
@@ -148,7 +148,7 @@
 					'description' => '',
 					'single' => false,
 					'sanitize_callback' => array( $this, '_new_sanitize_meta_cb' ),
-					'auth_callback' => '__return_true',
+					'auth_callback' => null,
 					'show_in_rest' => false,
 				),
 			),
Index: tests/phpunit/tests/post/meta.php
===================================================================
--- tests/phpunit/tests/post/meta.php	(revision 38935)
+++ tests/phpunit/tests/post/meta.php	(working copy)
@@ -237,4 +237,90 @@
 		$this->assertEquals($funky_meta, get_post_meta($this->post_id, 'test_funky_post_meta', true));
 
 	}
+
+	public function test_edit_post_meta_cap(){
+		$p = self::factory()->post->create( array( 'post_status' => 'publish' ) );
+		$u1 = self::factory()->user->create( array( 'role' => 'administrator' ) );
+		$u2 = self::factory()->user->create( array( 'role' => 'author' ) );
+
+		register_meta( 'post', 'foo', array() );
+
+		wp_set_current_user( $u1 );
+		$admin_update = false;
+		if( current_user_can( 'edit_post_meta', $p, 'foo' ) ){
+			$admin_update = update_post_meta( $p, 'foo', 'bar1' );
+		}
+		$this->assertNotEmpty( $admin_update );
+
+		wp_set_current_user( $u2 );
+		$auth_update = false;
+		if( current_user_can( 'edit_post_meta', $p, 'foo' ) ){
+			$auth_update = update_post_meta( $p, 'foo', 'bar2' );
+		}
+		$this->assertFalse( $auth_update );
+
+		unregister_meta_key( 'post', 'foo' );
+
+	}
+
+	public function test_edit_post_meta_with_auth_callback(){
+		$p = self::factory()->post->create( array( 'post_status' => 'publish' ) );
+		$u1 = self::factory()->user->create( array( 'role' => 'administrator' ) );
+		$u2 = self::factory()->user->create( array( 'role' => 'author' ) );
+
+		$args1 = array(
+			'auth_callback'	=> array( $this, 'test_edit_post_meta_auth_callback_false' )
+		);
+
+		$args2 = array(
+			'auth_callback'	=> array( $this, 'test_edit_post_meta_auth_callback_true' )
+		);
+
+		register_meta( 'post', 'foo1', $args1 );
+		register_meta( 'post', 'foo2', $args2 );
+
+		wp_set_current_user( $u1 );
+		$admin_update = false;
+		if( current_user_can( 'edit_post_meta', $p, 'foo1' ) ){
+			$admin_update = update_post_meta( $p, 'foo1', 'bar1' );
+		}
+		$this->assertFalse( $admin_update );
+
+		wp_set_current_user( $u2 );
+		$auth_update = false;
+		if( current_user_can( 'edit_post_meta', $p, 'foo2' ) ){
+			$auth_update = update_post_meta( $p, 'foo2', 'bar2' );
+		}
+		$this->assertNotEmpty( $auth_update );
+
+		unregister_meta_key( 'post', 'foo1' );
+		unregister_meta_key( 'post', 'foo2' );
+
+	}
+
+	public function test_user_can_edit_own_post_meta_as_default(){
+
+		$u = self::factory()->user->create( array( 'role' => 'author' ) );
+		$p = self::factory()->post->create( array( 'post_status' => 'publish', 'post_author' => $u ) );
+
+		register_meta( 'post', 'foo', array() );
+
+		wp_set_current_user( $u );
+		$update = false;
+		if( current_user_can( 'edit_post_meta', $p, 'foo' ) ){
+			$update = update_post_meta( $p, 'foo', 'bar' );
+		}
+		$this->assertNotEmpty( $update );
+
+		unregister_meta_key( 'post', 'foo' );
+
+	}
+
+	public function test_edit_post_meta_auth_callback_false(){
+		return false;
+	}
+
+	public function test_edit_post_meta_auth_callback_true(){
+		return true;
+	}
 }
Index: tests/phpunit/tests/term/meta.php
===================================================================
--- tests/phpunit/tests/term/meta.php	(revision 38935)
+++ tests/phpunit/tests/term/meta.php	(working copy)
@@ -407,4 +407,72 @@
 	public static function set_cache_results( $q ) {
 		$q->set( 'cache_results', true );
 	}
+
+	public function test_edit_term_meta_cap(){
+		$t 	= self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) );
+		$u1 = self::factory()->user->create( array( 'role' => 'administrator' ) );
+		$u2 = self::factory()->user->create( array( 'role' => 'author' ) );
+
+		register_meta( 'term', 'foo', array() );
+
+		wp_set_current_user( $u1 );
+		$admin_update = false;
+		if( current_user_can( 'edit_term_meta', $t, 'foo' ) ){
+			$admin_update = update_term_meta( $t, 'foo', 'bar1' );
+		}
+		$this->assertNotEmpty( $admin_update );
+
+		wp_set_current_user( $u2 );
+		$auth_update = false;
+		if( current_user_can( 'edit_term_meta', $t, 'foo' ) ){
+			$auth_update = update_term_meta( $t, 'foo', 'bar2' );
+		}
+		$this->assertFalse( $auth_update );
+
+		unregister_meta_key( 'term', 'foo' );
+
+	}
+
+	public function test_edit_term_meta_with_auth_callback(){
+		$t 	= self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) );
+		$u1 = self::factory()->user->create( array( 'role' => 'administrator' ) );
+		$u2 = self::factory()->user->create( array( 'role' => 'author' ) );
+
+		$args1 = array(
+			'auth_callback'	=> array( $this, 'test_edit_term_meta_auth_callback_false' )
+		);
+
+		$args2 = array(
+			'auth_callback'	=> array( $this, 'test_edit_term_meta_auth_callback_true' )
+		);
+
+		register_meta( 'term', 'foo1', $args1 );
+		register_meta( 'term', 'foo2', $args2 );
+
+		wp_set_current_user( $u1 );
+		$admin_update = false;
+		if( current_user_can( 'edit_term_meta', $t, 'foo1' ) ){
+			$admin_update = update_term_meta( $t, 'foo1', 'bar1' );
+		}
+		$this->assertFalse( $admin_update );
+
+		wp_set_current_user( $u2 );
+		$auth_update = false;
+		if( current_user_can( 'edit_term_meta', $t, 'foo2' ) ){
+			$auth_update = update_term_meta( $t, 'foo2', 'bar2' );
+		}
+		$this->assertNotEmpty( $auth_update );
+		
+		unregister_meta_key( 'term', 'foo1' );
+		unregister_meta_key( 'term', 'foo2' );
+
+	}
+
+	public function test_edit_term_meta_auth_callback_false(){
+		return false;
+	}
+
+	public function test_edit_term_meta_auth_callback_true(){
+		return true;
+	}
 }
Index: tests/phpunit/tests/user/capabilities.php
===================================================================
--- tests/phpunit/tests/user/capabilities.php	(revision 38935)
+++ tests/phpunit/tests/user/capabilities.php	(working copy)
@@ -318,7 +318,20 @@
 				// `manage_links` is a special case
 				$user_caps['manage_links'],
 				// `unfiltered_upload` is a special case
-				$user_caps['unfiltered_upload']
+				$user_caps['unfiltered_upload'],
+				// All of the object type meta capabilities are special cases. They are primitive capabilities but depend on a particular object.
+				$user_caps['edit_term_meta'],
+				$user_caps['add_term_meta'],
+				$user_caps['delete_term_meta'],
+				$user_caps['edit_post_meta'],
+				$user_caps['add_post_meta'],
+				$user_caps['delete_post_meta'],
+				$user_caps['edit_user_meta'],
+				$user_caps['add_user_meta'],
+				$user_caps['delete_user_meta'],
+				$user_caps['edit_comment_meta'],
+				$user_caps['add_comment_meta'],
+				$user_caps['delete_comment_meta']
 			);
 
 			$diff = array_diff( array_keys( $user_caps ), array_keys( $caps ) );
@@ -354,7 +367,20 @@
 			// `manage_links` is a special case in the caps tests:
 			$expected['manage_links'],
 			// `unfiltered_upload` is a special case in the caps tests:
-			$expected['unfiltered_upload']
+			$expected['unfiltered_upload'],
+			// All of the object type meta capabilities are special cases. They are primitive capabilities but depend on a particular object.
+			$expected['edit_term_meta'],
+			$expected['add_term_meta'],
+			$expected['delete_term_meta'],
+			$expected['edit_post_meta'],
+			$expected['add_post_meta'],
+			$expected['delete_post_meta'],
+			$expected['edit_user_meta'],
+			$expected['add_user_meta'],
+			$expected['delete_user_meta'],
+			$expected['edit_comment_meta'],
+			$expected['add_comment_meta'],
+			$expected['delete_comment_meta']
 		);
 
 		$expected = array_keys( $expected );
@@ -410,7 +436,7 @@
 			// Singular object meta capabilities (where an object ID is passed) are not tested:
 			$expected['remove_user'],
 			$expected['promote_user'],
-			$expected['edit_user'],
+			$expected['edit_user'],			
 			$expected['delete_post'],
 			$expected['delete_page'],
 			$expected['edit_post'],
@@ -418,14 +444,23 @@
 			$expected['read_post'],
 			$expected['read_page'],
 			$expected['publish_post'],
-			$expected['edit_post_meta'],
-			$expected['delete_post_meta'],
-			$expected['add_post_meta'],
 			$expected['edit_comment'],
 			$expected['edit_term'],
 			$expected['delete_term'],
-			$expected['assign_term'],
-			$expected['delete_user']
+			$expected['assign_term'],		
+			$expected['delete_user'],
+			$expected['edit_term_meta'],
+			$expected['add_term_meta'],
+			$expected['delete_term_meta'],
+			$expected['edit_comment_meta'],
+			$expected['add_comment_meta'],
+			$expected['delete_comment_meta'],
+			$expected['edit_post_meta'],
+			$expected['add_post_meta'],
+			$expected['delete_post_meta'],
+			$expected['edit_user_meta'],
+			$expected['add_user_meta'],
+			$expected['delete_user_meta']
 		);
 
 		$expected = array_keys( $expected );
