@@ -280,24 +280,28 @@ def test_initial_dashboard_password_env_must_be_valid(
280280 default_config = default_config ,
281281 )
282282
283- def test_legacy_password_change_required_rotates_and_keeps_config_flag (
283+ def test_password_change_required_does_not_rotate_existing_password (
284284 self , temp_config_path
285285 ):
286- """Test that the setup flag stays in dashboard config ."""
286+ """A pending password change must not silently rotate the stored password ."""
287287 default_config = {
288288 "dashboard" : {
289289 "username" : "astrbot" ,
290290 "password" : "" ,
291291 "pbkdf2_password" : "" ,
292+ "password_storage_upgraded" : False ,
293+ "password_change_required" : False ,
292294 },
293295 }
296+ stored_pbkdf2 = "pbkdf2_sha256$600000$00$00"
294297 with open (temp_config_path , "w" , encoding = "utf-8" ) as f :
295298 json .dump (
296299 {
297300 "dashboard" : {
298301 "username" : "astrbot" ,
299302 "password" : "" ,
300- "pbkdf2_password" : "pbkdf2_sha256$600000$00$00" ,
303+ "pbkdf2_password" : stored_pbkdf2 ,
304+ "password_storage_upgraded" : True ,
301305 "password_change_required" : True ,
302306 }
303307 },
@@ -308,20 +312,57 @@ def test_legacy_password_change_required_rotates_and_keeps_config_flag(
308312 config_path = temp_config_path ,
309313 default_config = default_config ,
310314 )
311- generated_password = getattr (config , "_generated_dashboard_password" , None )
312315
313- assert isinstance (generated_password , str )
316+ assert getattr (config , "_generated_dashboard_password" , None ) is None
317+ assert config ["dashboard" ]["pbkdf2_password" ] == stored_pbkdf2
314318 assert config ["dashboard" ]["password_change_required" ] is True
315319 assert config ["dashboard" ]["password_storage_upgraded" ] is True
316320 assert (
317321 getattr (config , "_dashboard_password_change_required_from_config" , False )
318322 is True
319323 )
320- assert verify_dashboard_password (
321- config ["dashboard" ]["pbkdf2_password" ], generated_password
324+
325+ def test_password_change_required_is_stable_across_reloads (
326+ self , temp_config_path
327+ ):
328+ """Repeated constructions must not rotate a pending generated password (issue #9662)."""
329+ default_config = {
330+ "dashboard" : {
331+ "username" : "astrbot" ,
332+ "password" : "" ,
333+ "pbkdf2_password" : "" ,
334+ "password_storage_upgraded" : False ,
335+ "password_change_required" : False ,
336+ },
337+ }
338+ with open (temp_config_path , "w" , encoding = "utf-8" ) as f :
339+ json .dump (
340+ {
341+ "dashboard" : {
342+ "username" : "astrbot" ,
343+ "password" : "" ,
344+ "pbkdf2_password" : "pbkdf2_sha256$600000$00$00" ,
345+ "password_storage_upgraded" : True ,
346+ "password_change_required" : True ,
347+ }
348+ },
349+ f ,
350+ )
351+
352+ first = AstrBotConfig (
353+ config_path = temp_config_path ,
354+ default_config = default_config ,
322355 )
323- assert verify_dashboard_password (
324- config ["dashboard" ]["password" ], generated_password
356+ second = AstrBotConfig (
357+ config_path = temp_config_path ,
358+ default_config = default_config ,
359+ )
360+
361+ assert getattr (first , "_generated_dashboard_password" , None ) is None
362+ assert getattr (second , "_generated_dashboard_password" , None ) is None
363+ assert (
364+ first ["dashboard" ]["pbkdf2_password" ]
365+ == second ["dashboard" ]["pbkdf2_password" ]
325366 )
326367
327368 def test_reset_dashboard_password_env_rotates_existing_password (
0 commit comments