Index: /trunk/src/wp-includes/capabilities.php
===================================================================
--- /trunk/src/wp-includes/capabilities.php	(revision 39178)
+++ /trunk/src/wp-includes/capabilities.php	(revision 39179)
@@ -243,54 +243,75 @@
 	case 'delete_post_meta':
 	case 'add_post_meta':
-		$post = get_post( $args[0] );
-		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}" ) ) ) {
-			/**
-			 * Filters whether the user is allowed to add post meta to a post.
-			 *
-			 * The dynamic portion of the hook name, `$meta_key`, refers to the
-			 * meta key passed to map_meta_cap().
-			 *
-			 * @since 3.3.0
-			 *
-			 * @param bool   $allowed  Whether the user can add the post meta. Default false.
-			 * @param string $meta_key The meta key.
-			 * @param int    $post_id  Post ID.
-			 * @param int    $user_id  User ID.
-			 * @param string $cap      Capability name.
-			 * @param array  $caps     User capabilities.
-			 */
-			$allowed = apply_filters( "auth_post_meta_{$meta_key}", false, $meta_key, $post->ID, $user_id, $cap, $caps );
-
-			/**
-			 * Filters whether the user is allowed to add post meta to a post of a given type.
-			 *
-			 * The dynamic portions of the hook name, `$meta_key` and `$post_type`,
-			 * refer to the meta key passed to map_meta_cap() and the post type, respectively.
-			 *
-			 * @since 4.6.0
-			 *
-			 * @param bool   $allowed  Whether the user can add the post meta. Default false.
-			 * @param string $meta_key The meta key.
-			 * @param int    $post_id  Post ID.
-			 * @param int    $user_id  User ID.
-			 * @param string $cap      Capability name.
-			 * @param array  $caps     User capabilities.
-			 */
-			$allowed = apply_filters( "auth_post_{$post_type}_meta_{$meta_key}", $allowed, $meta_key, $post->ID, $user_id, $cap, $caps );
-
-			if ( ! $allowed )
+	case 'edit_comment_meta':
+	case 'delete_comment_meta':
+	case 'add_comment_meta':
+	case 'edit_term_meta':
+	case 'delete_term_meta':
+	case 'add_term_meta':
+	case 'edit_user_meta':
+	case 'delete_user_meta':
+	case 'add_user_meta':
+		list( $_, $object_type, $_ ) = explode( '_', $cap );
+		$object_id = (int) $args[0];
+
+		switch ( $object_type ) {
+			case 'post':
+				$post = get_post( $object_id );
+				if ( ! $post ) {
+					break;
+				}
+
+				$sub_type = get_post_type( $post );
+				break;
+
+			case 'comment':
+				$comment = get_comment( $object_id );
+				if ( ! $comment ) {
+					break;
+				}
+
+				$sub_type = empty( $comment->comment_type ) ? 'comment' : $comment->comment_type;
+				break;
+
+			case 'term':
+				$term = get_term( $object_id );
+				if ( ! $term ) {
+					break;
+				}
+
+				$sub_type = $term->taxonomy;
+				break;
+
+			case 'user':
+				$user = get_user_by( 'id', $object_id );
+				if ( ! $user ) {
+					break;
+				}
+
+				$sub_type = 'user';
+				break;
+		}
+
+		if ( empty( $sub_type ) ) {
+			$caps[] = 'do_not_allow';
+			break;
+		}
+
+		$caps = map_meta_cap( "edit_{$object_type}", $user_id, $object_id );
+
+		$meta_key = isset( $args[1] ) ? $args[1] : false;
+
+		$has_filter = has_filter( "auth_{$object_type}_meta_{$meta_key}" ) || has_filter( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}" );
+		if ( $meta_key && $has_filter ) {
+			/** This filter is documented in wp-includes/meta.php */
+			$allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}", false, $meta_key, $object_id, $user_id, $cap, $caps );
+
+			/** This filter is documented in wp-includes/meta.php */
+			$allowed = apply_filters( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
+
+			if ( ! $allowed ) {
 				$caps[] = $cap;
-		} elseif ( $meta_key && is_protected_meta( $meta_key, 'post' ) ) {
+			}
+		} elseif ( $meta_key && is_protected_meta( $meta_key, $object_type ) ) {
 			$caps[] = $cap;
 		}
Index: /trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php
===================================================================
--- /trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php	(revision 39178)
+++ /trunk/src/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php	(revision 39179)
@@ -632,5 +632,5 @@
 				return new WP_Error( 'rest_comment_failed_edit', __( 'Updating comment status failed.' ), array( 'status' => 500 ) );
 			}
-		} else {
+		} elseif ( ! empty( $prepared_args ) ) {
 			if ( is_wp_error( $prepared_args ) ) {
 				return $prepared_args;
Index: /trunk/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php
===================================================================
--- /trunk/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php	(revision 39178)
+++ /trunk/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php	(revision 39179)
@@ -123,5 +123,4 @@
 	public function update_value( $request, $object_id ) {
 		$fields = $this->get_registered_fields();
-
 		foreach ( $fields as $name => $args ) {
 			if ( ! array_key_exists( $name, $request ) ) {
@@ -160,5 +159,6 @@
 	 */
 	protected function delete_meta_value( $object_id, $name ) {
-		if ( ! current_user_can( 'delete_post_meta', $object_id, $name ) ) {
+		$meta_type = $this->get_meta_type();
+		if ( ! current_user_can( "delete_{$meta_type}_meta", $object_id, $name ) ) {
 			return new WP_Error(
 				'rest_cannot_delete',
@@ -168,5 +168,5 @@
 		}
 
-		if ( ! delete_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ) ) ) {
+		if ( ! delete_metadata( $meta_type, $object_id, wp_slash( $name ) ) ) {
 			return new WP_Error(
 				'rest_meta_database_error',
@@ -193,5 +193,6 @@
 	 */
 	protected function update_multi_meta_value( $object_id, $name, $values ) {
-		if ( ! current_user_can( 'edit_post_meta', $object_id, $name ) ) {
+		$meta_type = $this->get_meta_type();
+		if ( ! current_user_can( "edit_{$meta_type}_meta", $object_id, $name ) ) {
 			return new WP_Error(
 				'rest_cannot_update',
@@ -201,5 +202,5 @@
 		}
 
-		$current = get_metadata( $this->get_meta_type(), $object_id, $name, false );
+		$current = get_metadata( $meta_type, $object_id, $name, false );
 
 		$to_remove = $current;
@@ -228,5 +229,5 @@
 
 		foreach ( $to_remove as $value ) {
-			if ( ! delete_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
+			if ( ! delete_metadata( $meta_type, $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
 				return new WP_Error(
 					'rest_meta_database_error',
@@ -238,5 +239,5 @@
 
 		foreach ( $to_add as $value ) {
-			if ( ! add_metadata( $this->get_meta_type(), $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
+			if ( ! add_metadata( $meta_type, $object_id, wp_slash( $name ), wp_slash( $value ) ) ) {
 				return new WP_Error(
 					'rest_meta_database_error',
@@ -262,5 +263,6 @@
 	 */
 	protected function update_meta_value( $object_id, $name, $value ) {
-		if ( ! current_user_can( 'edit_post_meta', $object_id, $name ) ) {
+		$meta_type = $this->get_meta_type();
+		if ( ! current_user_can(  "edit_{$meta_type}_meta", $object_id, $name ) ) {
 			return new WP_Error(
 				'rest_cannot_update',
@@ -270,5 +272,4 @@
 		}
 
-		$meta_type  = $this->get_meta_type();
 		$meta_key   = wp_slash( $name );
 		$meta_value = wp_slash( $value );
Index: /trunk/tests/phpunit/tests/user/capabilities.php
===================================================================
--- /trunk/tests/phpunit/tests/user/capabilities.php	(revision 39178)
+++ /trunk/tests/phpunit/tests/user/capabilities.php	(revision 39179)
@@ -432,8 +432,17 @@
 			$expected['add_post_meta'],
 			$expected['edit_comment'],
+			$expected['edit_comment_meta'],
+			$expected['delete_comment_meta'],
+			$expected['add_comment_meta'],
 			$expected['edit_term'],
 			$expected['delete_term'],
 			$expected['assign_term'],
-			$expected['delete_user']
+			$expected['edit_term_meta'],
+			$expected['delete_term_meta'],
+			$expected['add_term_meta'],
+			$expected['delete_user'],
+			$expected['edit_user_meta'],
+			$expected['delete_user_meta'],
+			$expected['add_user_meta']
 		);
 
@@ -1664,3 +1673,84 @@
 		$wp_roles->reinit();
 	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_no_one_can_edit_user_meta_for_non_existent_term() {
+		wp_set_current_user( self::$super_admin->ID );
+		$this->assertFalse( current_user_can( 'edit_user_meta', 999999 ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_user_can_edit_user_meta() {
+		wp_set_current_user( self::$users['administrator']->ID );
+		if ( is_multisite() ) {
+			grant_super_admin( self::$users['administrator']->ID );
+		}
+		$this->assertTrue( current_user_can( 'edit_user_meta', self::$users['subscriber']->ID, 'foo' ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_user_cannot_edit_user_meta() {
+		wp_set_current_user( self::$users['editor']->ID );
+		$this->assertFalse( current_user_can( 'edit_user_meta', self::$users['subscriber']->ID, 'foo' ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_no_one_can_delete_user_meta_for_non_existent_term() {
+		wp_set_current_user( self::$super_admin->ID );
+		$this->assertFalse( current_user_can( 'delete_user_meta', 999999, 'foo' ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_user_can_delete_user_meta() {
+		wp_set_current_user( self::$users['administrator']->ID );
+		if ( is_multisite() ) {
+			grant_super_admin( self::$users['administrator']->ID );
+		}
+		$this->assertTrue( current_user_can( 'delete_user_meta', self::$users['subscriber']->ID, 'foo' ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_user_cannot_delete_user_meta() {
+		wp_set_current_user( self::$users['editor']->ID );
+		$this->assertFalse( current_user_can( 'delete_user_meta', self::$users['subscriber']->ID, 'foo' ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_no_one_can_add_user_meta_for_non_existent_term() {
+		wp_set_current_user( self::$super_admin->ID );
+		$this->assertFalse( current_user_can( 'add_user_meta', 999999, 'foo' ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_user_can_add_user_meta() {
+		wp_set_current_user( self::$users['administrator']->ID );
+		if ( is_multisite() ) {
+			grant_super_admin( self::$users['administrator']->ID );
+		}
+		$this->assertTrue( current_user_can( 'add_user_meta', self::$users['subscriber']->ID, 'foo' ) );
+	}
+
+	/**
+	 * @ticket 38412
+	 */
+	public function test_user_cannot_add_user_meta() {
+		wp_set_current_user( self::$users['editor']->ID );
+		$this->assertFalse( current_user_can( 'add_user_meta', self::$users['subscriber']->ID, 'foo' ) );
+	}
 }
