Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-xmlsec for openSUSE:Factory checked in at 2022-05-12 23:00:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-xmlsec (Old) and /work/SRC/openSUSE:Factory/.python-xmlsec.new.1538 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-xmlsec" Thu May 12 23:00:23 2022 rev:6 rq:976452 version:1.3.12 Changes: -------- --- /work/SRC/openSUSE:Factory/python-xmlsec/python-xmlsec.changes 2021-06-11 00:19:24.193392967 +0200 +++ /work/SRC/openSUSE:Factory/.python-xmlsec.new.1538/python-xmlsec.changes 2022-05-12 23:00:48.528832929 +0200 @@ -1,0 +2,14 @@ +Wed May 11 12:39:21 UTC 2022 - Matej Cepl <[email protected]> + +- Update to 1.3.12: + - Added support for registering custom xmlsec IO callbacks + - Added support for building without MD5 transforms + - Added support for PEP 539 for Python 3.7 and newer + - Using lxml-stubs package instead of custom LXML stubs +- Add avoid_lxml_tests_failing.patch (help working + around the lxml issue lp#1880251 and lp#1887848, from + gh#mehcode/python-xmlsec#84). +- Switch off building on Python 3.10 and %ix86 + (gh#mehcode/python-xmlsec#204). + +------------------------------------------------------------------- Old: ---- xmlsec-1.3.11.tar.gz New: ---- avoid_lxml_tests_failing.patch xmlsec-1.3.12.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-xmlsec.spec ++++++ --- /var/tmp/diff_new_pack.0jiwT2/_old 2022-05-12 23:00:49.380834073 +0200 +++ /var/tmp/diff_new_pack.0jiwT2/_new 2022-05-12 23:00:49.388834084 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-xmlsec # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,22 +17,29 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} +# gh#mehcode/python-xmlsec#204 and gh#mehcode/python-xmlsec#210 +%define skip_python310 1 Name: python-xmlsec -Version: 1.3.11 +Version: 1.3.12 Release: 0 Summary: Python bindings for the XML Security Library License: MIT URL: https://github.com/mehcode/python-xmlsec Source: https://files.pythonhosted.org/packages/source/x/xmlsec/xmlsec-%{version}.tar.gz +# PATCH-FIX-UPSTREAM avoid_lxml_tests_failing.patch gh#mehcode/python-xmlsec#84 [email protected] +# work around the lxml issue +Patch0: avoid_lxml_tests_failing.patch BuildRequires: %{python_module devel} BuildRequires: %{python_module hypothesis} BuildRequires: %{python_module lxml >= 3.0} BuildRequires: %{python_module lxml-devel} +BuildRequires: %{python_module pip} BuildRequires: %{python_module pkgconfig} BuildRequires: %{python_module pytest} BuildRequires: %{python_module setuptools_scm} BuildRequires: %{python_module setuptools} BuildRequires: %{python_module toml} +BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: libtool BuildRequires: pkgconfig @@ -50,23 +57,33 @@ Python bindings for the XML Security Library %prep -%setup -q -n xmlsec-%{version} +%autosetup -p1 -n xmlsec-%{version} %build export CFLAGS="%{optflags}" -%python_build +%pyproject_wheel %install -%python_install +%pyproject_install %python_expand %fdupes %{buildroot}%{$python_sitearch} %check -# Tests coredump gh#mehcode/python-xmlsec#183 # %%pytest_arch tests/ +%ifarch %ix86 +export skip_tests="not test_reinitialize_module" +%else +export skip_tests="" +%endif +%{python_expand export PYTHONPATH=%{buildroot}%{$python_sitearch} PYTHONDONTWRITEBYTECODE=1 +rm -rf .hypothesis/ .pytest_cache/ +$python -mpytest --ignore=_build.python39 --ignore=_build.python310 --ignore=_build.python38 -v -k "$skip_tests" tests/ +} %files %{python_files} %doc README.rst %license LICENSE -%{python_sitearch}/* +%{python_sitearch}/xmlsec +%{python_sitearch}/xmlsec-%{version}*-info +%{python_sitearch}/xmlsec*.so %changelog ++++++ avoid_lxml_tests_failing.patch ++++++ --- tests/base.py | 1 + tests/test_doc_examples.py | 2 ++ 2 files changed, 3 insertions(+) --- a/tests/base.py +++ b/tests/base.py @@ -99,6 +99,7 @@ class TestMemoryLeaks(unittest.TestCase) def load_xml(self, name, xpath=None): """returns xml.etree""" + etree.set_default_parser(parser=etree.XMLParser()) root = etree.parse(self.path(name)).getroot() if xpath is None: return root --- a/tests/test_doc_examples.py +++ b/tests/test_doc_examples.py @@ -42,3 +42,5 @@ def test_doc_example(example): """ with cd(example.parent): runpy.run_path(str(example)) + from lxml import etree + etree.set_default_parser(parser=etree.XMLParser()) ++++++ xmlsec-1.3.11.tar.gz -> xmlsec-1.3.12.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/PKG-INFO new/xmlsec-1.3.12/PKG-INFO --- old/xmlsec-1.3.11/PKG-INFO 2021-05-28 23:18:26.535471700 +0200 +++ new/xmlsec-1.3.12/PKG-INFO 2021-09-05 18:54:29.396132200 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: xmlsec -Version: 1.3.11 +Version: 1.3.12 Summary: Python bindings for the XML Security Library Home-page: https://github.com/mehcode/python-xmlsec Author: Bulat Gaifullin @@ -10,6 +10,7 @@ License: MIT Project-URL: Documentation, https://xmlsec.readthedocs.io Project-URL: Source, https://github.com/mehcode/python-xmlsec +Project-URL: Changelog, https://github.com/mehcode/python-xmlsec/releases Keywords: xmlsec Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable @@ -84,7 +85,7 @@ .. code-block:: bash - apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl + apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl Note: There is no required version of LibXML2 for Ubuntu Precise, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/README.rst new/xmlsec-1.3.12/README.rst --- old/xmlsec-1.3.11/README.rst 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/README.rst 2021-09-05 18:54:21.000000000 +0200 @@ -53,7 +53,7 @@ .. code-block:: bash - apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl + apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl Note: There is no required version of LibXML2 for Ubuntu Precise, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/mypy.ini new/xmlsec-1.3.12/mypy.ini --- old/xmlsec-1.3.11/mypy.ini 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/mypy.ini 2021-09-05 18:54:21.000000000 +0200 @@ -1,6 +1,5 @@ [mypy] files = src -mypy_path = typeshed/ ignore_missing_imports = False warn_unused_configs = True disallow_subclassing_any = True diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/setup.cfg new/xmlsec-1.3.12/setup.cfg --- old/xmlsec-1.3.11/setup.cfg 2021-05-28 23:18:26.535471700 +0200 +++ new/xmlsec-1.3.12/setup.cfg 2021-09-05 18:54:29.396132200 +0200 @@ -1,9 +1,9 @@ [metadata] -description-file = README.rst +description_file = README.rst [bdist_rpm] release = 1 -build-requires = pkg-config xmlsec1-devel libxml2-devel xmlsec1-openssl-devel +build_requires = pkg-config xmlsec1-devel libxml2-devel xmlsec1-openssl-devel group = Development/Libraries requires = xmlsec1 xmlsec1-openssl @@ -13,7 +13,7 @@ all_files = 1 [upload_docs] -upload-dir = doc/build/html +upload_dir = doc/build/html [flake8] max-line-length = 130 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/setup.py new/xmlsec-1.3.12/setup.py --- old/xmlsec-1.3.11/setup.py 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/setup.py 2021-09-05 18:54:21.000000000 +0200 @@ -415,6 +415,7 @@ project_urls={ 'Documentation': 'https://xmlsec.readthedocs.io', 'Source': 'https://github.com/mehcode/python-xmlsec', + 'Changelog': 'https://github.com/mehcode/python-xmlsec/releases', }, license='MIT', keywords=['xmlsec'], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/constants.c new/xmlsec-1.3.12/src/constants.c --- old/xmlsec-1.3.11/src/constants.c 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/src/constants.c 2021-09-05 18:54:21.000000000 +0200 @@ -507,7 +507,10 @@ PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformEcdsaSha512, "ECDSA_SHA512"); #endif +#ifndef XMLSEC_NO_MD5 PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacMd5, "HMAC_MD5"); +#endif + #ifndef XMLSEC_NO_RIPEMD160 PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacRipemd160, "HMAC_RIPEMD160"); #endif @@ -517,7 +520,10 @@ PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacSha384, "HMAC_SHA384"); PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformHmacSha512, "HMAC_SHA512"); +#ifndef XMLSEC_NO_MD5 PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaMd5, "RSA_MD5"); +#endif + #ifndef XMLSEC_NO_RIPEMD160 PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaRipemd160, "RSA_RIPEMD160"); #endif @@ -529,7 +535,10 @@ PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaPkcs1, "RSA_PKCS1"); PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRsaOaep, "RSA_OAEP"); +#ifndef XMLSEC_NO_MD5 PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformMd5, "MD5"); +#endif + #ifndef XMLSEC_NO_RIPEMD160 PYXMLSEC_ADD_TRANSFORM_CONSTANT(TransformRipemd160, "RIPEMD160"); #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/exception.c new/xmlsec-1.3.12/src/exception.c --- old/xmlsec-1.3.11/src/exception.c 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/src/exception.c 2021-09-05 18:54:21.000000000 +0200 @@ -23,7 +23,11 @@ PyObject* PyXmlSec_InternalError; PyObject* PyXmlSec_VerificationError; +#if PY_MINOR_VERSION >= 7 +static Py_tss_t PyXmlSec_LastErrorKey; +#else static int PyXmlSec_LastErrorKey = 0; +#endif static int PyXmlSec_PrintErrorMessage = 0; @@ -71,16 +75,26 @@ PyXmlSec_ErrorHolder* v; int r; + #if PY_MINOR_VERSION >= 7 + if (PyThread_tss_is_created(&PyXmlSec_LastErrorKey) == 0) { + #else if (PyXmlSec_LastErrorKey == 0) { + #endif PYXMLSEC_DEBUG("WARNING: There is no error key."); PyXmlSec_ErrorHolderFree(e); return NULL; } // get_key_value and set_key_value are gil free + #if PY_MINOR_VERSION >= 7 + v = (PyXmlSec_ErrorHolder*)PyThread_tss_get(&PyXmlSec_LastErrorKey); + //PyThread_tss_delete(&PyXmlSec_LastErrorKey); + r = PyThread_tss_set(&PyXmlSec_LastErrorKey, (void*)e); + #else v = (PyXmlSec_ErrorHolder*)PyThread_get_key_value(PyXmlSec_LastErrorKey); PyThread_delete_key_value(PyXmlSec_LastErrorKey); r = PyThread_set_key_value(PyXmlSec_LastErrorKey, (void*)e); + #endif PYXMLSEC_DEBUGF("set_key_value returns %d", r); return v; } @@ -166,7 +180,11 @@ } void PyXmlSec_InstallErrorCallback() { + #if PY_MINOR_VERSION >= 7 + if (PyThread_tss_is_created(&PyXmlSec_LastErrorKey) != 0) { + #else if (PyXmlSec_LastErrorKey != 0) { + #endif xmlSecErrorsSetCallback(PyXmlSec_ErrorCallback); } } @@ -190,8 +208,14 @@ if (PyModule_AddObject(package, "InternalError", PyXmlSec_InternalError) < 0) goto ON_FAIL; if (PyModule_AddObject(package, "VerificationError", PyXmlSec_VerificationError) < 0) goto ON_FAIL; + #if PY_MINOR_VERSION >= 7 + if (PyThread_tss_create(&PyXmlSec_LastErrorKey) == 0) { + PyXmlSec_InstallErrorCallback(); + } + #else PyXmlSec_LastErrorKey = PyThread_create_key(); PyXmlSec_InstallErrorCallback(); + #endif return 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/keys.c new/xmlsec-1.3.12/src/keys.c --- old/xmlsec-1.3.11/src/keys.c 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/src/keys.c 2021-09-05 18:54:21.000000000 +0200 @@ -453,7 +453,7 @@ } if (value == NULL) { - if (xmlSecKeySetName(key->handle, value) < 0) { + if (xmlSecKeySetName(key->handle, NULL) < 0) { PyXmlSec_SetLastError("cannot delete name"); return -1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/main.c new/xmlsec-1.3.12/src/main.c --- old/xmlsec-1.3.11/src/main.c 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/src/main.c 2021-09-05 18:54:21.000000000 +0200 @@ -15,6 +15,7 @@ #include <xmlsec/crypto.h> #include <xmlsec/errors.h> #include <xmlsec/base64.h> +#include <xmlsec/io.h> #define _PYXMLSEC_FREE_NONE 0 #define _PYXMLSEC_FREE_XMLSEC 1 @@ -87,6 +88,11 @@ PyXmlSec_Free(_PYXMLSEC_FREE_ALL); return -1; } + // xmlsec will install default callback in xmlSecCryptoInit, + // overwriting any custom callbacks. + // We thus reinstall our callback now. + PyXmlSec_InstallErrorCallback(); + free_mode = _PYXMLSEC_FREE_ALL; return 0; } @@ -128,6 +134,211 @@ Py_RETURN_NONE; } +// NB: This whole thing assumes that the `xmlsec` callbacks are not re-entrant +// (i.e. that xmlsec won't come across a link in the reference it's processing +// and try to open that with these callbacks too). +typedef struct CbList { + PyObject* match_cb; + PyObject* open_cb; + PyObject* read_cb; + PyObject* close_cb; + struct CbList* next; +} CbList; + +static CbList* registered_callbacks = NULL; + +static void RCBListCons(CbList* cb_list_item) { + cb_list_item->next = registered_callbacks; + registered_callbacks = cb_list_item; +} + +static void RCBListClear() { + CbList* cb_list_item = registered_callbacks; + while (cb_list_item) { + Py_DECREF(cb_list_item->match_cb); + Py_DECREF(cb_list_item->open_cb); + Py_DECREF(cb_list_item->read_cb); + Py_DECREF(cb_list_item->close_cb); + CbList* next = cb_list_item->next; + free(cb_list_item); + cb_list_item = next; + } + registered_callbacks = NULL; +} + +// The currently executing set of Python callbacks: +static CbList* cur_cb_list_item; + +static int PyXmlSec_MatchCB(const char* filename) { + cur_cb_list_item = registered_callbacks; + PyGILState_STATE state = PyGILState_Ensure(); + PyObject* args = Py_BuildValue("(y)", filename); + while (cur_cb_list_item) { + PyObject* result = PyObject_CallObject(cur_cb_list_item->match_cb, args); + if (result && PyObject_IsTrue(result)) { + Py_DECREF(result); + Py_DECREF(args); + PyGILState_Release(state); + return 1; + } + Py_XDECREF(result); + cur_cb_list_item = cur_cb_list_item->next; + } + Py_DECREF(args); + PyGILState_Release(state); + return 0; +} + +static void* PyXmlSec_OpenCB(const char* filename) { + PyGILState_STATE state = PyGILState_Ensure(); + + // NB: Assumes the match callback left the current callback list item in the + // right place: + PyObject* args = Py_BuildValue("(y)", filename); + PyObject* result = PyObject_CallObject(cur_cb_list_item->open_cb, args); + Py_DECREF(args); + + PyGILState_Release(state); + return result; +} + +static int PyXmlSec_ReadCB(void* context, char* buffer, int len) { + PyGILState_STATE state = PyGILState_Ensure(); + + // NB: Assumes the match callback left the current callback list item in the + // right place: + PyObject* py_buffer = PyMemoryView_FromMemory(buffer, (Py_ssize_t) len, PyBUF_WRITE); + PyObject* args = Py_BuildValue("(OO)", context, py_buffer); + PyObject* py_bytes_read = PyObject_CallObject(cur_cb_list_item->read_cb, args); + Py_DECREF(args); + Py_DECREF(py_buffer); + int result; + if (py_bytes_read && PyLong_Check(py_bytes_read)) { + result = (int)PyLong_AsLong(py_bytes_read); + } else { + result = EOF; + } + Py_XDECREF(py_bytes_read); + + PyGILState_Release(state); + return result; +} + +static int PyXmlSec_CloseCB(void* context) { + PyGILState_STATE state = PyGILState_Ensure(); + + PyObject* args = Py_BuildValue("(O)", context); + PyObject* result = PyObject_CallObject(cur_cb_list_item->close_cb, args); + Py_DECREF(args); + Py_DECREF(context); + Py_DECREF(result); + + PyGILState_Release(state); + return 0; +} + +static char PyXmlSec_PyIOCleanupCallbacks__doc__[] = \ + "Unregister globally all sets of IO callbacks from xmlsec."; +static PyObject* PyXmlSec_PyIOCleanupCallbacks(PyObject *self) { + xmlSecIOCleanupCallbacks(); + // We always have callbacks registered to delegate to any Python callbacks + // we have registered within these bindings: + if (xmlSecIORegisterCallbacks( + PyXmlSec_MatchCB, PyXmlSec_OpenCB, PyXmlSec_ReadCB, + PyXmlSec_CloseCB) < 0) { + return NULL; + } + RCBListClear(); + Py_RETURN_NONE; +} + +static char PyXmlSec_PyIORegisterDefaultCallbacks__doc__[] = \ + "Register globally xmlsec's own default set of IO callbacks."; +static PyObject* PyXmlSec_PyIORegisterDefaultCallbacks(PyObject *self) { + // NB: The default callbacks (specifically libxml2's `xmlFileMatch`) always + // match, and callbacks are called in the reverse order to that which they + // were added. So, there's no point in holding onto any previously registered + // callbacks, because they will never be run: + xmlSecIOCleanupCallbacks(); + RCBListClear(); + if (xmlSecIORegisterDefaultCallbacks() < 0) { + return NULL; + } + // We need to make sure we can continue trying to match any newly added + // Python callbacks: + if (xmlSecIORegisterCallbacks( + PyXmlSec_MatchCB, PyXmlSec_OpenCB, PyXmlSec_ReadCB, + PyXmlSec_CloseCB) < 0) { + return NULL; + }; + Py_RETURN_NONE; +} + +static char PyXmlSec_PyIORegisterCallbacks__doc__[] = \ + "Register globally a custom set of IO callbacks with xmlsec.\n\n" + ":param callable input_match_callback: A callable that takes a filename `bytestring` and " + "returns a boolean as to whether the other callbacks in this set can handle that name.\n" + ":param callable input_open_callback: A callable that takes a filename and returns some " + "context object (e.g. a file object) that the remaining callables in this set will be passed " + "during handling.\n" + // FIXME: How do we handle failures in ^^ (e.g. can't find the file)? + ":param callable input_read_callback: A callable that that takes the context object from the " + "open callback and a buffer, and should fill the buffer with data (e.g. BytesIO.readinto()). " + "xmlsec will call this function several times until there is no more data returned.\n" + ":param callable input_close_callback: A callable that takes the context object from the " + "open callback and can do any resource cleanup necessary.\n" + ; +static PyObject* PyXmlSec_PyIORegisterCallbacks(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = { + "input_match_callback", + "input_open_callback", + "input_read_callback", + "input_close_callback", + NULL + }; + CbList* cb_list_item = malloc(sizeof(CbList)); + if (cb_list_item == NULL) { + return NULL; + } + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "OOOO:register_callbacks", kwlist, + &cb_list_item->match_cb, &cb_list_item->open_cb, &cb_list_item->read_cb, + &cb_list_item->close_cb)) { + free(cb_list_item); + return NULL; + } + if (!PyCallable_Check(cb_list_item->match_cb)) { + PyErr_SetString(PyExc_TypeError, "input_match_callback must be a callable"); + free(cb_list_item); + return NULL; + } + if (!PyCallable_Check(cb_list_item->open_cb)) { + PyErr_SetString(PyExc_TypeError, "input_open_callback must be a callable"); + free(cb_list_item); + return NULL; + } + if (!PyCallable_Check(cb_list_item->read_cb)) { + PyErr_SetString(PyExc_TypeError, "input_read_callback must be a callable"); + free(cb_list_item); + return NULL; + } + if (!PyCallable_Check(cb_list_item->close_cb)) { + PyErr_SetString(PyExc_TypeError, "input_close_callback must be a callable"); + free(cb_list_item); + return NULL; + } + Py_INCREF(cb_list_item->match_cb); + Py_INCREF(cb_list_item->open_cb); + Py_INCREF(cb_list_item->read_cb); + Py_INCREF(cb_list_item->close_cb); + cb_list_item->next = NULL; + RCBListCons(cb_list_item); + // NB: We don't need to register the callbacks with `xmlsec` here, because + // we've already registered our helper functions that will trawl through our + // list of callbacks. + Py_RETURN_NONE; +} + static char PyXmlSec_PyBase64DefaultLineSize__doc__[] = \ "base64_default_line_size(size = None)\n" "Configures the default maximum columns size for base64 encoding.\n\n" @@ -177,6 +388,24 @@ PyXmlSec_PyEnableDebugOutput__doc__ }, { + "cleanup_callbacks", + (PyCFunction)PyXmlSec_PyIOCleanupCallbacks, + METH_NOARGS, + PyXmlSec_PyIOCleanupCallbacks__doc__ + }, + { + "register_default_callbacks", + (PyCFunction)PyXmlSec_PyIORegisterDefaultCallbacks, + METH_NOARGS, + PyXmlSec_PyIORegisterDefaultCallbacks__doc__ + }, + { + "register_callbacks", + (PyCFunction)PyXmlSec_PyIORegisterCallbacks, + METH_VARARGS|METH_KEYWORDS, + PyXmlSec_PyIORegisterCallbacks__doc__ + }, + { "base64_default_line_size", (PyCFunction)PyXmlSec_PyBase64DefaultLineSize, METH_VARARGS|METH_KEYWORDS, @@ -239,11 +468,6 @@ if (PyXmlSec_Init() < 0) goto ON_FAIL; - // xmlsec will install default callback in PyXmlSec_Init, - // overwriting any custom callbacks. - // We thus install our callback again now. - PyXmlSec_InstallErrorCallback(); - if (PyModule_AddStringConstant(module, "__version__", STRINGIFY(MODULE_VERSION)) < 0) goto ON_FAIL; if (PyXmlSec_InitLxmlModule() < 0) goto ON_FAIL; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec/__init__.pyi new/xmlsec-1.3.12/src/xmlsec/__init__.pyi --- old/xmlsec-1.3.11/src/xmlsec/__init__.pyi 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/src/xmlsec/__init__.pyi 2021-09-05 18:54:21.000000000 +0200 @@ -1,5 +1,7 @@ import sys -from typing import AnyStr, IO, Iterable, Optional, Type, TypeVar, Union, overload +from typing import ( + Any, AnyStr, Callable, IO, Iterable, Optional, Type, TypeVar, Union, + overload) from lxml.etree import _Element @@ -24,6 +26,14 @@ def enable_debug_trace(enabled: bool = ...) -> None: ... def init() -> None: ... def shutdown() -> None: ... +def cleanup_callbacks() -> None: ... +def register_default_callbacks() -> None: ... +def register_callbacks( + input_match_callback: Callable[[bytes], bool], + input_open_callback: Callable[[bytes], Any], + input_read_callback: Callable[[Any, memoryview], int], + input_close_callback: Callable[[Any], None], +) -> None: ... @overload def base64_default_line_size() -> int: ... @overload diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec/constants.pyi new/xmlsec-1.3.12/src/xmlsec/constants.pyi --- old/xmlsec-1.3.11/src/xmlsec/constants.pyi 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/src/xmlsec/constants.pyi 2021-09-05 18:54:21.000000000 +0200 @@ -1,5 +1,5 @@ import sys -from typing import NamedTuple +from typing import NamedTuple, Optional if sys.version_info >= (3, 8): from typing import Final @@ -7,12 +7,12 @@ from typing_extensions import Final class __KeyData(NamedTuple): # __KeyData type - href: str name: str + href: Optional[str] class __Transform(NamedTuple): # __Transform type - href: str name: str + href: Optional[str] usage: int DSigNs: Final = 'http://www.w3.org/2000/09/xmldsig#' @@ -109,7 +109,9 @@ 'c14n11-with-comments', 'http://www.w3.org/2006/12/xml-c14n11#WithComments', 3 ) TransformInclC14NWithComments: Final = __Transform( - 'c14n-with-comments', 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments', 3 + 'c14n-with-comments', + 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments', + 3, ) TransformKWAes128: Final = __Transform('kw-aes128', 'http://www.w3.org/2001/04/xmlenc#kw-aes128', 16) TransformKWAes192: Final = __Transform('kw-aes192', 'http://www.w3.org/2001/04/xmlenc#kw-aes192', 16) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec.egg-info/PKG-INFO new/xmlsec-1.3.12/src/xmlsec.egg-info/PKG-INFO --- old/xmlsec-1.3.11/src/xmlsec.egg-info/PKG-INFO 2021-05-28 23:18:26.000000000 +0200 +++ new/xmlsec-1.3.12/src/xmlsec.egg-info/PKG-INFO 2021-09-05 18:54:29.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: xmlsec -Version: 1.3.11 +Version: 1.3.12 Summary: Python bindings for the XML Security Library Home-page: https://github.com/mehcode/python-xmlsec Author: Bulat Gaifullin @@ -10,6 +10,7 @@ License: MIT Project-URL: Documentation, https://xmlsec.readthedocs.io Project-URL: Source, https://github.com/mehcode/python-xmlsec +Project-URL: Changelog, https://github.com/mehcode/python-xmlsec/releases Keywords: xmlsec Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable @@ -84,7 +85,7 @@ .. code-block:: bash - apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl + apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl Note: There is no required version of LibXML2 for Ubuntu Precise, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/src/xmlsec.egg-info/SOURCES.txt new/xmlsec-1.3.12/src/xmlsec.egg-info/SOURCES.txt --- old/xmlsec-1.3.11/src/xmlsec.egg-info/SOURCES.txt 2021-05-28 23:18:26.000000000 +0200 +++ new/xmlsec-1.3.12/src/xmlsec.egg-info/SOURCES.txt 2021-09-05 18:54:29.000000000 +0200 @@ -85,6 +85,4 @@ tests/data/sign5-out.xml tests/data/sign6-in.bin tests/data/sign6-out.bin -tests/data/sign_template.xml -typeshed/lxml/__init__.pyi -typeshed/lxml/etree.pyi \ No newline at end of file +tests/data/sign_template.xml \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/tests/test_main.py new/xmlsec-1.3.12/tests/test_main.py --- old/xmlsec-1.3.11/tests/test_main.py 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/tests/test_main.py 2021-09-05 18:54:21.000000000 +0200 @@ -1,5 +1,10 @@ +import sys +from io import BytesIO +from unittest import skipIf + import xmlsec from tests import base +from xmlsec import constants as consts class TestBase64LineSize(base.TestMemoryLeaks): @@ -30,3 +35,126 @@ with self.assertRaises(ValueError): xmlsec.base64_default_line_size(-1) self.assertEqual(xmlsec.base64_default_line_size(), size) + + +class TestCallbacks(base.TestMemoryLeaks): + def setUp(self): + super().setUp() + xmlsec.cleanup_callbacks() + + def _sign_doc(self): + root = self.load_xml("doc.xml") + sign = xmlsec.template.create(root, c14n_method=consts.TransformExclC14N, sign_method=consts.TransformRsaSha1) + xmlsec.template.add_reference(sign, consts.TransformSha1, uri="cid:123456") + + ctx = xmlsec.SignatureContext() + ctx.key = xmlsec.Key.from_file(self.path("rsakey.pem"), format=consts.KeyDataFormatPem) + ctx.sign(sign) + return sign + + def _expect_sign_failure(self): + with self.assertRaisesRegex(xmlsec.Error, 'failed to sign'): + self._sign_doc() + + def _mismatch_callbacks(self, match_cb=lambda filename: False): + return [ + match_cb, + lambda filename: None, + lambda none, buf: 0, + lambda none: None, + ] + + def _register_mismatch_callbacks(self, match_cb=lambda filename: False): + xmlsec.register_callbacks(*self._mismatch_callbacks(match_cb)) + + def _register_match_callbacks(self): + xmlsec.register_callbacks( + lambda filename: filename == b'cid:123456', + lambda filename: BytesIO(b'<html><head/><body/></html>'), + lambda bio, buf: bio.readinto(buf), + lambda bio: bio.close(), + ) + + def _find(self, elem, *tags): + try: + return elem.xpath( + './' + '/'.join('xmldsig:{}'.format(tag) for tag in tags), + namespaces={ + 'xmldsig': 'http://www.w3.org/2000/09/xmldsig#', + }, + )[0] + except IndexError as e: + raise KeyError(tags) from e + + def _verify_external_data_signature(self): + signature = self._sign_doc() + digest = self._find(signature, 'SignedInfo', 'Reference', 'DigestValue').text + self.assertEqual(digest, 'VihZwVMGJ48NsNl7ertVHiURXk8=') + + def test_sign_external_data_no_callbacks_fails(self): + self._expect_sign_failure() + + def test_sign_external_data_default_callbacks_fails(self): + xmlsec.register_default_callbacks() + self._expect_sign_failure() + + def test_sign_external_data_no_matching_callbacks_fails(self): + self._register_mismatch_callbacks() + self._expect_sign_failure() + + def test_sign_data_from_callbacks(self): + self._register_match_callbacks() + self._verify_external_data_signature() + + def test_sign_data_not_first_callback(self): + bad_match_calls = 0 + + def match_cb(filename): + nonlocal bad_match_calls + bad_match_calls += 1 + False + + for _ in range(2): + self._register_mismatch_callbacks(match_cb) + + self._register_match_callbacks() + + for _ in range(2): + self._register_mismatch_callbacks() + + self._verify_external_data_signature() + self.assertEqual(bad_match_calls, 0) + + @skipIf(sys.platform == "win32", "unclear behaviour on windows") + def test_failed_sign_because_default_callbacks(self): + mismatch_calls = 0 + + def mismatch_cb(filename): + nonlocal mismatch_calls + mismatch_calls += 1 + False + + # NB: These first two sets of callbacks should never get called, + # because the default callbacks always match beforehand: + self._register_match_callbacks() + self._register_mismatch_callbacks(mismatch_cb) + xmlsec.register_default_callbacks() + self._register_mismatch_callbacks(mismatch_cb) + self._register_mismatch_callbacks(mismatch_cb) + self._expect_sign_failure() + self.assertEqual(mismatch_calls, 2) + + def test_register_non_callables(self): + for idx in range(4): + cbs = self._mismatch_callbacks() + cbs[idx] = None + self.assertRaises(TypeError, xmlsec.register_callbacks, *cbs) + + def test_sign_external_data_fails_on_read_callback_wrong_returns(self): + xmlsec.register_callbacks( + lambda filename: filename == b'cid:123456', + lambda filename: BytesIO(b'<html><head/><body/></html>'), + lambda bio, buf: None, + lambda bio: bio.close(), + ) + self._expect_sign_failure() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/tests/test_xmlsec.py new/xmlsec-1.3.12/tests/test_xmlsec.py --- old/xmlsec-1.3.11/tests/test_xmlsec.py 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/tests/test_xmlsec.py 2021-09-05 18:54:21.000000000 +0200 @@ -10,5 +10,5 @@ tests don't fail, we know that the ``init()``/``shutdown()`` function pair doesn't break anything. """ - # xmlsec.shutdown() - # xmlsec.init() + xmlsec.shutdown() + xmlsec.init() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xmlsec-1.3.11/typeshed/lxml/etree.pyi new/xmlsec-1.3.12/typeshed/lxml/etree.pyi --- old/xmlsec-1.3.11/typeshed/lxml/etree.pyi 2021-05-28 23:18:21.000000000 +0200 +++ new/xmlsec-1.3.12/typeshed/lxml/etree.pyi 1970-01-01 01:00:00.000000000 +0100 @@ -1,6 +0,0 @@ -from typing import Any - -def __getattr__(name: str) -> Any: ... # incomplete - -class _Element: - def __getattr__(self, name: str) -> Any: ... # incomplete
