From 4a685fcbaa6f94193a4de6924fb45dde01e701b9 Mon Sep 17 00:00:00 2001 From: Bulat Gaifullin Date: Thu, 25 May 2017 22:49:45 +0300 Subject: [PATCH] Added binding for method xmlSecTmplTransformAddC14NInclNamespaces #59 --- .travis.yml | 3 ++ doc/source/modules/constants.rst | 2 ++ src/constants.c | 2 ++ src/platform.h | 1 + src/template.c | 57 ++++++++++++++++++++++++++++++++ tests/examples/test_templates.py | 35 ++++++++++++++++++++ 6 files changed, 100 insertions(+) create mode 100644 tests/examples/test_templates.py diff --git a/.travis.yml b/.travis.yml index f1b1a5f6..6708afde 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ dist: trusty sudo: false language: python +notifications: + email: false + python: - '2.7' - '3.4' diff --git a/doc/source/modules/constants.rst b/doc/source/modules/constants.rst index c69ef359..460fdd22 100644 --- a/doc/source/modules/constants.rst +++ b/doc/source/modules/constants.rst @@ -58,6 +58,8 @@ Namespaces - *XPointerNs* - http://www.w3.org/2001/04/xmldsig-more/xptr - *Soap11Ns* - http://schemas.xmlsoap.org/soap/envelope/ - *Soap12Ns* - http://www.w3.org/2002/06/soap-envelope +- *NsExcC14N* - http://www.w3.org/2001/10/xml-exc-c14n# +- *NsExcC14NWithComments* - http://www.w3.org/2001/10/xml-exc-c14n#WithComments Nodes ***** diff --git a/src/constants.c b/src/constants.c index 4c890729..ad85a210 100644 --- a/src/constants.c +++ b/src/constants.c @@ -292,6 +292,8 @@ int PyXmlSec_ConstantsModule_Init(PyObject* package) { PYXMLSEC_ADD_NS_CONSTANT(XPointerNs, "XPOINTER"); PYXMLSEC_ADD_NS_CONSTANT(Soap11Ns, "SOAP11"); PYXMLSEC_ADD_NS_CONSTANT(Soap12Ns, "SOAP12"); + PYXMLSEC_ADD_NS_CONSTANT(NsExcC14N, "EXC_C14N"); + PYXMLSEC_ADD_NS_CONSTANT(NsExcC14NWithComments, "EXC_C14N_WITH_COMMENT"); PYXMLSEC_CLOSE_NAMESPACE(nsCls); diff --git a/src/platform.h b/src/platform.h index eae902b9..795062f2 100644 --- a/src/platform.h +++ b/src/platform.h @@ -37,6 +37,7 @@ typedef int Py_ssize_t; #if PY_MAJOR_VERSION >= 3 #define PY3K 1 +#define PyString_Check PyUnicode_Check #define PyString_FromStringAndSize PyUnicode_FromStringAndSize #define PyString_FromString PyUnicode_FromString diff --git a/src/template.c b/src/template.c index f2c4bffe..56aa4bf4 100644 --- a/src/template.c +++ b/src/template.c @@ -651,6 +651,57 @@ static PyObject* PyXmlSec_TemplateEncryptedDataEnsureCipherValue(PyObject* self, return NULL; } +static char PyXmlSec_TemplateTransformAddC14NInclNamespaces__doc__[] = \ + "Adds 'inclusive' namespaces to the ExcC14N transform node *node*.\n\n" + ":param node: the pointer to node.\n" + ":param prefixList: the list of namespace prefixes, where 'default' indicates the default namespace (optional)."; +static PyObject* PyXmlSec_TemplateTransformAddC14NInclNamespaces(PyObject* self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = { "node", "prefixes", NULL}; + + PyXmlSec_LxmlElementPtr node = NULL; + PyObject* prefixes = NULL; + // transform_add_c14n_inclusive_namespaces + PYXMLSEC_DEBUG("template encrypted_data_ensure_cipher_value - start"); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O:transform_add_c14n_inclusive_namespaces", kwlist, + PyXmlSec_LxmlElementConverter, &node, &prefixes)) + { + prefixes = NULL; + goto ON_FAIL; + } + if (PyList_Check(prefixes) || PyTuple_Check(prefixes)) { + PyObject* sep = PyString_FromString(" "); + prefixes = PyObject_CallMethod(sep, "join", "O", prefixes); + Py_DECREF(sep); + } else if (PyString_Check(prefixes)) { + Py_INCREF(prefixes); + } else { + PyErr_SetString(PyExc_TypeError, "expected instance of str or list of str"); + prefixes = NULL; + } + + if (prefixes == NULL) { + goto ON_FAIL; + } + + int res; + const char* c_prefixes = PyString_AsString(prefixes); + Py_BEGIN_ALLOW_THREADS; + res = xmlSecTmplTransformAddC14NInclNamespaces(node->_c_node, XSTR(c_prefixes)); + Py_END_ALLOW_THREADS; + if (res != 0) { + PyXmlSec_SetLastError("cannot add 'inclusive' namespaces to the ExcC14N transform node"); + goto ON_FAIL; + } + + Py_DECREF(prefixes); + PYXMLSEC_DEBUG("transform_add_c14n_inclusive_namespaces - ok"); + Py_RETURN_NONE; + +ON_FAIL: + PYXMLSEC_DEBUG("transform_add_c14n_inclusive_namespaces - fail"); + Py_XDECREF(prefixes); + return NULL; +} static PyMethodDef PyXmlSec_TemplateMethods[] = { { @@ -761,6 +812,12 @@ static PyMethodDef PyXmlSec_TemplateMethods[] = { METH_VARARGS|METH_KEYWORDS, PyXmlSec_TemplateEncryptedDataEnsureCipherValue__doc__ }, + { + "transform_add_c14n_inclusive_namespaces", + (PyCFunction)PyXmlSec_TemplateTransformAddC14NInclNamespaces, + METH_VARARGS|METH_KEYWORDS, + PyXmlSec_TemplateTransformAddC14NInclNamespaces__doc__, + }, {NULL, NULL} /* sentinel */ }; diff --git a/tests/examples/test_templates.py b/tests/examples/test_templates.py new file mode 100644 index 00000000..a6fd1b78 --- /dev/null +++ b/tests/examples/test_templates.py @@ -0,0 +1,35 @@ +import xmlsec +from .base import parse_xml +from lxml import etree + + +def _check_transform_add_custom_c14n_inclusive_namespaces(prefixes, expected): + template = parse_xml('sign2-doc.xml') + assert template is not None + + # Create a signature template for RSA-SHA1 enveloped signature. + signature_node = xmlsec.template.create(template, xmlsec.Transform.EXCL_C14N, xmlsec.Transform.RSA_SHA1) + assert signature_node is not None + + # Add the node to the document. + template.append(signature_node) + + # Add the node to the signature template. + ref = xmlsec.template.add_reference(signature_node, xmlsec.Transform.SHA1) + + # Add the enveloped transform descriptor. + transform = xmlsec.template.add_transform(ref, xmlsec.Transform.ENVELOPED) + assert transform is not None + + xmlsec.template.transform_add_c14n_inclusive_namespaces(transform, prefixes) + ins = xmlsec.tree.find_child(transform, "InclusiveNamespaces", xmlsec.constants.NsExcC14N) + assert ins is not None + assert expected == ins.get("PrefixList") + + +def test_transform_add_custom_c14n_inclusive_namespaces(): + _check_transform_add_custom_c14n_inclusive_namespaces(["ns1", "ns2"], "ns1 ns2") + + +def test_transform_add_default_c14n_inclusive_namespaces(): + _check_transform_add_custom_c14n_inclusive_namespaces("default", "default")