diff options
author | John Paraskevopoulos <jpr@dialectics.space> | 2019-09-30 13:22:44 +0300 |
---|---|---|
committer | Ivan Kanakarakis <ivan.kanak@gmail.com> | 2019-11-26 14:02:27 +0200 |
commit | cc4526867734e40f3e413f309c450bb9033a871b (patch) | |
tree | a6caa568dd8138a3b818bedcf89b97fcc20955f1 | |
parent | 2109a65b1a233d42da84cc2aad982bf8a4b49816 (diff) | |
download | pysaml2-cc4526867734e40f3e413f309c450bb9033a871b.tar.gz |
Extract `PYSAML2_DELETE_TMPFILES` option to config.py
- Moves parsing PYSAML2_DELETE_TMPFILES option to config.py and uses the
value as a Config class property (`delete_tmpfiles`). This attribute is
part of the configuration so its place is in the config.py and the
corresponding class. This may add the config object dependency to
classes/functions that are calling the `make_temp` function, but at the
same time keeps a more layered approach since this config option is now
processed and set up in the correct layer; that is the Config class and
the config module. Scripts that (in)directly use classes that have
methods that use the `make_temp` functions were not changed since
those methods are not called when these scripts run and they are out of
the scripts' scope (that is, the script functionality does not create
any temp file). Those scripts are `verify_metadata`, `merge_metadata`
and `mdexport`
-rw-r--r-- | docs/howto/config.rst | 24 | ||||
-rw-r--r-- | src/saml2/config.py | 8 | ||||
-rw-r--r-- | src/saml2/entity.py | 6 | ||||
-rw-r--r-- | src/saml2/sigver.py | 79 | ||||
-rw-r--r-- | tests/_test_80_p11_backend.py | 1 | ||||
-rw-r--r-- | tests/test_40_sigver.py | 1 |
6 files changed, 71 insertions, 48 deletions
diff --git a/docs/howto/config.rst b/docs/howto/config.rst index ddb41194..0e3be8a8 100644 --- a/docs/howto/config.rst +++ b/docs/howto/config.rst @@ -1,15 +1,5 @@ .. _howto_config: -Environment variables -===================== - -PYSAML2_DELETE_TMPFILES -^^^^^^^^^^^^^^^^^^^^^^^ - -If set to "False" will keep temporary xml files in the system temporary storage. -Default: "true"; delete temporary files. - - Configuration of pySAML2 entities ================================= @@ -44,6 +34,7 @@ The basic structure of the configuration file is therefore like this:: "key_file" : "my.key", "cert_file" : "ca.pem", "xmlsec_binary" : "/usr/local/bin/xmlsec1", + "delete_tmpfiles": True, "metadata": { "local": ["edugain.xml"], }, @@ -328,6 +319,17 @@ Example:: "xmlsec_binary": "/usr/local/bin/xmlsec1", +delete_tmpfiles +^^^^^^^^^^^^^^^ + +In many cases temporary files will have to be created during the +encryption/decryption/signing/validation process. +This option defines whether these temporary files will be automatically deleted when +they are no longer needed. Setting this to False, will keep these files until they are +manually deleted or automatically deleted by the OS (i.e Linux rules for /tmp). +Absence of this option, defaults to True. + + valid_for ^^^^^^^^^ @@ -842,6 +844,7 @@ We start with a simple but fairly complete Service provider configuration:: "key_file" : "./mykey.pem", "cert_file" : "./mycert.pem", "xmlsec_binary" : "/usr/local/bin/xmlsec1", + "delete_tmpfiles": True, "attribute_map_dir": "./attributemaps", "metadata": { "local": ["idp.xml"] @@ -890,6 +893,7 @@ A slightly more complex configuration:: "key_file" : "./mykey.pem", "cert_file" : "./mycert.pem", "xmlsec_binary" : "/usr/local/bin/xmlsec1", + "delete_tmpfiles": True, "metadata" : { "local": ["example.xml"], "remote": [{ diff --git a/src/saml2/config.py b/src/saml2/config.py index 9b7debcb..274e3960 100644 --- a/src/saml2/config.py +++ b/src/saml2/config.py @@ -69,6 +69,7 @@ COMMON_ARGS = [ "allow_unknown_attributes", "crypto_backend", "id_attr_name", + "delete_tmpfiles", ] SP_ARGS = [ @@ -243,6 +244,7 @@ class Config(object): self.attribute = [] self.attribute_profile = [] self.requested_attribute_name_format = NAME_FORMAT_URI + self.delete_tmpfiles = True def setattr(self, context, attr, val): if context == "": @@ -358,6 +360,12 @@ class Config(object): except TypeError: # Something that can't be a string setattr(self, arg, cnf[arg]) + if not self.delete_tmpfiles: + logger.warning( + "delete_tmpfiles is set to False; " + "temporary files will not be deleted." + ) + if "service" in cnf: for typ in ["aa", "idp", "sp", "pdp", "aq"]: try: diff --git a/src/saml2/entity.py b/src/saml2/entity.py index f76ae67d..2ab1995b 100644 --- a/src/saml2/entity.py +++ b/src/saml2/entity.py @@ -144,7 +144,7 @@ class Entity(HTTPBase): if _val.startswith("http"): r = requests.request("GET", _val) if r.status_code == 200: - tmp = make_temp(r.text, ".pem", False) + tmp = make_temp(r.text, ".pem", False, self.config.delete_tmpfiles) setattr(self.config, item, tmp.name) else: raise Exception( @@ -560,7 +560,9 @@ class Entity(HTTPBase): _cert = "%s%s" % (begin_cert, _cert) if end_cert not in _cert: _cert = "%s%s" % (_cert, end_cert) - tmp = make_temp(_cert.encode('ascii'), decode=False) + tmp = make_temp(_cert.encode('ascii'), + decode=False, + delete_tmpfiles=self.config.delete_tmpfiles) response = self.sec.encrypt_assertion(response, tmp.name, pre_encryption_part(), node_xpath=node_xpath) diff --git a/src/saml2/sigver.py b/src/saml2/sigver.py index aaaa412c..cbeca41f 100644 --- a/src/saml2/sigver.py +++ b/src/saml2/sigver.py @@ -8,11 +8,9 @@ import hashlib import itertools import logging import os -import ssl import six from time import mktime -from binascii import hexlify from six.moves.urllib import parse @@ -43,7 +41,6 @@ from saml2.s_utils import sid from saml2.s_utils import Unsupported from saml2.time_util import instant -from saml2.time_util import utc_now from saml2.time_util import str_to_time from saml2.xmldsig import SIG_RSA_SHA1 @@ -195,7 +192,7 @@ def get_xmlsec_binary(paths=None): raise SigverError('Cannot find {binary}'.format(binary=bin_name)) -def _get_xmlsec_cryptobackend(path=None, search_paths=None): +def _get_xmlsec_cryptobackend(path=None, search_paths=None, delete_tmpfiles=True): """ Initialize a CryptoBackendXmlSec1 crypto backend. @@ -203,7 +200,7 @@ def _get_xmlsec_cryptobackend(path=None, search_paths=None): """ if path is None: path = get_xmlsec_binary(paths=search_paths) - return CryptoBackendXmlSec1(path) + return CryptoBackendXmlSec1(path, delete_tmpfiles=delete_tmpfiles) NODE_NAME = 'urn:oasis:names:tc:SAML:2.0:assertion:Assertion' @@ -211,20 +208,6 @@ ENC_NODE_NAME = 'urn:oasis:names:tc:SAML:2.0:assertion:EncryptedAssertion' ENC_KEY_CLASS = 'EncryptedKey' -def get_environ_delete_tmpfiles(): - default = "true" - value = os.environ.get("PYSAML2_DELETE_TMPFILES", default) - result = value.lower() == default - - if not result: - logger.warning( - "PYSAML2_DELETE_TMPFILES set to False, " - "temporary xml files will not be deleted." - ) - - return result - - def _make_vals(val, klass, seccont, klass_inst=None, prop=None, part=False, base64encode=False, elements_to_sign=None): """ @@ -336,7 +319,7 @@ def signed_instance_factory(instance, seccont, elements_to_sign=None): return instance -def make_temp(content, suffix="", decode=True): +def make_temp(content, suffix="", decode=True, delete_tmpfiles=True): """ Create a temporary file with the given content. @@ -348,6 +331,8 @@ def make_temp(content, suffix="", decode=True): suffix in certain circumstances. :param decode: The input content might be base64 coded. If so it must, in some cases, be decoded before being placed in the file. + :param delete_tmpfiles: Whether to keep the tmp files or delete them when they are + no longer in use :return: 2-tuple with file pointer ( so the calling function can close the file) and filename (which is for instance needed by the xmlsec function). @@ -356,7 +341,6 @@ def make_temp(content, suffix="", decode=True): content.encode("utf-8") if not isinstance(content, six.binary_type) else content ) content_raw = base64.b64decode(content_encoded) if decode else content_encoded - delete_tmpfiles = get_environ_delete_tmpfiles() ntf = NamedTemporaryFile(suffix=suffix, delete=delete_tmpfiles) ntf.write(content_raw) ntf.seek(0) @@ -689,11 +673,11 @@ class CryptoBackendXmlSec1(CryptoBackend): __DEBUG = 0 - def __init__(self, xmlsec_binary, **kwargs): + def __init__(self, xmlsec_binary, delete_tmpfiles=True, **kwargs): CryptoBackend.__init__(self, **kwargs) assert (isinstance(xmlsec_binary, six.string_types)) self.xmlsec = xmlsec_binary - + self.delete_tmpfiles = delete_tmpfiles try: self.non_xml_crypto = RSACrypto(kwargs['rsa_key']) except KeyError: @@ -721,7 +705,7 @@ class CryptoBackendXmlSec1(CryptoBackend): :return: """ logger.debug('Encryption input len: %d', len(text)) - tmp = make_temp(text, decode=False) + tmp = make_temp(text, decode=False, delete_tmpfiles=self.delete_tmpfiles) com_list = [ self.xmlsec, '--encrypt', @@ -758,8 +742,12 @@ class CryptoBackendXmlSec1(CryptoBackend): if isinstance(statement, SamlBase): statement = pre_encrypt_assertion(statement) - tmp = make_temp(_str(statement), decode=False) - tmp2 = make_temp(_str(template), decode=False) + tmp = make_temp(_str(statement), + decode=False, + delete_tmpfiles=self.delete_tmpfiles) + tmp2 = make_temp(_str(template), + decode=False, + delete_tmpfiles=self.delete_tmpfiles) if not node_xpath: node_xpath = ASSERT_XPATH @@ -792,7 +780,7 @@ class CryptoBackendXmlSec1(CryptoBackend): """ logger.debug('Decrypt input len: %d', len(enctext)) - tmp = make_temp(enctext, decode=False) + tmp = make_temp(enctext, decode=False, delete_tmpfiles=self.delete_tmpfiles) com_list = [ self.xmlsec, @@ -824,7 +812,10 @@ class CryptoBackendXmlSec1(CryptoBackend): if isinstance(statement, SamlBase): statement = str(statement) - tmp = make_temp(statement, suffix=".xml", decode=False) + tmp = make_temp(statement, + suffix=".xml", + decode=False, + delete_tmpfiles=self.delete_tmpfiles) com_list = [ self.xmlsec, @@ -865,7 +856,10 @@ class CryptoBackendXmlSec1(CryptoBackend): if not isinstance(signedtext, six.binary_type): signedtext = signedtext.encode('utf-8') - tmp = make_temp(signedtext, suffix=".xml", decode=False) + tmp = make_temp(signedtext, + suffix=".xml", + decode=False, + delete_tmpfiles=self.delete_tmpfiles) com_list = [ self.xmlsec, @@ -1023,7 +1017,8 @@ def security_context(conf): err_msg = err_msg.format(binary=xmlsec_binary) raise SigverError(err_msg) - crypto = _get_xmlsec_cryptobackend(xmlsec_binary) + crypto = _get_xmlsec_cryptobackend(xmlsec_binary, + delete_tmpfiles=conf.delete_tmpfiles) _file_name = conf.getattr('key_file', '') if _file_name: @@ -1063,7 +1058,8 @@ def security_context(conf): enc_key_files=enc_key_files, encryption_keypairs=conf.encryption_keypairs, sec_backend=sec_backend, - id_attr=id_attr) + id_attr=id_attr, + delete_tmpfiles=conf.delete_tmpfiles) def encrypt_cert_from_item(item): @@ -1253,7 +1249,8 @@ class SecurityContext(object): encryption_keypairs=None, enc_cert_type='pem', sec_backend=None, - id_attr=''): + id_attr='', + delete_tmpfiles=True): self.id_attr = id_attr or SecurityContext.DEFAULT_ID_ATTR_NAME @@ -1304,6 +1301,7 @@ class SecurityContext(object): self.template = template self.encrypt_key_type = encrypt_key_type + self.delete_tmpfiles = delete_tmpfiles def correctly_signed(self, xml, must=False): logger.debug('verify correct signature') @@ -1360,7 +1358,10 @@ class SecurityContext(object): key.encode("ascii") if not isinstance(key, six.binary_type) else key for key in keys_filtered ) - key_files = list(make_temp(key, decode=False) for key in keys_encoded) + key_files = list( + make_temp(key, decode=False, delete_tmpfiles=self.delete_tmpfiles) + for key in keys_encoded + ) key_file_names = list(tmp.name for tmp in key_files) try: @@ -1450,7 +1451,10 @@ class SecurityContext(object): for cert in _certs: if isinstance(cert, six.string_types): content = pem_format(cert) - tmp = make_temp(content, suffix=".pem", decode=False) + tmp = make_temp(content, + suffix=".pem", + decode=False, + delete_tmpfiles=self.delete_tmpfiles) certs.append(tmp) else: certs.append(cert) @@ -1460,7 +1464,10 @@ class SecurityContext(object): if not certs and not self.only_use_keys_in_metadata: logger.debug('==== Certs from instance ====') certs = [ - make_temp(content=pem_format(cert), suffix=".pem", decode=False) + make_temp(content=pem_format(cert), + suffix=".pem", + decode=False, + delete_tmpfiles=self.delete_tmpfiles) for cert in cert_from_instance(item) ] else: @@ -1648,7 +1655,7 @@ class SecurityContext(object): if not key_file and key: content = str(key).encode() - tmp = make_temp(content, suffix=".pem") + tmp = make_temp(content, suffix=".pem", delete_tmpfiles=self.delete_tmpfiles) key_file = tmp.name if not key and not key_file: diff --git a/tests/_test_80_p11_backend.py b/tests/_test_80_p11_backend.py index 218d1897..bdbb4fa6 100644 --- a/tests/_test_80_p11_backend.py +++ b/tests/_test_80_p11_backend.py @@ -63,6 +63,7 @@ class FakeConfig(): self.tmp_cert_file = None self.tmp_key_file = None self.validate_certificate = False + self.delete_tmpfiles = True class TestPKCS11(): diff --git a/tests/test_40_sigver.py b/tests/test_40_sigver.py index 89296dc0..07c919b7 100644 --- a/tests/test_40_sigver.py +++ b/tests/test_40_sigver.py @@ -128,6 +128,7 @@ class FakeConfig(): tmp_cert_file = None tmp_key_file = None validate_certificate = False + delete_tmpfiles = True def getattr(self, attr, default): return getattr(self, attr, default) |