@@ -425,6 +425,39 @@ def test_login_dry_run(self):
425425 self .subprocess_utils .check_output .assert_not_called ()
426426 self .assertFalse (os .path .exists (self .test_netrc_path ))
427427
428+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
429+ @mock .patch ('awscli.customizations.codeartifact.login.is_macos' , False )
430+ def test_login_adjusts_permissions_on_preexisting_file (self ):
431+ existing_content = (
432+ f'machine { self .hostname } login token password old-token\n '
433+ )
434+ with open (self .test_netrc_path , 'w' ) as f :
435+ f .write (existing_content )
436+ os .chmod (self .test_netrc_path , 0o644 )
437+ self .test_subject .login ()
438+ file_mode = os .stat (self .test_netrc_path ).st_mode
439+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
440+
441+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
442+ @mock .patch ('awscli.customizations.codeartifact.login.is_macos' , False )
443+ @mock .patch ('sys.stderr' )
444+ @mock .patch ('os.chmod' , side_effect = OSError (
445+ errno .EPERM , 'Operation not permitted'
446+ ))
447+ def test_login_writes_error_when_chmod_fails (
448+ self , mock_chmod , mock_stderr
449+ ):
450+ existing_content = (
451+ f'machine { self .hostname } login token password old-token\n '
452+ )
453+ with open (self .test_netrc_path , 'w' ) as f :
454+ f .write (existing_content )
455+ self .test_subject .login ()
456+ mock_stderr .write .assert_any_call (
457+ 'Unable to set file permissions for %s: '
458+ '[Errno 1] Operation not permitted%s'
459+ % (self .test_netrc_path , os .linesep )
460+ )
428461
429462class TestNuGetLogin (unittest .TestCase ):
430463 _NUGET_INDEX_URL_FMT = NuGetLogin ._NUGET_INDEX_URL_FMT
@@ -1316,6 +1349,45 @@ def test_login_existing_pypi_rc_with_codeartifact_not_clobbered(self):
13161349 password = 'JgCXIr5xGG' ,
13171350 )
13181351
1352+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
1353+ def test_login_sets_secure_permissions_on_new_file (self ):
1354+ self .assertFalse (os .path .exists (self .test_pypi_rc_path ))
1355+ self .test_subject .login ()
1356+ file_mode = os .stat (self .test_pypi_rc_path ).st_mode
1357+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
1358+
1359+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
1360+ def test_login_adjusts_permissions_on_preexisting_file (self ):
1361+ existing_pypi_rc = '''\
1362+ [distutils]
1363+ index-servers=
1364+ pypi
1365+
1366+ [pypi]
1367+ repository: http://www.python.org/pypi/
1368+ username: test
1369+ password: JgCXIr5xGG
1370+ '''
1371+ with open (self .test_pypi_rc_path , 'w' ) as f :
1372+ f .write (existing_pypi_rc )
1373+ os .chmod (self .test_pypi_rc_path , 0o644 )
1374+ self .test_subject .login ()
1375+ file_mode = os .stat (self .test_pypi_rc_path ).st_mode
1376+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
1377+
1378+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
1379+ @mock .patch ('sys.stderr' )
1380+ @mock .patch ('os.chmod' , side_effect = OSError (
1381+ errno .EPERM , 'Operation not permitted'
1382+ ))
1383+ def test_login_writes_error_when_chmod_fails (self , mock_chmod , mock_stderr ):
1384+ self .test_subject .login ()
1385+ mock_stderr .write .assert_any_call (
1386+ 'Unable to set file permissions for %s: '
1387+ '[Errno 1] Operation not permitted%s'
1388+ % (self .test_pypi_rc_path , os .linesep )
1389+ )
1390+
13191391 def test_login_existing_invalid_pypi_rc_error (self ):
13201392 # This is an invalid pypirc as the list of servers are expected under
13211393 # an 'index-servers' option instead of 'servers'.
0 commit comments