Set file permissions on credentials files.
Reviewed in http://codereview.appspot.com/5540053/.
diff --git a/oauth2client/file.py b/oauth2client/file.py
index d20cf6e..d71e888 100644
--- a/oauth2client/file.py
+++ b/oauth2client/file.py
@@ -20,6 +20,8 @@
__author__ = 'jcgregorio@google.com (Joe Gregorio)'
+import os
+import stat
import threading
from anyjson import simplejson
@@ -56,7 +58,7 @@
"""
credentials = None
try:
- f = open(self._filename, 'r')
+ f = open(self._filename, 'rb')
content = f.read()
f.close()
except IOError:
@@ -70,12 +72,28 @@
return credentials
+ def _create_file_if_needed(self):
+ """Create an empty file if necessary.
+
+ This method will not initialize the file. Instead it implements a
+ simple version of "touch" to ensure the file has been created.
+ """
+ if not os.path.exists(self._filename):
+ old_umask = os.umask(0177)
+ try:
+ open(self._filename, 'a+b').close()
+ finally:
+ os.umask(old_umask)
+
+
def locked_put(self, credentials):
"""Write Credentials to file.
Args:
credentials: Credentials, the credentials to store.
"""
- f = open(self._filename, 'w')
+
+ self._create_file_if_needed()
+ f = open(self._filename, 'wb')
f.write(credentials.to_json())
f.close()
diff --git a/oauth2client/multistore_file.py b/oauth2client/multistore_file.py
index b6126f5..d9b89c8 100644
--- a/oauth2client/multistore_file.py
+++ b/oauth2client/multistore_file.py
@@ -32,6 +32,7 @@
__author__ = 'jbeda@google.com (Joe Beda)'
import base64
+import errno
import fcntl
import logging
import os
@@ -167,7 +168,7 @@
if not os.path.exists(self._filename):
old_umask = os.umask(0177)
try:
- open(self._filename, 'a+').close()
+ open(self._filename, 'a+b').close()
finally:
os.umask(old_umask)
@@ -175,12 +176,13 @@
"""Lock the entire multistore."""
self._thread_lock.acquire()
# Check to see if the file is writeable.
- if os.access(self._filename, os.W_OK):
- self._file_handle = open(self._filename, 'r+')
+ try:
+ self._file_handle = open(self._filename, 'r+b')
fcntl.lockf(self._file_handle.fileno(), fcntl.LOCK_EX)
- else:
- # Cannot open in read/write mode. Open only in read mode.
- self._file_handle = open(self._filename, 'r')
+ except IOError, e:
+ if e.errno != errno.EACCES:
+ raise e
+ self._file_handle = open(self._filename, 'rb')
self._read_only = True
if self._warn_on_readonly:
logger.warn('The credentials file (%s) is not writable. Opening in '
diff --git a/tests/test_oauth2client_file.py b/tests/test_oauth2client_file.py
index a34cce7..7ff1eeb 100644
--- a/tests/test_oauth2client_file.py
+++ b/tests/test_oauth2client_file.py
@@ -27,6 +27,7 @@
import httplib2
import os
import pickle
+import stat
import tempfile
import unittest
@@ -134,6 +135,39 @@
self.assertNotEquals(None, credentials)
self.assertEquals('foo', credentials.access_token)
+ mode = os.stat(FILENAME).st_mode
+
+ if os.name == 'posix':
+ self.assertEquals('0600', oct(stat.S_IMODE(os.stat(FILENAME).st_mode)))
+
+ def test_read_only_file_fail_lock(self):
+ access_token = 'foo'
+ client_secret = 'cOuDdkfjxxnv+'
+ refresh_token = '1/0/a.df219fjls0'
+ token_expiry = datetime.datetime.utcnow()
+ token_uri = 'https://www.google.com/accounts/o8/oauth2/token'
+ user_agent = 'refresh_checker/1.0'
+ client_id = 'some_client_id'
+
+ credentials = OAuth2Credentials(
+ access_token, client_id, client_secret,
+ refresh_token, token_expiry, token_uri,
+ user_agent)
+
+ open(FILENAME, 'a+b').close()
+ os.chmod(FILENAME, 0400)
+
+ store = multistore_file.get_credential_storage(
+ FILENAME,
+ credentials.client_id,
+ credentials.user_agent,
+ ['some-scope', 'some-other-scope'])
+
+ store.put(credentials)
+ if os.name == 'posix':
+ self.assertTrue(store._multistore._read_only)
+ os.chmod(FILENAME, 0600)
+
def test_multistore_non_existent_file(self):
store = multistore_file.get_credential_storage(
@@ -171,5 +205,8 @@
self.assertNotEquals(None, credentials)
self.assertEquals('foo', credentials.access_token)
+ if os.name == 'posix':
+ self.assertEquals('0600', oct(stat.S_IMODE(os.stat(FILENAME).st_mode)))
+
if __name__ == '__main__':
unittest.main()