@@ -145,7 +145,7 @@ func TestHandleSelfUserCredentialUpdateRequest_Success(t *testing.T) {
145145
146146 mockSvc := NewUserServiceInterfaceMock (t )
147147 credentialsJSON := json .RawMessage (`{"password":[{"value":"Secret123!"}]}` )
148- mockSvc .On ("UpdateUserCredentials " , mock .Anything , userID , credentialsJSON ).Return (nil )
148+ mockSvc .On ("UpdateSelfUserCredentials " , mock .Anything , userID , "" , credentialsJSON ).Return (nil )
149149
150150 handler := newUserHandler (mockSvc )
151151 req := httptest .NewRequest (http .MethodPost , "/users/me/update-credentials" ,
@@ -165,7 +165,7 @@ func TestHandleSelfUserCredentialUpdateRequest_StringValue(t *testing.T) {
165165
166166 mockSvc := NewUserServiceInterfaceMock (t )
167167 credentialsJSON := json .RawMessage (`{"password":"plaintext-password"}` )
168- mockSvc .On ("UpdateUserCredentials " , mock .Anything , userID , credentialsJSON ).Return (nil )
168+ mockSvc .On ("UpdateSelfUserCredentials " , mock .Anything , userID , "" , credentialsJSON ).Return (nil )
169169
170170 handler := newUserHandler (mockSvc )
171171 req := httptest .NewRequest (http .MethodPost , "/users/me/update-credentials" ,
@@ -241,7 +241,7 @@ func TestHandleSelfUserCredentialUpdateRequest_ErrorCases(t *testing.T) {
241241 for _ , tc := range testCases {
242242 t .Run (tc .name , func (t * testing.T ) {
243243 mockSvc := NewUserServiceInterfaceMock (t )
244- mockSvc .On ("UpdateUserCredentials " , mock .Anything , userID , tc .mockJSON ).Return (tc .mockError )
244+ mockSvc .On ("UpdateSelfUserCredentials " , mock .Anything , userID , "" , tc .mockJSON ).Return (tc .mockError )
245245
246246 handler := newUserHandler (mockSvc )
247247 req := httptest .NewRequest (http .MethodPost , "/users/me/update-credentials" ,
@@ -267,7 +267,7 @@ func TestHandleSelfUserCredentialUpdateRequest_MultipleCredentialTypes(t *testin
267267 mockSvc := NewUserServiceInterfaceMock (t )
268268 // Test that multiple credential types are updated in a single atomic call
269269 credentialsJSON := json .RawMessage (`{"password":"new-password","pin":"1234"}` )
270- mockSvc .On ("UpdateUserCredentials " , mock .Anything , userID , credentialsJSON ).Return (nil )
270+ mockSvc .On ("UpdateSelfUserCredentials " , mock .Anything , userID , "" , credentialsJSON ).Return (nil )
271271
272272 handler := newUserHandler (mockSvc )
273273 req := httptest .NewRequest (http .MethodPost , "/users/me/update-credentials" ,
@@ -279,8 +279,8 @@ func TestHandleSelfUserCredentialUpdateRequest_MultipleCredentialTypes(t *testin
279279
280280 require .Equal (t , http .StatusNoContent , rr .Code )
281281 require .Equal (t , 0 , rr .Body .Len ())
282- // Verify that UpdateUserCredentials was called exactly once with all credentials
283- mockSvc .AssertNumberOfCalls (t , "UpdateUserCredentials " , 1 )
282+ // Verify that UpdateSelfUserCredentials was called exactly once with all credentials
283+ mockSvc .AssertNumberOfCalls (t , "UpdateSelfUserCredentials " , 1 )
284284}
285285
286286func TestHandleUserCredentialUpdateRequest_Success (t * testing.T ) {
@@ -1342,3 +1342,78 @@ func TestHandleSelfUserMetadataGetRequest_ServiceError(t *testing.T) {
13421342
13431343 require .Equal (t , http .StatusNotFound , rr .Code )
13441344}
1345+
1346+ // ─────────────────────────────────────────────────────────────────────────────
1347+ // Self credential update – current-password plumbing
1348+ // ─────────────────────────────────────────────────────────────────────────────
1349+
1350+ func TestHandleSelfUserCredentialUpdateRequest_ForwardsCurrentPassword (t * testing.T ) {
1351+ userID := testUserID789
1352+ authCtx := security .NewSecurityContextForTest (userID , "" , "" , nil , nil )
1353+
1354+ mockSvc := NewUserServiceInterfaceMock (t )
1355+ credentialsJSON := json .RawMessage (`{"password":"n3wP@ssword!"}` )
1356+ mockSvc .
1357+ On ("UpdateSelfUserCredentials" , mock .Anything , userID , "0ldP@ssword!" , credentialsJSON ).
1358+ Return (nil )
1359+
1360+ handler := newUserHandler (mockSvc )
1361+ req := httptest .NewRequest (http .MethodPost , "/users/me/update-credentials" ,
1362+ bytes .NewBufferString (`{"currentPassword":"0ldP@ssword!","attributes":{"password":"n3wP@ssword!"}}` ))
1363+ req = req .WithContext (security .WithSecurityContextTest (req .Context (), authCtx ))
1364+ rr := httptest .NewRecorder ()
1365+
1366+ handler .HandleSelfUserCredentialUpdateRequest (rr , req )
1367+
1368+ require .Equal (t , http .StatusNoContent , rr .Code )
1369+ mockSvc .AssertNumberOfCalls (t , "UpdateSelfUserCredentials" , 1 )
1370+ }
1371+
1372+ func TestHandleSelfUserCredentialUpdateRequest_InvalidCurrentPasswordReturns403 (t * testing.T ) {
1373+ userID := testUserID789
1374+ authCtx := security .NewSecurityContextForTest (userID , "" , "" , nil , nil )
1375+
1376+ mockSvc := NewUserServiceInterfaceMock (t )
1377+ mockSvc .
1378+ On ("UpdateSelfUserCredentials" , mock .Anything , userID , "wrong" , mock .Anything ).
1379+ Return (& ErrorInvalidCurrentPassword )
1380+
1381+ handler := newUserHandler (mockSvc )
1382+ req := httptest .NewRequest (http .MethodPost , "/users/me/update-credentials" ,
1383+ bytes .NewBufferString (`{"currentPassword":"wrong","attributes":{"password":"n3wP@ssword!"}}` ))
1384+ req = req .WithContext (security .WithSecurityContextTest (req .Context (), authCtx ))
1385+ rr := httptest .NewRecorder ()
1386+
1387+ handler .HandleSelfUserCredentialUpdateRequest (rr , req )
1388+
1389+ // 403 is what the SDK maps onto the current-password field, distinct from 401's
1390+ // "re-authenticate" meaning, so the status matters.
1391+ require .Equal (t , http .StatusForbidden , rr .Code )
1392+
1393+ var errResp apierror.ErrorResponse
1394+ require .NoError (t , json .NewDecoder (rr .Body ).Decode (& errResp ))
1395+ require .Equal (t , ErrorInvalidCurrentPassword .Code , errResp .Code )
1396+ }
1397+
1398+ // Guards the console's admin reset flow: it must keep calling UpdateUserCredentials, never the
1399+ // verifying self variant, since an admin can't know the target user's current password.
1400+ func TestHandleUserCredentialUpdateRequest_DoesNotRequireCurrentPassword (t * testing.T ) {
1401+ userID := testUserID789
1402+
1403+ mockSvc := NewUserServiceInterfaceMock (t )
1404+ credentialsJSON := json .RawMessage (`{"password":"admin-reset-password"}` )
1405+ mockSvc .On ("UpdateUserCredentials" , mock .Anything , userID , credentialsJSON ).Return (nil )
1406+
1407+ handler := newUserHandler (mockSvc )
1408+ req := httptest .NewRequest (http .MethodPost , "/users/" + userID + "/update-credentials" ,
1409+ bytes .NewBufferString (`{"credentials":{"password":"admin-reset-password"}}` ))
1410+ req .SetPathValue ("id" , userID )
1411+ rr := httptest .NewRecorder ()
1412+
1413+ handler .HandleUserCredentialUpdateRequest (rr , req )
1414+
1415+ require .Equal (t , http .StatusNoContent , rr .Code )
1416+ mockSvc .AssertNumberOfCalls (t , "UpdateUserCredentials" , 1 )
1417+ mockSvc .AssertNotCalled (t , "UpdateSelfUserCredentials" ,
1418+ mock .Anything , mock .Anything , mock .Anything , mock .Anything )
1419+ }
0 commit comments