Commit eb42b9b3b24 for woocommerce

commit eb42b9b3b24724b31587e06e4403214761f18baa
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date:   Wed Oct 7 13:31:46 2026 +0200

    Prevent shop managers from editing users with additional roles they cannot edit (#69527)

    Co-authored-by: Thomas Roberts <5656702+opr@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/fix-shop-manager-mixed-role-permissions b/plugins/woocommerce/changelog/fix-shop-manager-mixed-role-permissions
new file mode 100644
index 00000000000..64a8ccf4be4
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-shop-manager-mixed-role-permissions
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent shop managers from editing users who have additional roles that cannot be edited by shop managers.
diff --git a/plugins/woocommerce/includes/wc-rest-functions.php b/plugins/woocommerce/includes/wc-rest-functions.php
index 4aa7cca12f3..deeb5ed1e65 100644
--- a/plugins/woocommerce/includes/wc-rest-functions.php
+++ b/plugins/woocommerce/includes/wc-rest-functions.php
@@ -275,10 +275,10 @@ function wc_rest_check_user_permissions( $context = 'read', $object_id = 0 ) {
 		$shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) );

 		if ( isset( $user_data->roles ) ) {
-			$can_manage_users = array_intersect( $user_data->roles, array_unique( $shop_manager_editable_roles ) );
+			$can_manage_user = ! empty( $user_data->roles ) && empty( array_diff( $user_data->roles, array_unique( $shop_manager_editable_roles ) ) );

-			// Check if Shop Manager can edit customer or with the is same shop manager.
-			if ( 0 < count( $can_manage_users ) || intval( $object_id ) === intval( get_current_user_id() ) ) {
+			// Check if the Shop Manager can edit the user or is editing themselves.
+			if ( $can_manage_user || intval( $object_id ) === intval( get_current_user_id() ) ) {
 				$permission = current_user_can( $contexts[ $context ], $object_id );
 			}
 		}
diff --git a/plugins/woocommerce/includes/wc-user-functions.php b/plugins/woocommerce/includes/wc-user-functions.php
index 708ac3b89ba..727b8b5eda5 100644
--- a/plugins/woocommerce/includes/wc-user-functions.php
+++ b/plugins/woocommerce/includes/wc-user-functions.php
@@ -748,7 +748,8 @@ function wc_modify_map_meta_cap( $caps, $cap, $user_id, $args ) {
 						break;
 					}
 					$shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
-					if ( ! empty( $userdata->roles ) && ! array_intersect( $userdata->roles, $shop_manager_editable_roles ) ) {
+					$can_manage_user             = ! empty( $userdata->roles ) && empty( array_diff( $userdata->roles, array_unique( $shop_manager_editable_roles ) ) );
+					if ( ! $can_manage_user ) {
 						$caps[] = 'do_not_allow';
 					}
 				}
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-user-functions.php b/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-user-functions.php
index 8d4f6a755a1..a5edbfce37d 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-user-functions.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/util/class-wc-tests-user-functions.php
@@ -129,6 +129,34 @@ class WC_Tests_User_Functions extends WC_Unit_Test_Case {
 		$this->assertEquals( array( 'edit_users' ), $caps );
 	}

+	/**
+	 * @testdox A shop manager cannot edit a customer who also has a custom role.
+	 */
+	public function test_shop_manager_cannot_edit_customer_with_extra_custom_role() {
+		$options_role = 'test_manage_options_role';
+		add_role(
+			$options_role,
+			'Test manage options role',
+			array(
+				'manage_options' => true,
+			)
+		);
+
+		try {
+			$manager_id       = self::factory()->user->create( array( 'role' => 'shop_manager' ) );
+			$customer_id      = self::factory()->user->create( array( 'role' => 'customer' ) );
+			$customer_only_id = self::factory()->user->create( array( 'role' => 'customer' ) );
+			$customer         = new WP_User( $customer_id );
+			$customer->add_role( $options_role );
+			wp_set_current_user( $manager_id );
+
+			$this->assertTrue( current_user_can( 'edit_user', $customer_only_id ), 'A shop manager can edit a customer-only user.' );
+			$this->assertFalse( current_user_can( 'edit_user', $customer_id ), 'A shop manager cannot edit a customer with a custom role.' );
+		} finally {
+			remove_role( $options_role );
+		}
+	}
+
 	/**
 	 * Data provider for test_wc_modify_map_meta_cap_invalid_user_id.
 	 *
diff --git a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller-tests.php b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller-tests.php
index 39f1c50b1f1..3c1760aed02 100644
--- a/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller-tests.php
+++ b/plugins/woocommerce/tests/php/includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller-tests.php
@@ -137,6 +137,24 @@ class WC_REST_Customers_Controller_Test extends WC_Unit_Test_Case {
 		$this->assertEquals( 'customer', $customer->get_role() );
 	}

+	/**
+	 * @testdox A shop manager cannot update a customer who also has a role outside the editable roles.
+	 */
+	public function test_shop_manager_cannot_update_user_with_extra_role(): void {
+		$customer = new WP_User( $this->customer_id );
+		$customer->add_role( 'administrator' );
+
+		$api_request = new WP_REST_Request( 'PUT', '/wc/v3/customers/' );
+		$api_request->set_param( 'id', $this->customer_id );
+		$api_request->set_param( 'first_name', 'Test' );
+		wp_set_current_user( $this->shop_manager_id );
+
+		$result = $this->sut->update_item_permissions_check( $api_request );
+
+		$this->assertWPError( $result, 'A shop manager cannot update a mixed-role administrator.' );
+		$this->assertSame( 'woocommerce_rest_cannot_edit', $result->get_error_code() );
+	}
+
 	/**
 	 * @testDox Test deleting customers.
 	 */