@@ -435,6 +435,40 @@ def test_login_dry_run(self):
435435 self .subprocess_utils .check_output .assert_not_called ()
436436 self .assertFalse (os .path .exists (self .test_netrc_path ))
437437
438+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
439+ @mock .patch ('awscli.customizations.codeartifact.login.is_macos' , False )
440+ def test_login_adjusts_permissions_on_preexisting_file (self ):
441+ existing_content = (
442+ f'machine { self .hostname } login token password old-token\n '
443+ )
444+ with open (self .test_netrc_path , 'w' ) as f :
445+ f .write (existing_content )
446+ os .chmod (self .test_netrc_path , 0o644 )
447+ self .test_subject .login ()
448+ file_mode = os .stat (self .test_netrc_path ).st_mode
449+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
450+
451+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
452+ @mock .patch ('awscli.customizations.codeartifact.login.is_macos' , False )
453+ @mock .patch ('sys.stderr' )
454+ @mock .patch ('os.chmod' , side_effect = OSError (
455+ errno .EPERM , 'Operation not permitted'
456+ ))
457+ def test_login_writes_error_when_chmod_fails (
458+ self , mock_chmod , mock_stderr
459+ ):
460+ existing_content = (
461+ f'machine { self .hostname } login token password old-token\n '
462+ )
463+ with open (self .test_netrc_path , 'w' ) as f :
464+ f .write (existing_content )
465+ self .test_subject .login ()
466+ mock_stderr .write .assert_any_call (
467+ 'Unable to set file permissions for %s: '
468+ '[Errno 1] Operation not permitted%s'
469+ % (self .test_netrc_path , os .linesep )
470+ )
471+
438472
439473class TestNuGetLogin (unittest .TestCase ):
440474 _NUGET_INDEX_URL_FMT = NuGetLogin ._NUGET_INDEX_URL_FMT
@@ -1246,6 +1280,45 @@ def test_login_existing_pypi_rc_with_codeartifact_not_clobbered(self):
12461280 password = 'JgCXIr5xGG'
12471281 )
12481282
1283+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
1284+ def test_login_sets_secure_permissions_on_new_file (self ):
1285+ self .assertFalse (os .path .exists (self .test_pypi_rc_path ))
1286+ self .test_subject .login ()
1287+ file_mode = os .stat (self .test_pypi_rc_path ).st_mode
1288+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
1289+
1290+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
1291+ def test_login_adjusts_permissions_on_preexisting_file (self ):
1292+ existing_pypi_rc = '''\
1293+ [distutils]
1294+ index-servers=
1295+ pypi
1296+
1297+ [pypi]
1298+ repository: http://www.python.org/pypi/
1299+ username: test
1300+ password: JgCXIr5xGG
1301+ '''
1302+ with open (self .test_pypi_rc_path , 'w' ) as f :
1303+ f .write (existing_pypi_rc )
1304+ os .chmod (self .test_pypi_rc_path , 0o644 )
1305+ self .test_subject .login ()
1306+ file_mode = os .stat (self .test_pypi_rc_path ).st_mode
1307+ self .assertEqual (stat .S_IMODE (file_mode ), 0o600 )
1308+
1309+ @skip_if_windows ("Unix file permissions are not supported on Windows." )
1310+ @mock .patch ('sys.stderr' )
1311+ @mock .patch ('os.chmod' , side_effect = OSError (
1312+ errno .EPERM , 'Operation not permitted'
1313+ ))
1314+ def test_login_writes_error_when_chmod_fails (self , mock_chmod , mock_stderr ):
1315+ self .test_subject .login ()
1316+ mock_stderr .write .assert_any_call (
1317+ 'Unable to set file permissions for %s: '
1318+ '[Errno 1] Operation not permitted%s'
1319+ % (self .test_pypi_rc_path , os .linesep )
1320+ )
1321+
12491322 def test_login_existing_invalid_pypi_rc_error (self ):
12501323 # This is an invalid pypirc as the list of servers are expected under
12511324 # an 'index-servers' option instead of 'servers'.
0 commit comments