Skip to content

Commit

Permalink
Merge branch 'trunk' into 8009-rodrigc-cgi-py3
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigc committed Mar 20, 2017
2 parents 6137407 + 78ded00 commit 7d29b61
Show file tree
Hide file tree
Showing 8 changed files with 28 additions and 187 deletions.
5 changes: 2 additions & 3 deletions src/twisted/application/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,9 @@ def loadApplication(filename, kind, passphrase=None):
@type passphrase: C{str}
"""
if kind == 'python':
application = sob.loadValueFromFile(filename, 'application',
passphrase)
application = sob.loadValueFromFile(filename, 'application')
else:
application = sob.load(filename, kind, passphrase)
application = sob.load(filename, kind)
return application


Expand Down
2 changes: 2 additions & 0 deletions src/twisted/conch/scripts/ckeygen.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

import sys, os, getpass, socket
from functools import wraps
from imp import reload

if getpass.getpass == getpass.unix_getpass:
try:
import termios # hack around broken termios
Expand Down
70 changes: 12 additions & 58 deletions src/twisted/persisted/sob.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,15 @@

import os
import sys
import warnings

try:
import cPickle as pickle
except ImportError:
import pickle
from io import BytesIO
from hashlib import md5
from twisted.python import log, runtime
from twisted.persisted import styles
from zope.interface import implementer, Interface

# Note:
# These encrypt/decrypt functions only work for data formats
# which are immune to having spaces tucked at the end.
# All data formats which persist saves hold that condition.
def _encrypt(passphrase, data):
from Crypto.Cipher import AES as cipher

warnings.warn(
'Saving encrypted persisted data is deprecated since Twisted 15.5.0',
DeprecationWarning, stacklevel=2)

leftover = len(data) % cipher.block_size
if leftover:
data += b' ' * (cipher.block_size - leftover)
return cipher.new(md5(passphrase).digest()[:16]).encrypt(data)



def _decrypt(passphrase, data):
from Crypto.Cipher import AES

warnings.warn(
'Loading encrypted persisted data is deprecated since Twisted 15.5.0',
DeprecationWarning, stacklevel=2)

return AES.new(md5(passphrase).digest()[:16]).decrypt(data)



class IPersistable(Interface):
Expand Down Expand Up @@ -101,14 +71,9 @@ def _getFilename(self, filename, ext, tag):
finalname = "%s.%s" % (self.name, ext)
return finalname, filename

def _saveTemp(self, filename, passphrase, dumpFunc):
def _saveTemp(self, filename, dumpFunc):
with open(filename, 'wb') as f:
if passphrase is None:
dumpFunc(self.original, f)
else:
s = BytesIO()
dumpFunc(self.original, s)
f.write(_encrypt(passphrase, s.getvalue()))
dumpFunc(self.original, f)

def _getStyle(self):
if self.style == "source":
Expand All @@ -128,11 +93,12 @@ def save(self, tag=None, filename=None, passphrase=None):
@type passphrase: string
"""
ext, dumpFunc = self._getStyle()
if passphrase:
if passphrase is not None:
raise TypeError("passphrase must be None")
ext = 'e' + ext
finalname, filename = self._getFilename(filename, ext, tag)
log.msg("Saving "+self.name+" application to "+finalname+"...")
self._saveTemp(filename, passphrase, dumpFunc)
self._saveTemp(filename, dumpFunc)
if runtime.platformType == "win32" and os.path.isfile(finalname):
os.remove(finalname)
os.rename(filename, finalname)
Expand Down Expand Up @@ -162,25 +128,21 @@ def __getattr__(self, key):
return styles.Ephemeral()


def load(filename, style, passphrase=None):
def load(filename, style):
"""Load an object from a file.
Deserialize an object from a file. The file can be encrypted.
@param filename: string
@param style: string (one of 'pickle' or 'source')
@param passphrase: string
"""
mode = 'r'
if style=='source':
from twisted.persisted.aot import unjellyFromSource as _load
else:
_load, mode = pickle.load, 'rb'
if passphrase:
with open(filename, 'rb') as loadedFile:
fp = BytesIO(_decrypt(passphrase, loadedFile.read()))
else:
fp = open(filename, mode)

fp = open(filename, mode)
ee = _EverythingEphemeral(sys.modules['__main__'])
sys.modules['__main__'] = ee
ee.initRun = 1
Expand All @@ -199,26 +161,18 @@ def load(filename, style, passphrase=None):
return value


def loadValueFromFile(filename, variable, passphrase=None):
def loadValueFromFile(filename, variable):
"""Load the value of a variable in a Python file.
Run the contents of the file, after decrypting if C{passphrase} is
given, in a namespace and return the result of the variable
named C{variable}.
Run the contents of the file in a namespace and return the result of the
variable named C{variable}.
@param filename: string
@param variable: string
@param passphrase: string
"""
if passphrase:
mode = 'rb'
else:
mode = 'r'
with open(filename, mode) as fileObj:
with open(filename, 'r') as fileObj:
data = fileObj.read()
d = {'__file__': filename}
if passphrase:
data = _decrypt(passphrase, data)
codeObj = compile(data, filename, "exec")
eval(codeObj, d, d)
value = d[variable]
Expand Down
2 changes: 2 additions & 0 deletions src/twisted/python/rebuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import time
import linecache

from imp import reload

# Sibling Imports
from twisted.python import log, reflect
from twisted.python._oldstyle import _oldStyle
Expand Down
1 change: 1 addition & 0 deletions src/twisted/test/test_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import warnings
import calendar
from io import IOBase
from imp import reload

from twisted.trial import unittest

Expand Down
135 changes: 9 additions & 126 deletions src/twisted/test/test_sob.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,8 @@
from twisted.trial import unittest
from twisted.persisted import sob
from twisted.python import components
from twisted.python.filepath import FilePath
from twisted.python.reflect import namedAny, ObjectNotFound
from twisted.persisted.styles import Ephemeral

try:
namedAny('Crypto.Cipher.AES')
skipCrypto = None
except ObjectNotFound:
skipCrypto = 'PyCrypto is required.'



class Dummy(components.Componentized):
Expand Down Expand Up @@ -59,6 +51,15 @@ def testStylesBeingSet(self):
self.assertEqual(sob.IPersistable(o1).style, style)


def testPassphraseError(self):
"""
Calling save() with a passphrase is an error.
"""
p = sob.Persistant(None, 'object')
self.assertRaises(
TypeError, p.save, 'filename.pickle', passphrase='abc')


def testNames(self):
o = [1,2,3]
p = sob.Persistent(o, 'object')
Expand Down Expand Up @@ -175,121 +176,3 @@ def tearDown(self):
Restore __main__ to its original value
"""
sys.modules['__main__'] = self.realMain



class PersistentEncryptionTests(unittest.TestCase):
"""
Unit tests for Small OBjects persistence using encryption.
"""

if skipCrypto is not None:
skip = skipCrypto


def test_encryptedStyles(self):
"""
Data can be persisted with encryption for all the supported styles.
"""
for o in objects:
phrase = b'once I was the king of spain'
p = sob.Persistent(o, '')
for style in 'source pickle'.split():
p.setStyle(style)
p.save(filename='epersisttest.'+style, passphrase=phrase)
o1 = sob.load('epersisttest.'+style, style, phrase)
self.assertEqual(o, o1)
self.flushWarnings([p._saveTemp, sob.load])


def test_loadValueFromFileEncryptedPython(self):
"""
Encrypted Python data can be loaded from a file.
"""
phrase = b'once I was the king of spain'
with open("epersisttest.python", 'wb') as f:
f.write(sob._encrypt(phrase, b'foo=[1,2,3]'))

o = sob.loadValueFromFile('epersisttest.python', 'foo', phrase)

self.assertEqual(o, [1,2,3])
self.flushWarnings([
sob.loadValueFromFile, self.test_loadValueFromFileEncryptedPython])


def test_saveEncryptedDeprecation(self):
"""
Persisting data with encryption is deprecated.
"""
tempDir = FilePath(self.mktemp())
tempDir.makedirs()
persistedPath = tempDir.child('epersisttest.python')
data = b'once I was the king of spain'
persistance = sob.Persistent(data, 'test-data')

persistance.save(filename=persistedPath.path, passphrase=b'some-pass')

# Check deprecation message.
warnings = self.flushWarnings([persistance._saveTemp])
self.assertEqual(1, len(warnings))
self.assertIs(DeprecationWarning, warnings[0]['category'])
self.assertEqual(
'Saving encrypted persisted data is deprecated since '
'Twisted 15.5.0',
warnings[0]['message'])
# Check that data is still valid, even if we are deprecating this
# functionality.
loadedData = sob.load(
persistedPath.path, persistance.style, b'some-pass')
self.assertEqual(data, loadedData)
self.flushWarnings([sob.load])


def test_loadEncryptedDeprecation(self):
"""
Loading encrypted persisted data is deprecated.
"""
tempDir = FilePath(self.mktemp())
tempDir.makedirs()
persistedPath = tempDir.child('epersisttest.python')
data = b'once I was the king of spain'
persistance = sob.Persistent(data, 'test-data')
persistance.save(filename=persistedPath.path, passphrase=b'some-pass')
# Clean all previous warnings as save will also raise a warning.
self.flushWarnings([persistance._saveTemp])

loadedData = sob.load(
persistedPath.path, persistance.style, b'some-pass')

self.assertEqual(data, loadedData)
warnings = self.flushWarnings([sob.load])
self.assertEqual(1, len(warnings))
self.assertIs(DeprecationWarning, warnings[0]['category'])
self.assertEqual(
'Loading encrypted persisted data is deprecated since '
'Twisted 15.5.0',
warnings[0]['message'])

def test_loadValueFromFileEncryptedDeprecation(self):
"""
Loading encrypted persisted data is deprecated.
"""
tempDir = FilePath(self.mktemp())
tempDir.makedirs()
persistedPath = tempDir.child('epersisttest.python')
persistedPath.setContent(sob._encrypt(b'some-pass', b'foo=[1,2,3]'))
# Clean all previous warnings as _encpryt will also raise a warning.
self.flushWarnings([
self.test_loadValueFromFileEncryptedDeprecation])

loadedData = sob.loadValueFromFile(
persistedPath.path, 'foo', b'some-pass')

self.assertEqual([1, 2, 3], loadedData)
warnings = self.flushWarnings([sob.loadValueFromFile])
self.assertEqual(1, len(warnings))
self.assertIs(DeprecationWarning, warnings[0]['category'])
self.assertEqual(
'Loading encrypted persisted data is deprecated since '
'Twisted 15.5.0',
warnings[0]['message'])
Empty file added src/twisted/topfiles/9070.misc
Empty file.
Empty file added src/twisted/topfiles/9075.misc
Empty file.

0 comments on commit 7d29b61

Please sign in to comment.