From 168be649f8ffcc003cd868b92048705e86ed8a34 Mon Sep 17 00:00:00 2001
From: Jon Rhees <jrhees@caengineering.com>
Date: Thu, 27 Jun 2024 14:54:35 -0600
Subject: [PATCH 1/3] [DRLK] Bugfix: return INVALID_COMMAND when attempting to
 add/modify credential from a different fabric than the User/Credential's
 creator fabric

Add YAML test steps to verify correct behavior

Fixes #34119
---
 .../door-lock-server/door-lock-server.cpp     |  35 ++
 .../tests/suites/DL_UsersAndCredentials.yaml  | 328 +++++++++++++++++-
 2 files changed, 361 insertions(+), 2 deletions(-)

diff --git a/src/app/clusters/door-lock-server/door-lock-server.cpp b/src/app/clusters/door-lock-server/door-lock-server.cpp
index 528cbd6eb0971d..0cebca44a53e5c 100644
--- a/src/app/clusters/door-lock-server/door-lock-server.cpp
+++ b/src/app/clusters/door-lock-server/door-lock-server.cpp
@@ -788,6 +788,19 @@ void DoorLockServer::setCredentialCommandHandler(
             return;
         }
 
+        // return INVALID_COMMAND if the accessing fabric index doesn’t match the
+        // CreatorFabricIndex of the credential being modified
+        if (existingCredential.createdBy != fabricIdx)
+        {
+            ChipLogProgress(Zcl,
+                            "[createCredential] Unable to modify credential. Fabric index differs from creator fabric "
+                            "[endpointId=%d,credentialIndex=%d,creatorIdx=%d,modifierIdx=%d]",
+                            commandPath.mEndpointId, credentialIndex, existingCredential.createdBy, fabricIdx);
+
+            sendSetCredentialResponse(commandObj, commandPath, DlStatus::kInvalidField, 0, nextAvailableCredentialSlot);
+            return;
+        }
+
         // if userIndex is NULL then we're changing the programming user PIN
         if (userIndex.IsNull())
         {
@@ -2192,6 +2205,17 @@ DlStatus DoorLockServer::createNewCredentialAndAddItToUser(chip::EndpointId endp
         return DlStatus::kInvalidField;
     }
 
+    // return INVALID_COMMAND if the accessing fabric index doesn’t match the
+    // CreatorFabricIndex in the user record pointed to by UserIndex
+    if (user.createdBy != modifierFabricIdx)
+    {
+        ChipLogProgress(Zcl,
+                        "[createCredential] Unable to create credential for user created by different fabric "
+                        "[endpointId=%d,userIndex=%d,creatorIdx=%d,fabricIdx=%d]",
+                        endpointId, userIndex, user.createdBy, modifierFabricIdx);
+        return DlStatus::kInvalidField;
+    }
+
     // Add new credential to the user
     auto status = addCredentialToUser(endpointId, modifierFabricIdx, userIndex, credential);
     if (DlStatus::kSuccess != status)
@@ -2312,6 +2336,17 @@ DlStatus DoorLockServer::modifyCredentialForUser(chip::EndpointId endpointId, ch
         return DlStatus::kFailure;
     }
 
+    // return INVALID_COMMAND if the accessing fabric index doesn’t match the
+    // CreatorFabricIndex in the user record pointed to by UserIndex
+    if (user.createdBy != modifierFabricIdx)
+    {
+        ChipLogProgress(Zcl,
+                        "[createCredential] Unable to modify credential for user created by different fabric "
+                        "[endpointId=%d,userIndex=%d,creatorIdx=%d,fabricIdx=%d]",
+                        endpointId, userIndex, user.createdBy, modifierFabricIdx);
+        return DlStatus::kInvalidField;
+    }
+
     for (size_t i = 0; i < user.credentials.size(); ++i)
     {
         // appclusters, 5.2.4.40: user should already be associated with given credentialIndex
diff --git a/src/app/tests/suites/DL_UsersAndCredentials.yaml b/src/app/tests/suites/DL_UsersAndCredentials.yaml
index 5030145412db2f..c4a0c9d058497f 100644
--- a/src/app/tests/suites/DL_UsersAndCredentials.yaml
+++ b/src/app/tests/suites/DL_UsersAndCredentials.yaml
@@ -18,6 +18,18 @@ config:
     nodeId: 0x12344321
     cluster: "Door Lock"
     endpoint: 1
+    payload:
+        type: char_string
+        defaultValue: "MT:-24J0AFN00KA0648G00"
+    discriminator:
+        type: int16u
+        defaultValue: 3840
+    waitAfterCommissioning:
+        type: int16u
+        defaultValue: 1000
+    PakeVerifier:
+        type: octet_string
+        defaultValue: "hex:b96170aae803346884724fe9a3b287c30330c2a660375d17bb205a8cf1aecb350457f8ab79ee253ab6a8e46bb09e543ae422736de501e3db37d441fe344920d09548e4c18240630c4ff4913c53513839b7c07fcc0627a1b8573a149fcd1fa466cf"
 
 tests:
     - label: "Wait for the commissioned device to be retrieved"
@@ -64,6 +76,57 @@ tests:
           saveAs: NumberOfTotalUsersSupportedValue
           value: 10
 
+    #
+    # Commission to second fabric to facilitate testing SetCredential/SetUser fabric restrictions
+    #
+    - label: "Open Commissioning Window from alpha"
+      endpoint: 0
+      cluster: "Administrator Commissioning"
+      command: "OpenCommissioningWindow"
+      timedInteractionTimeoutMs: 10000
+      arguments:
+          values:
+              - name: "CommissioningTimeout"
+                value: 180
+              - name: "PAKEPasscodeVerifier"
+                value: PakeVerifier
+              - name: "Discriminator"
+                value: discriminator
+              - name: "Iterations"
+                value: 1000
+              - name: "Salt"
+                value: "SPAKE2P Key Salt"
+
+    - label: "Waiting after opening commissioning window"
+      cluster: "DelayCommands"
+      command: "WaitForMs"
+      arguments:
+          values:
+              - name: "ms"
+                value: waitAfterCommissioning
+
+    - label: "Commission from TH2"
+      identity: "beta"
+      endpoint: 0
+      cluster: "CommissionerCommands"
+      command: "PairWithCode"
+      arguments:
+          values:
+              - name: "nodeId"
+                value: nodeId
+              - name: "payload"
+                value: payload
+
+    - label: "Wait for the commissioned device to be retrieved for TH2"
+      endpoint: 0
+      identity: beta
+      cluster: "DelayCommands"
+      command: "WaitForCommissionee"
+      arguments:
+          values:
+              - name: "nodeId"
+                value: nodeId
+
     - label: "Read fails for user with index 0"
       command: "GetUser"
       arguments:
@@ -448,6 +511,131 @@ tests:
               - name: "NextUserIndex"
                 value: null
 
+    - label: "Modify UserStatus, UserType, CredentialRule for existing user from different fabric"
+      command: "SetUser"
+      identity: "beta"
+      timedInteractionTimeoutMs: 10000
+      arguments:
+          values:
+              - name: "OperationType"
+                value: 2
+              - name: "UserIndex"
+                value: 1
+              - name: "UserName"
+                value: null
+              - name: "UserUniqueID"
+                value: null
+              - name: "UserStatus"
+                value: 3
+              - name: "UserType"
+                value: 6
+              - name: "CredentialRule"
+                value: 2
+
+    - label: "Read the modified user back and verify its fields"
+      command: "GetUser"
+      arguments:
+          values:
+              - name: "UserIndex"
+                value: 1
+      response:
+          values:
+              - name: "UserIndex"
+                value: 1
+              - name: "UserName"
+                value: "test_user"
+              - name: "UserUniqueID"
+                value: 0x1BCDA0A0
+              - name: "UserStatus"
+                value: 3
+              - name: "UserType"
+                value: 6
+              - name: "CredentialRule"
+                value: 2
+              - name: "Credentials"
+                value: []
+              - name: "CreatorFabricIndex"
+                value: 1
+              - name: "LastModifiedFabricIndex"
+                value: 2
+              - name: "NextUserIndex"
+                value: null
+
+    - label: "Attempt to modify UserName for existing user from different fabric"
+      command: "SetUser"
+      identity: "beta"
+      timedInteractionTimeoutMs: 10000
+      arguments:
+          values:
+              - name: "OperationType"
+                value: 2
+              - name: "UserIndex"
+                value: 1
+              - name: "UserName"
+                value: "test_fab2"
+              - name: "UserUniqueID"
+                value: null
+              - name: "UserStatus"
+                value: null
+              - name: "UserType"
+                value: null
+              - name: "CredentialRule"
+                value: null
+      response:
+          error: INVALID_COMMAND
+
+    - label: "Attempt to modify userUniqueId for existing user from different fabric"
+      command: "SetUser"
+      identity: "beta"
+      timedInteractionTimeoutMs: 10000
+      arguments:
+          values:
+              - name: "OperationType"
+                value: 2
+              - name: "UserIndex"
+                value: 1
+              - name: "UserName"
+                value: null
+              - name: "UserUniqueID"
+                value: 0x1234ABCD
+              - name: "UserStatus"
+                value: null
+              - name: "UserType"
+                value: null
+              - name: "CredentialRule"
+                value: null
+      response:
+          error: INVALID_COMMAND
+
+    - label: "Read the modified user back and verify its fields"
+      command: "GetUser"
+      arguments:
+          values:
+              - name: "UserIndex"
+                value: 1
+      response:
+          values:
+              - name: "UserIndex"
+                value: 1
+              - name: "UserName"
+                value: "test_user"
+              - name: "UserUniqueID"
+                value: 0x1BCDA0A0
+              - name: "UserStatus"
+                value: 3
+              - name: "UserType"
+                value: 6
+              - name: "CredentialRule"
+                value: 2
+              - name: "Credentials"
+                value: []
+              - name: "CreatorFabricIndex"
+                value: 1
+              - name: "LastModifiedFabricIndex"
+                value: 2
+              - name: "NextUserIndex"
+                value: null
+
     - label: "Add another user with non-default fields"
       command: "SetUser"
       timedInteractionTimeoutMs: 10000
@@ -1327,6 +1515,81 @@ tests:
               - name: "NextCredentialIndex"
                 value: 2
 
+    - label: "Attempt to create new RFID credential from different fabric and add it to existing user"
+      command: "SetCredential"
+      identity: "beta"
+      timedInteractionTimeoutMs: 10000
+      arguments:
+          values:
+              - name: "OperationType"
+                value: 0
+              - name: "Credential"
+                value: { CredentialType: 2, CredentialIndex: 1 }
+              - name: "CredentialData"
+                value: "rfid_data_123456"
+              - name: "UserIndex"
+                value: 1
+              - name: "UserStatus"
+                value: null
+              - name: "UserType"
+                value: null
+      response:
+          values:
+              - name: "Status"
+                value: 0x85
+              - name: "UserIndex"
+                value: null
+              - name: "NextCredentialIndex"
+                value: 2
+
+    - label: "Verify user has not been modified"
+      command: "GetUser"
+      arguments:
+          values:
+              - name: "UserIndex"
+                value: 1
+      response:
+          values:
+              - name: "UserIndex"
+                value: 1
+              - name: "UserName"
+                value: ""
+              - name: "UserUniqueID"
+                value: null
+              - name: "UserStatus"
+                value: 1
+              - name: "UserType"
+                value: 0
+              - name: "CredentialRule"
+                value: 0
+              - name: "Credentials"
+                value: [{ CredentialType: 1, CredentialIndex: 1 }]
+              - name: "CreatorFabricIndex"
+                value: 1
+              - name: "LastModifiedFabricIndex"
+                value: 1
+              - name: "NextUserIndex"
+                value: null
+
+    - label: "Verify no credential has been created"
+      command: "GetCredentialStatus"
+      arguments:
+          values:
+              - name: "Credential"
+                value: { CredentialType: 2, CredentialIndex: 1 }
+      response:
+          values:
+              - name: "CredentialExists"
+                value: false
+              - name: "UserIndex"
+                value: null
+              - name: "CreatorFabricIndex"
+                value: null
+              - name: "LastModifiedFabricIndex"
+                value: null
+              - name: "NextCredentialIndex"
+                value: null
+
     - label: "Create new RFID credential and add it to existing user"
       command: "SetCredential"
       timedInteractionTimeoutMs: 10000
@@ -1405,6 +1668,67 @@ tests:
               - name: "NextCredentialIndex"
                 value: null
 
+
+
+    - label: "Attempt to modify credentialData of existing RFID credential from different fabric"
+      command: "SetCredential"
+      identity: "beta"
+      timedInteractionTimeoutMs: 10000
+      arguments:
+          values:
+              - name: "OperationType"
+                value: 2
+              - name: "Credential"
+                value: { CredentialType: 2, CredentialIndex: 1 }
+              - name: "CredentialData"
+                value: "rfid_data_654321"
+              - name: "UserIndex"
+                value: 1
+              - name: "UserStatus"
+                value: null
+              - name: "UserType"
+                value: null
+      response:
+          values:
+              - name: "Status"
+                value: 0x85
+              - name: "UserIndex"
+                value: null
+              - name: "NextCredentialIndex"
+                value: 2
+
+    - label:
+          "Verify that credential was not changed by attempting to create
+          new credential with unmodified data"
+      command: "SetCredential"
+      timedInteractionTimeoutMs: 10000
+      arguments:
+          values:
+              - name: "OperationType"
+                value: 0
+              - name: "Credential"
+                value: { CredentialType: 2, CredentialIndex: 2 }
+              - name: "CredentialData"
+                value: "rfid_data_123456"
+              - name: "UserIndex"
+                value: null
+              - name: "UserStatus"
+                value: null
+              - name: "UserType"
+                value: null
+      response:
+          values:
+              - name: "Status"
+                value: 0x02
+              - name: "UserIndex"
+                value: null
+              - name: "NextCredentialIndex"
+                value: 3
+
+
+
+
+
     - label: "Create new RFID credential and user with index 0 fails"
       command: "SetCredential"
       timedInteractionTimeoutMs: 10000
@@ -2054,10 +2378,10 @@ tests:
     - label: "Make sure a LockUserChange event was generated"
       command: "readEvent"
       event: "LockUserChange"
-      # I wish there were a way to not hardcode this 25, but it's experimentally
+      # I wish there were a way to not hardcode this 27, but it's experimentally
       # determined: doing a read without an eventNumber filter here shows 24
       # LockUserChange events before this removal.
-      eventNumber: 25
+      eventNumber: 27
       response:
           value:
               {

From 6ad8e6f1f3676a43f684607346c0287472bed286 Mon Sep 17 00:00:00 2001
From: "Restyled.io" <commits@restyled.io>
Date: Thu, 27 Jun 2024 20:59:07 +0000
Subject: [PATCH 2/3] Restyled by prettier-yaml

---
 .../tests/suites/DL_UsersAndCredentials.yaml  | 29 ++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/src/app/tests/suites/DL_UsersAndCredentials.yaml b/src/app/tests/suites/DL_UsersAndCredentials.yaml
index c4a0c9d058497f..15f389f43889e9 100644
--- a/src/app/tests/suites/DL_UsersAndCredentials.yaml
+++ b/src/app/tests/suites/DL_UsersAndCredentials.yaml
@@ -511,7 +511,9 @@ tests:
               - name: "NextUserIndex"
                 value: null
 
-    - label: "Modify UserStatus, UserType, CredentialRule for existing user from different fabric"
+    - label:
+          "Modify UserStatus, UserType, CredentialRule for existing user from
+          different fabric"
       command: "SetUser"
       identity: "beta"
       timedInteractionTimeoutMs: 10000
@@ -561,7 +563,8 @@ tests:
               - name: "NextUserIndex"
                 value: null
 
-    - label: "Attempt to modify UserName for existing user from different fabric"
+    - label:
+          "Attempt to modify UserName for existing user from different fabric"
       command: "SetUser"
       identity: "beta"
       timedInteractionTimeoutMs: 10000
@@ -584,7 +587,9 @@ tests:
       response:
           error: INVALID_COMMAND
 
-    - label: "Attempt to modify userUniqueId for existing user from different fabric"
+    - label:
+          "Attempt to modify userUniqueId for existing user from different
+          fabric"
       command: "SetUser"
       identity: "beta"
       timedInteractionTimeoutMs: 10000
@@ -1515,7 +1520,9 @@ tests:
               - name: "NextCredentialIndex"
                 value: 2
 
-    - label: "Attempt to create new RFID credential from different fabric and add it to existing user"
+    - label:
+          "Attempt to create new RFID credential from different fabric and add
+          it to existing user"
       command: "SetCredential"
       identity: "beta"
       timedInteractionTimeoutMs: 10000
@@ -1668,9 +1675,9 @@ tests:
               - name: "NextCredentialIndex"
                 value: null
 
-
-
-    - label: "Attempt to modify credentialData of existing RFID credential from different fabric"
+    - label:
+          "Attempt to modify credentialData of existing RFID credential from
+          different fabric"
       command: "SetCredential"
       identity: "beta"
       timedInteractionTimeoutMs: 10000
@@ -1698,8 +1705,8 @@ tests:
                 value: 2
 
     - label:
-          "Verify that credential was not changed by attempting to create
-          new credential with unmodified data"
+          "Verify that credential was not changed by attempting to create new
+          credential with unmodified data"
       command: "SetCredential"
       timedInteractionTimeoutMs: 10000
       arguments:
@@ -1725,10 +1732,6 @@ tests:
               - name: "NextCredentialIndex"
                 value: 3
 
-
-
-
-
     - label: "Create new RFID credential and user with index 0 fails"
       command: "SetCredential"
       timedInteractionTimeoutMs: 10000

From 28d1fcddcdf8c16a4039836e20afb081d82af18d Mon Sep 17 00:00:00 2001
From: jrhees-cae <61466710+jrhees-cae@users.noreply.github.com>
Date: Mon, 8 Jul 2024 08:12:42 -0600
Subject: [PATCH 3/3] Update src/app/tests/suites/DL_UsersAndCredentials.yaml

Co-authored-by: Andrei Litvin <andy314@gmail.com>
---
 src/app/tests/suites/DL_UsersAndCredentials.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/app/tests/suites/DL_UsersAndCredentials.yaml b/src/app/tests/suites/DL_UsersAndCredentials.yaml
index 15f389f43889e9..6edbef1d645aa9 100644
--- a/src/app/tests/suites/DL_UsersAndCredentials.yaml
+++ b/src/app/tests/suites/DL_UsersAndCredentials.yaml
@@ -2382,7 +2382,7 @@ tests:
       command: "readEvent"
       event: "LockUserChange"
       # I wish there were a way to not hardcode this 27, but it's experimentally
-      # determined: doing a read without an eventNumber filter here shows 24
+      # determined: doing a read without an eventNumber filter here shows 26
       # LockUserChange events before this removal.
       eventNumber: 27
       response: