Restrict editing of posts with specific taxonomy to users with a matching meta value?

Is it possible to restrict multiple users to only be able to edit a
post (or posts) where those users have a custom meta key matching the
custom post’s custom taxonomy or meta key?

Yes, you can use user_has_cap which is a filter applied when current_user_can() or user_can() is called — these two functions are used for checking whether the current user or a specific user has the permission to do things like, editing posts, deleting terms, changing site options, etc.

So here’s a working example, tried with a custom post type with capability_type set to org_profile which is also the post type slug:

Note: Replace the org_profile with the correct post type, org_id with the correct meta key, and organization with the correct taxonomy slug, and if the user meta stores the term ID instead of slug or name, make sure you pass an integer to has_term(), e.g. has_term( (int) $user_org_id, ... ). ( PS: If you remove the comments, the function is actually pretty simple.. 🙂 )

add_filter( 'user_has_cap', 'wpse_393410', 10, 4 );
function wpse_393410( $allcaps, $caps, $args, $user ) {
    /* With the following, and assuming the current user's ID is 123:
        current_user_can( 'edit_post', 456 );
        user_can( 123, 'edit_post', 456 );

       $args would be array( 'edit_post', 123, 456 ).
       i.e. array( 'edit_post', <user ID>, <post ID> )
     */

    if ( ! empty( $args[2] ) && 'edit_post' === $args[0] &&
        'org_profile' === get_post_type( $args[2] )
    ) {
        // If the current user does NOT have any of the roles in the include list, we
        // do nothing.
        $include = array( 'org_user' );
        if ( empty( array_intersect( (array) $user->roles, $include ) ) ) {
            return $allcaps;
        }

        $user_org_id = get_user_meta( $args[1], 'org_id', true );

        // If the user meta is empty, we do nothing. Else, check if the post has a term
        // with the same SLUG OR NAME in the 'organization' taxonomy.
        // Note: We just need to disable one of the caps (in $caps).
        if ( $user_org_id && ! has_term( $user_org_id, 'organization', $args[2] ) ) {
            $allcaps[ $caps[0] ] = false;
        }
    }

    return $allcaps;
}

If you want to use the post meta instead, then replace this:

$user_org_id = get_user_meta( $args[1], 'org_id', true );

// If the user meta is empty, we do nothing. Else, check if the post has a term
// with the same SLUG OR NAME in the 'organization' taxonomy.
// Note: We just need to disable one of the caps (in $caps).
if ( $user_org_id && ! has_term( $user_org_id, 'organization', $args[2] ) ) {
    $allcaps[ $caps[0] ] = false;
}

With this:

$user_org_id = get_user_meta( $args[1], 'org_id', true );
$post_org_id = get_post_meta( $args[2], 'org_id', true );

// If the post OR user meta is empty, we do nothing. Else, check if the post meta
// matches the user meta.
// Note: We just need to disable one of the caps (in $caps).
if ( $post_org_id && $user_org_id && $user_org_id !== $post_org_id ) {
    $allcaps[ $caps[0] ] = false;
}

Additionally, in the above function, I used an include list ($include), but you can instead use an exclude list, if you want to, e.g. $exclude = array( 'administrator', 'custom' ); and then do ! empty( array_intersect( $roles, $exclude ) ).