From 21c24584792fe7a4ca5b4c753658de966a39d4f9 Mon Sep 17 00:00:00 2001 From: Danny Hermes Date: Wed, 25 Mar 2015 13:31:15 -0700 Subject: [PATCH] Making save_to_well_known_file() save with 0600 permission. Fixes #144. H/T to http://stackoverflow.com/a/5624691/1068170 and http://stackoverflow.com/a/5337329/1068170 --- oauth2client/client.py | 21 ++++++++++++++++++--- tests/test_oauth2client.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/oauth2client/client.py b/oauth2client/client.py index 9c6578f..05f9818 100644 --- a/oauth2client/client.py +++ b/oauth2client/client.py @@ -28,7 +28,9 @@ import logging import os import socket import sys +import tempfile import time +import shutil import six from six.moves import urllib @@ -1208,6 +1210,21 @@ class GoogleCredentials(OAuth2Credentials): 'method should point to a file.') +def _save_private_file(filename, json_contents): + """Saves a file with read-write permissions on for the owner. + + Args: + filename: String. Absolute path to file. + json_contents: JSON serializable object to be saved. + """ + temp_filename = tempfile.mktemp() + file_desc = os.open(temp_filename, os.O_WRONLY | os.O_CREAT, 0o600) + with os.fdopen(file_desc, 'w') as file_handle: + json.dump(json_contents, file_handle, sort_keys=True, + indent=2, separators=(',', ': ')) + shutil.move(temp_filename, filename) + + def save_to_well_known_file(credentials, well_known_file=None): """Save the provided GoogleCredentials to the well known file. @@ -1226,9 +1243,7 @@ def save_to_well_known_file(credentials, well_known_file=None): well_known_file = _get_well_known_file() credentials_data = credentials.serialization_data - - with open(well_known_file, 'w') as f: - json.dump(credentials_data, f, sort_keys=True, indent=2, separators=(',', ': ')) + _save_private_file(well_known_file, credentials_data) def _get_environment_variable_file(): diff --git a/tests/test_oauth2client.py b/tests/test_oauth2client.py index c800b94..70268c1 100644 --- a/tests/test_oauth2client.py +++ b/tests/test_oauth2client.py @@ -1112,5 +1112,35 @@ class MemoryCacheTests(unittest.TestCase): self.assertEqual(None, m.get('foo')) +class Test__save_private_file(unittest.TestCase): + + def _save_helper(self, filename): + contents = [] + contents_str = '[]' + client._save_private_file(filename, contents) + with open(filename, 'r') as f: + stored_contents = f.read() + self.assertEqual(stored_contents, contents_str) + + stat_mode = os.stat(filename).st_mode + # Octal 777, only last 3 positions matter for permissions mask. + stat_mode &= 0o777 + self.assertEqual(stat_mode, 0o600) + + def test_new(self): + import tempfile + filename = tempfile.mktemp() + self.assertFalse(os.path.exists(filename)) + self._save_helper(filename) + + def test_existing(self): + import tempfile + filename = tempfile.mktemp() + with open(filename, 'w') as f: + f.write('a bunch of nonsense longer than []') + self.assertTrue(os.path.exists(filename)) + self._save_helper(filename) + + if __name__ == '__main__': unittest.main()